2026-05-06 dcc8bb8f47544cbad6e6440640dcdaa946086013
src/main/java/com/ruoyi/staff/service/impl/StaffOnJobServiceImpl.java
@@ -1,204 +1,387 @@
package com.ruoyi.staff.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.base.BaseException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.dto.WordDateDto;
import com.ruoyi.project.system.domain.SysDept;
import com.ruoyi.project.system.domain.SysPost;
import com.ruoyi.project.system.mapper.SysDeptMapper;
import com.ruoyi.project.system.mapper.SysPostMapper;
import com.ruoyi.staff.dto.StaffOnJobDto;
import com.ruoyi.staff.dto.StaffOnJobImportDto;
import com.ruoyi.staff.mapper.StaffContractMapper;
import com.ruoyi.staff.mapper.StaffJoinLeaveRecordMapper;
import com.ruoyi.staff.mapper.StaffLeaveMapper;
import com.ruoyi.staff.mapper.StaffOnJobMapper;
import com.ruoyi.staff.pojo.StaffContract;
import com.ruoyi.staff.pojo.StaffJoinLeaveRecord;
import com.ruoyi.staff.pojo.StaffLeave;
import com.ruoyi.staff.pojo.StaffOnJob;
import com.ruoyi.staff.service.IStaffOnJobService;
import freemarker.template.Configuration;
import freemarker.template.Template;
import lombok.AllArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;
import static com.ruoyi.common.enums.StaffJoinLeaveRecordDimissionReason.StaffJoinLeaveRecordDimissionReasonOther;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@AllArgsConstructor
@Service
public class StaffOnJobServiceImpl extends ServiceImpl<StaffOnJobMapper, StaffOnJob>  implements IStaffOnJobService {
public class StaffOnJobServiceImpl extends ServiceImpl<StaffOnJobMapper, StaffOnJob> implements IStaffOnJobService {
    @Autowired
    private StaffOnJobMapper staffOnJobMapper;
    @Autowired
    private SysPostMapper sysPostMapper;
    private StaffJoinLeaveRecordMapper staffJoinLeaveRecordMapper;
    @Autowired
    private SysDeptMapper sysDeptMapper;
    @Autowired
    private StaffContractMapper staffContractMapper;
    @Autowired
    private StaffLeaveMapper staffLeaveMapper;
    //在职员工台账分页查询
    @Override
    public IPage<StaffOnJobDto> staffOnJobListPage(Page page, StaffOnJob staffOnJob) {
        return staffOnJobMapper.staffOnJobListPage(page,staffOnJob);
        IPage<StaffOnJobDto> result = staffOnJobMapper.staffOnJobListPage(page, staffOnJob);
        fillNationFallback(result.getRecords());
        return result;
    }
    //新增入职
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int add(StaffOnJobDto staffOnJobPrams) {
        String[] ignoreProperties = {"id"};//排除id属性
        // 判断编号是否存在
        List<StaffOnJob> staffOnJobs = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery().eq(StaffOnJob::getStaffNo, staffOnJobPrams.getStaffNo()));
        if (staffOnJobs.size()>0){
            throw new BaseException("编号为"+staffOnJobPrams.getStaffNo()+"的员工已经存在,无法新增!!!");
        }
        // 创建入职数据
        staffOnJobMapper.insert(staffOnJobPrams);
    public int add(StaffOnJobDto staffOnJobParams) {
        syncStaffBasicFields(staffOnJobParams);
        // 创建合同记录
        List<StaffOnJob> existedStaffNo = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery()
                .eq(StaffOnJob::getStaffNo, staffOnJobParams.getStaffNo()));
        if (!CollectionUtils.isEmpty(existedStaffNo)) {
            throw new BaseException("Duplicate staffNo: " + staffOnJobParams.getStaffNo());
        }
        staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
        staffOnJobParams.setStaffState(1);
        staffOnJobMapper.insert(staffOnJobParams);
        StaffContract staffContract = new StaffContract();
        staffContract.setStaffOnJobId(staffOnJobPrams.getId());
        staffContract.setContractTerm(staffOnJobPrams.getContractTerm());
        staffContract.setContractStartTime(staffOnJobPrams.getContractStartTime());
        staffContract.setContractEndTime(staffOnJobPrams.getContractEndTime());
        staffContract.setStaffOnJobId(staffOnJobParams.getId());
        staffContract.setContractTerm(staffOnJobParams.getContractTerm());
        staffContract.setContractStartTime(staffOnJobParams.getContractStartTime());
        staffContract.setContractEndTime(staffOnJobParams.getContractEndTime());
        return staffContractMapper.insert(staffContract);
    }
    //更新入职信息
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int update(Long id, StaffOnJobDto staffOnJobParams) {
        // 判断对象是否存在
        syncStaffBasicFields(staffOnJobParams);
        staffOnJobParams.setId(id);
        StaffOnJob job = staffOnJobMapper.selectById(id);
        if (job == null){
            throw new BaseException("编号为"+staffOnJobParams.getStaffNo()+"的员工不存在,无法更新!!!");
        if (job == null) {
            throw new BaseException("Staff not found, id=" + id);
        }
        // 更新员工数据
        String[] ignoreProperties = {"id"};//排除id属性
        BeanUtils.copyProperties(staffOnJobParams,job,ignoreProperties);
        staffOnJobMapper.updateById(job);
        if (StringUtils.isNotBlank(staffOnJobParams.getStaffNo())) {
            List<StaffOnJob> existedStaffNo = staffOnJobMapper.selectList(Wrappers.<StaffOnJob>lambdaQuery()
                    .eq(StaffOnJob::getStaffNo, staffOnJobParams.getStaffNo())
                    .ne(StaffOnJob::getId, id));
            if (!CollectionUtils.isEmpty(existedStaffNo)) {
                throw new BaseException("Duplicate staffNo: " + staffOnJobParams.getStaffNo());
            }
        }
        // 获取最新合同数据,并且更新
        String[] ignoreProperties = {"id"};
        StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
                .eq(StaffContract::getStaffOnJobId, id)
                .orderByDesc(StaffContract::getId));
        if (contract != null){
            BeanUtils.copyProperties(staffOnJobParams,contract,ignoreProperties);
            return staffContractMapper.updateById(contract);
                .orderByDesc(StaffContract::getId)
                .last("limit 1"));
        if (contract != null) {
            BeanUtils.copyProperties(staffOnJobParams, contract, ignoreProperties);
            staffContractMapper.updateById(contract);
        }
        return 0;
        staffOnJobParams.setContractExpireTime(staffOnJobParams.getContractEndTime());
        return staffOnJobMapper.updateById(staffOnJobParams);
    }
    //删除入职
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int delStaffOnJobs(List<Integer> ids) {
        // 删除入职数据
        staffOnJobMapper.deleteBatchIds(ids);
        // 删除离职数据
        staffLeaveMapper.delete(Wrappers.<StaffLeave>lambdaQuery().in(StaffLeave::getStaffOnJobId, ids));
        // 删除合同数据
        return staffContractMapper.delete(Wrappers.<StaffContract>lambdaQuery().in(StaffContract::getStaffOnJobId, ids));
    }
    //在职员工详情
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int renewContract(Long id, StaffContract staffContract) {
        StaffOnJob job = staffOnJobMapper.selectById(id);
        if (job == null) {
            throw new BaseException("Staff not found, id=" + id);
        }
        StaffContract newStaffContract = new StaffContract();
        newStaffContract.setStaffOnJobId(id);
        newStaffContract.setContractTerm(staffContract.getContractTerm());
        newStaffContract.setContractStartTime(staffContract.getContractStartTime());
        newStaffContract.setContractEndTime(staffContract.getContractEndTime());
        staffContractMapper.insert(newStaffContract);
        job.setContractExpireTime(staffContract.getContractEndTime());
        staffOnJobMapper.updateById(job);
        return 0;
    }
    @Override
    public StaffOnJobDto staffOnJobDetail(Long id) {
        StaffOnJob staffOnJob  = staffOnJobMapper.selectById(id);
        StaffOnJob staffOnJob = staffOnJobMapper.selectById(id);
        if (staffOnJob == null) {
            throw new BaseException("Staff not found, id=" + id);
        }
        StaffOnJobDto staffOnJobDto = new StaffOnJobDto();
        BeanUtils.copyProperties(staffOnJob, staffOnJobDto);
        // 查询岗位名称
        SysPost post = sysPostMapper.selectPostById((long) staffOnJob.getSysPostId());
        staffOnJobDto.setPostName(post.getPostName());
        // 查询合同信息
        if (staffOnJob.getSysPostId() != null) {
            SysPost post = sysPostMapper.selectPostById(staffOnJob.getSysPostId().longValue());
            if (post != null) {
                staffOnJobDto.setPostName(post.getPostName());
            }
        }
        StaffContract contract = staffContractMapper.selectOne(Wrappers.<StaffContract>lambdaQuery()
                .eq(StaffContract::getStaffOnJobId, staffOnJob.getId())
                .orderByDesc(StaffContract::getId));
        if (contract != null){
                .orderByDesc(StaffContract::getId)
                .last("limit 1"));
        if (contract != null) {
            staffOnJobDto.setContractTerm(contract.getContractTerm());
            staffOnJobDto.setContractStartTime(contract.getContractStartTime());
            staffOnJobDto.setContractEndTime(contract.getContractEndTime());
        }
        fillNationFallback(staffOnJobDto);
        return staffOnJobDto;
    }
    //在职员工导出
    @Override
    public void staffOnJobExport(HttpServletResponse response, StaffOnJob staffOnJob) {
        List<StaffOnJobDto> staffOnJobs = staffOnJobMapper.staffOnJobList(staffOnJob);
        ExcelUtil<StaffOnJobDto> util = new ExcelUtil<StaffOnJobDto>(StaffOnJobDto.class);
        fillNationFallback(staffOnJobs);
        ExcelUtil<StaffOnJobDto> util = new ExcelUtil<>(StaffOnJobDto.class);
        util.exportExcel(response, staffOnJobs, "在职员工台账导出");
    }
    @Override
    public List<StaffJoinLeaveRecord> staffOnJobList() {
        return staffJoinLeaveRecordMapper.staffOnJobList();
    public List<StaffOnJobDto> staffOnJobList(StaffOnJob staffOnJob) {
        List<StaffOnJobDto> staffOnJobs = staffOnJobMapper.staffOnJobList(staffOnJob);
        fillNationFallback(staffOnJobs);
        return staffOnJobs;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean importData(MultipartFile file) {
        try {
            ExcelUtil<StaffOnJob> util = new ExcelUtil<>(StaffOnJob.class);
            List<StaffOnJob> staffOnJobs = util.importExcel(file.getInputStream());
            return saveOrUpdateBatch(staffOnJobs);
            ExcelUtil<StaffOnJobImportDto> util = new ExcelUtil<>(StaffOnJobImportDto.class);
            List<StaffOnJobImportDto> staffOnJobs = util.importExcel(file.getInputStream());
            if (CollectionUtils.isEmpty(staffOnJobs)) {
                throw new BaseException("Import data is empty");
            }
            Map<String, SysPost> postMap = buildPostMap();
            Map<String, SysDept> deptMap = buildDeptMap();
            Set<String> importedStaffNos = new HashSet<>();
            for (int i = 0; i < staffOnJobs.size(); i++) {
                StaffOnJobDto staffOnJobDto = buildImportStaff(staffOnJobs.get(i), i + 2, postMap, deptMap, importedStaffNos);
                add(staffOnJobDto);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
            if (e instanceof BaseException) {
                throw (BaseException) e;
            }
            throw new BaseException("Import failed, please check template and data format");
        }
    }
    private StaffOnJobDto buildImportStaff(StaffOnJobImportDto row, int rowNum, Map<String, SysPost> postMap,
                                           Map<String, SysDept> deptMap, Set<String> importedStaffNos) {
        String staffNo = normalizeValue(row.getStaffNo());
        String postName = normalizeValue(row.getPostName());
        String deptName = normalizeValue(row.getDeptName());
        if (StringUtils.isBlank(staffNo)) {
            throw new BaseException("Row " + rowNum + " staffNo cannot be blank");
        }
        if (!importedStaffNos.add(staffNo)) {
            throw new BaseException("Row " + rowNum + " duplicated staffNo: " + staffNo);
        }
        if (StringUtils.isBlank(normalizeValue(row.getStaffName()))) {
            throw new BaseException("Row " + rowNum + " staffName cannot be blank");
        }
        if (StringUtils.isBlank(postName)) {
            throw new BaseException("Row " + rowNum + " postName cannot be blank");
        }
        if (StringUtils.isBlank(deptName)) {
            throw new BaseException("Row " + rowNum + " deptName cannot be blank");
        }
        if (row.getContractStartTime() == null) {
            throw new BaseException("Row " + rowNum + " contractStartTime cannot be blank");
        }
        if (row.getContractEndTime() == null) {
            throw new BaseException("Row " + rowNum + " contractEndTime cannot be blank");
        }
        if (row.getEntryDate() == null) {
            throw new BaseException("Row " + rowNum + " entryDate cannot be blank");
        }
        SysPost post = postMap.get(postName);
        if (post == null) {
            throw new BaseException("Row " + rowNum + " post not found or disabled: " + postName);
        }
        SysDept dept = deptMap.get(deptName);
        if (dept == null) {
            throw new BaseException("Row " + rowNum + " dept not found or disabled: " + deptName);
        }
        StaffOnJobDto staffOnJobDto = new StaffOnJobDto();
        BeanUtils.copyProperties(row, staffOnJobDto);
        staffOnJobDto.setStaffNo(staffNo);
        staffOnJobDto.setStaffName(normalizeValue(row.getStaffName()));
        staffOnJobDto.setSex(normalizeValue(row.getSex()));
        staffOnJobDto.setNation(normalizeValue(row.getNation()));
        staffOnJobDto.setNativePlace(normalizeValue(row.getNation()));
        staffOnJobDto.setBirthDate(row.getBirthDate());
        staffOnJobDto.setIdentityCard(normalizeValue(row.getIdentityCard()));
        staffOnJobDto.setAdress(normalizeValue(row.getAdress()));
        staffOnJobDto.setPhone(normalizeValue(row.getPhone()));
        staffOnJobDto.setEmergencyContactPhone(normalizeValue(row.getEmergencyContactPhone()));
        staffOnJobDto.setEntryDate(row.getEntryDate());
        staffOnJobDto.setContractTerm(normalizeValue(row.getContractTerm()));
        staffOnJobDto.setSysPostId(post.getPostId().intValue());
        staffOnJobDto.setSysDeptId(dept.getDeptId().intValue());
        syncStaffBasicFields(staffOnJobDto);
        return staffOnJobDto;
    }
    private void syncStaffBasicFields(StaffOnJobDto staffOnJobDto) {
        if (staffOnJobDto == null) {
            return;
        }
        staffOnJobDto.setStaffNo(normalizeValue(staffOnJobDto.getStaffNo()));
        staffOnJobDto.setStaffName(normalizeValue(staffOnJobDto.getStaffName()));
        staffOnJobDto.setNation(normalizeValue(staffOnJobDto.getNation()));
        staffOnJobDto.setNativePlace(normalizeValue(staffOnJobDto.getNativePlace()));
        if (StringUtils.isBlank(staffOnJobDto.getNation())) {
            staffOnJobDto.setNation(staffOnJobDto.getNativePlace());
        }
        if (StringUtils.isBlank(staffOnJobDto.getNativePlace())) {
            staffOnJobDto.setNativePlace(staffOnJobDto.getNation());
        }
        if (staffOnJobDto.getEntryDate() == null) {
            staffOnJobDto.setEntryDate(staffOnJobDto.getContractStartTime());
        }
    }
    private void fillNationFallback(StaffOnJob staffOnJob) {
        if (staffOnJob == null) {
            return;
        }
        if (StringUtils.isBlank(staffOnJob.getNation())) {
            staffOnJob.setNation(staffOnJob.getNativePlace());
        }
        if (StringUtils.isBlank(staffOnJob.getNativePlace())) {
            staffOnJob.setNativePlace(staffOnJob.getNation());
        }
    }
    private void fillNationFallback(List<? extends StaffOnJob> staffOnJobs) {
        if (CollectionUtils.isEmpty(staffOnJobs)) {
            return;
        }
        staffOnJobs.forEach(this::fillNationFallback);
    }
    private Map<String, SysPost> buildPostMap() {
        SysPost query = new SysPost();
        query.setStatus("0");
        return buildUniqueMap(sysPostMapper.selectPostList(query), SysPost::getPostName, "post");
    }
    private Map<String, SysDept> buildDeptMap() {
        SysDept query = new SysDept();
        query.setStatus("0");
        return buildUniqueMap(sysDeptMapper.selectDeptList(query), SysDept::getDeptName, "dept");
    }
    private <T> Map<String, T> buildUniqueMap(List<T> dataList, Function<T, String> nameGetter, String fieldName) {
        Map<String, List<T>> groupedMap = dataList.stream()
                .filter(Objects::nonNull)
                .filter(item -> StringUtils.isNotBlank(normalizeValue(nameGetter.apply(item))))
                .collect(Collectors.groupingBy(item -> normalizeValue(nameGetter.apply(item))));
        List<String> duplicateNames = groupedMap.entrySet().stream()
                .filter(entry -> entry.getValue().size() > 1)
                .map(Map.Entry::getKey)
                .sorted()
                .collect(Collectors.toList());
        if (!duplicateNames.isEmpty()) {
            throw new BaseException("Duplicate " + fieldName + " names: " + String.join(", ", duplicateNames));
        }
        return groupedMap.entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().get(0)));
    }
    private String normalizeValue(String value) {
        return value == null ? null : value.trim();
    }
    @Override
    public String exportCopy(HttpServletResponse response, StaffOnJob staffOnJob) throws Exception {
        String url = "/javaWork/product-inventory-management/file/prod/" + staffOnJob.getStaffName() + "-劳动合同书.docx";
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_32);
        // 设置模板文件所在目录(绝对路径,例如:/templates/)
        cfg.setClassForTemplateLoading(StaffOnJobServiceImpl.class, "/static");
        cfg.setDefaultEncoding("UTF-8");
        //2.定义需要填充的变里
        // ① 构造员工信息(实际项目中可从数据库/Excel读取)
        WordDateDto staff = new WordDateDto();
        BeanUtils.copyProperties(staffOnJob, staff);
        // 通过合同年限,合同到期日期计算合同开始日期,在获取开始日期,结束日期的年月日数字
        // 合同到期日期 - 合同年限(Date类型)
        // 1. 将Date转换为Instant(时间戳)
        Instant instant = staff.getContractExpireTime().toInstant();
        // 也可以指定具体时区,例如Asia/Shanghai:
        LocalDate localDate = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();  // 合同结束时间
        LocalDate localDate1 = localDate.minusYears(Integer.parseInt(staff.getContractTerm()));// 合同开始时间
        // 签订日期转换lcoaldate
        LocalDate localDate = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
        LocalDate localDate1 = localDate.minusYears(Integer.parseInt(staff.getContractTerm()));
        LocalDate localDate2 = staff.getSignDate().toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
        // 试用日期转换lcoaldate
        LocalDate localDate3 = staff.getTrialStartDate().toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
        LocalDate localDate4 = staff.getTrialEndDate().toInstant().atZone(ZoneId.of("Asia/Shanghai")).toLocalDate();
        staff.setQyear(localDate2.getYear() + "");
        staff.setQmoth(localDate2.getMonthValue() + "");
        staff.setQday(localDate2.getDayOfMonth() + "");
        if(staff.getDateSelect().equals("A")){
        if (staff.getDateSelect().equals("A")) {
            staff.setSyear(localDate1.getYear() + "");
            staff.setSmoth(localDate1.getMonthValue() + "");
            staff.setSday(localDate1.getDayOfMonth() + "");
@@ -212,8 +395,7 @@
            staff.setSeyear(localDate4.getYear() + "");
            staff.setSemoth(localDate4.getMonthValue() + "");
            staff.setSeday(localDate4.getDayOfMonth() + "");
        }else if (staff.getDateSelect().equals("B")){
        } else if (staff.getDateSelect().equals("B")) {
            staff.setBsyear(localDate1.getYear() + "");
            staff.setBsmoth(localDate1.getMonthValue() + "");
            staff.setBsday(localDate1.getDayOfMonth() + "");
@@ -224,29 +406,24 @@
            staff.setBseyear(localDate4.getYear() + "");
            staff.setBsemoth(localDate4.getMonthValue() + "");
            staff.setBseday(localDate4.getDayOfMonth() + "");
        }else if (staff.getDateSelect().equals("C")){
        } else if (staff.getDateSelect().equals("C")) {
            staff.setCsyear(localDate1.getYear() + "");
            staff.setCsmoth(localDate1.getMonthValue() + "");
            staff.setCsday(localDate1.getDayOfMonth() + "");
        }
        Map<String,Object> data = new HashMap<>();
        data.put("item",staff);
        //3.加载XML 模板
        Map<String, Object> data = new HashMap<>();
        data.put("item", staff);
        Template template = cfg.getTemplate("劳动合同书.xml");
        //4.生成填充后的 XML 内容
        StringWriter out = new StringWriter();
        template.process(data, out);
        String filledXml = out.toString();
        //5.将XML内容写入交件并改为.docx 格式
        File outputFile = new File(url);
        try(FileOutputStream fos = new FileOutputStream(outputFile);
            OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
        try (FileOutputStream fos = new FileOutputStream(outputFile);
             OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8)) {
            osw.write(filledXml);
        }
        return url;
    }
}