| | |
| | | package cn.iocoder.yudao.module.mes.service.safety; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.hrm.api.employee.HrmEmployeeApi; |
| | | import cn.iocoder.yudao.module.hrm.api.employee.dto.HrmEmployeeSimpleDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.cert.SafetyCertificatePageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.cert.SafetyCertificateRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.cert.SafetyCertificateSaveReqVO; |
| | |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.SafetyErrorCodeConstants.*; |
| | |
| | | * |
| | | * <p>核心能力:证书到期自动预警。状态由 {@link #resolveStatus} 根据有效期实时推导, |
| | | * 不依赖手工维护,避免过期未及时更新状态。</p> |
| | | * |
| | | * <p>持证人通过 employeeId 关联人力资源的员工档案: |
| | | * 新增/修改时从档案带出姓名、工号、部门并落为快照;查询时实时回填最新档案信息, |
| | | * 员工调岗后证件列表也会跟着显示新部门。</p> |
| | | * |
| | | * @author 超级管理员 |
| | | */ |
| | |
| | | |
| | | @Resource |
| | | private SafetyCertificateMapper certificateMapper; |
| | | @Resource |
| | | private HrmEmployeeApi hrmEmployeeApi; |
| | | @Resource |
| | | private SafetyRefResolver refResolver; |
| | | |
| | | public Long createCertificate(SafetyCertificateSaveReqVO reqVO) { |
| | | reqVO.setId(null); |
| | | validateCertificateForCreate(reqVO); |
| | | SafetyCertificateDO cert = buildCertificate(reqVO); |
| | | SafetyCertificateDO cert = buildCertificate(reqVO, null); |
| | | certificateMapper.insert(cert); |
| | | return cert.getId(); |
| | | } |
| | |
| | | public void updateCertificate(SafetyCertificateSaveReqVO reqVO) { |
| | | SafetyCertificateDO old = validateCertificateExists(reqVO.getId()); |
| | | validateCertificateForUpdate(reqVO, old); |
| | | SafetyCertificateDO cert = buildCertificate(reqVO); |
| | | SafetyCertificateDO cert = buildCertificate(reqVO, old); |
| | | certificateMapper.updateById(cert); |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * 分页结果的展示字段补全(类型名称、状态名称、剩余天数) |
| | | * 批量补全持证人的最新员工档案信息(姓名、工号、部门、手机、关联用户) |
| | | * |
| | | * <p>查询时用档案实时值覆盖快照值,保证员工调岗后台账同步; |
| | | * 员工档案已被删除时回退到快照值,保证历史证件仍可展示。</p> |
| | | */ |
| | | public static void fillDisplayFields(SafetyCertificateDO cert, SafetyCertificateRespVO respVO) { |
| | | public void fillEmployeeInfo(List<SafetyCertificateDO> certs) { |
| | | List<Long> employeeIds = certs.stream() |
| | | .map(SafetyCertificateDO::getEmployeeId) |
| | | .filter(Objects::nonNull).distinct().toList(); |
| | | // 用 new HashMap<>() 而非 Map.of():employeeId 允许为 null, |
| | | // 后面 employeeMap.get(cert.getEmployeeId()) 在空集合场景会 get(null), |
| | | // Map.of() 的 get(null) 会抛 NPE 导致证件列表接口 500。 |
| | | Map<Long, HrmEmployeeSimpleDTO> employeeMap = employeeIds.isEmpty() ? new HashMap<>() |
| | | : hrmEmployeeApi.getEmployeeList(employeeIds).stream() |
| | | .collect(Collectors.toMap(HrmEmployeeSimpleDTO::getId, e -> e, (a, b) -> a)); |
| | | certs.forEach(cert -> { |
| | | HrmEmployeeSimpleDTO employee = employeeMap.get(cert.getEmployeeId()); |
| | | if (employee != null) { |
| | | cert.setEmployeeName(employee.getName()); |
| | | cert.setEmployeeNo(employee.getEmployeeNo()); |
| | | cert.setDeptName(employee.getDeptName()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 批量补全展示字段,持证人档案一次性查好,避免逐条查询 |
| | | */ |
| | | public void fillDisplayFieldsBatch(List<SafetyCertificateDO> certs, List<SafetyCertificateRespVO> resps) { |
| | | var employeeMap = refResolver.employeeMap(certs.stream().map(SafetyCertificateDO::getEmployeeId).toList()); |
| | | for (int i = 0; i < certs.size(); i++) { |
| | | fillDisplayFields(certs.get(i), resps.get(i), employeeMap.get(certs.get(i).getEmployeeId())); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 单个证件的展示字段补全(类型名称、状态名称、剩余天数、持证人档案) |
| | | */ |
| | | public void fillDisplayFields(SafetyCertificateDO cert, SafetyCertificateRespVO respVO) { |
| | | fillDisplayFields(cert, respVO, cert.getEmployeeId() == null ? null |
| | | : hrmEmployeeApi.getEmployee(cert.getEmployeeId())); |
| | | } |
| | | |
| | | private void fillDisplayFields(SafetyCertificateDO cert, SafetyCertificateRespVO respVO, |
| | | HrmEmployeeSimpleDTO employee) { |
| | | respVO.setCertTypeName(SafetyCertificateTypeEnum.getNameByType(cert.getCertType())); |
| | | String status = resolveStatus(cert.getExpireDate(), cert.getWarnDays(), cert.getStatus()); |
| | | respVO.setStatus(status); |
| | | respVO.setStatusName(SafetyCertificateStatusEnum.getNameByStatus(status)); |
| | | if (cert.getExpireDate() != null) { |
| | | respVO.setRemainingDays(ChronoUnit.DAYS.between(LocalDate.now(), cert.getExpireDate())); |
| | | } |
| | | if (employee != null) { |
| | | respVO.setEmployeeName(employee.getName()); |
| | | respVO.setEmployeeNo(employee.getEmployeeNo()); |
| | | respVO.setDeptName(employee.getDeptName()); |
| | | respVO.setPhone(employee.getPhone()); |
| | | respVO.setUserId(employee.getUserId()); |
| | | } |
| | | } |
| | | |
| | |
| | | return SafetyCertificateStatusEnum.VALID.getStatus(); |
| | | } |
| | | |
| | | private SafetyCertificateDO buildCertificate(SafetyCertificateSaveReqVO reqVO) { |
| | | /** |
| | | * 构建 DO:持证人姓名/工号/部门从员工档案带出并落为快照 |
| | | */ |
| | | private SafetyCertificateDO buildCertificate(SafetyCertificateSaveReqVO reqVO, SafetyCertificateDO old) { |
| | | HrmEmployeeSimpleDTO employee = hrmEmployeeApi.getEmployee(reqVO.getEmployeeId()); |
| | | if (employee == null) { |
| | | throw exception(SAFETY_CERT_EMPLOYEE_NOT_EXISTS); |
| | | } |
| | | SafetyCertificateDO cert = BeanUtils.toBean(reqVO, SafetyCertificateDO.class); |
| | | cert.setEmployeeName(StrUtil.trim(reqVO.getEmployeeName())); |
| | | cert.setEmployeeId(employee.getId()); |
| | | cert.setEmployeeName(employee.getName()); |
| | | cert.setEmployeeNo(employee.getEmployeeNo()); |
| | | cert.setDeptName(employee.getDeptName()); |
| | | cert.setCertNo(StrUtil.trim(reqVO.getCertNo())); |
| | | cert.setWarnDays(reqVO.getWarnDays() == null ? DEFAULT_WARN_DAYS : reqVO.getWarnDays()); |
| | | cert.setStatus(resolveStatus(reqVO.getExpireDate(), cert.getWarnDays(), reqVO.getStatus())); |
| | | // 已注销状态不允许通过编辑改回 |
| | | String currentStatus = old == null ? reqVO.getStatus() : old.getStatus(); |
| | | cert.setStatus(resolveStatus(reqVO.getExpireDate(), cert.getWarnDays(), currentStatus)); |
| | | cert.setRemark(reqVO.getRemark() == null ? "" : reqVO.getRemark().trim()); |
| | | return cert; |
| | | } |
| | |
| | | SafetyCertificateDO byCertNo = certificateMapper.selectByCertNo(StrUtil.trim(reqVO.getCertNo())); |
| | | if (byCertNo != null && !byCertNo.getId().equals(old.getId())) { |
| | | throw exception(SAFETY_CERT_NO_DUPLICATE); |
| | | } |
| | | // 保留原状态中的「已注销」,不允许通过编辑随意改回 |
| | | if (SafetyCertificateStatusEnum.REVOKED.getStatus().equals(old.getStatus()) |
| | | && !SafetyCertificateStatusEnum.REVOKED.getStatus().equals(reqVO.getStatus())) { |
| | | reqVO.setStatus(SafetyCertificateStatusEnum.REVOKED.getStatus()); |
| | | } |
| | | } |
| | | |