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.mes.controller.admin.safety.vo.permit.*; import cn.iocoder.yudao.module.mes.dal.dataobject.safety.SafetyWorkPermitDO; import cn.iocoder.yudao.module.mes.dal.dataobject.safety.SafetyWorkPermitMonitorDO; import cn.iocoder.yudao.module.mes.dal.mysql.safety.SafetyWorkPermitMapper; import cn.iocoder.yudao.module.mes.dal.mysql.safety.SafetyWorkPermitMonitorMapper; import cn.iocoder.yudao.module.mes.enums.safety.SafetyPermitStatusEnum; import cn.iocoder.yudao.module.mes.enums.safety.SafetyPermitTypeEnum; import cn.iocoder.yudao.module.mes.enums.safety.SafetyRiskLevelEnum; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.List; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.SafetyErrorCodeConstants.*; /** * 安全生产 - 高危作业票 Service * *

动火、高压带电、有限空间等作业的线上审批与电子留存。 * 状态流转:草稿 → 待审批 → 已批准 → 作业中 → 已完结;驳回/作废为终止态。

* * @author 超级管理员 */ @Service @Validated public class SafetyWorkPermitService { private static final DateTimeFormatter CODE_DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); @Resource private SafetyWorkPermitMapper permitMapper; @Resource private SafetyRefResolver refResolver; @Resource private SafetyWorkPermitMonitorMapper monitorMapper; /** * 提交作业申请(草稿或待审批) */ @Transactional(rollbackFor = Exception.class) public Long createPermit(SafetyWorkPermitSaveReqVO reqVO) { reqVO.setId(null); validateTimeRange(reqVO); SafetyWorkPermitDO permit = BeanUtils.toBean(reqVO, SafetyWorkPermitDO.class); permit.setCode(permitMapper.selectNextCode("WP" + LocalDate.now().format(CODE_DATE))); permit.setTitle(StrUtil.trim(reqVO.getTitle())); applyPermitRefs(permit, reqVO); permit.setApplyTime(reqVO.getApplyTime() == null ? LocalDateTime.now() : reqVO.getApplyTime()); permit.setRiskLevel(StrUtil.blankToDefault(reqVO.getRiskLevel(), SafetyRiskLevelEnum.NORMAL.getLevel())); permit.setStatus(SafetyPermitStatusEnum.PENDING.getStatus()); permit.setRemark(reqVO.getRemark() == null ? "" : reqVO.getRemark().trim()); permitMapper.insert(permit); return permit.getId(); } /** * 修改作业票(仅草稿/待审批可改) */ public void updatePermit(SafetyWorkPermitSaveReqVO reqVO) { SafetyWorkPermitDO old = validatePermitExists(reqVO.getId()); ensureStatus(old, SafetyPermitStatusEnum.DRAFT, SafetyPermitStatusEnum.PENDING); validateTimeRange(reqVO); SafetyWorkPermitDO update = BeanUtils.toBean(reqVO, SafetyWorkPermitDO.class); update.setCode(null); update.setStatus(null); applyPermitRefs(update, reqVO); permitMapper.updateById(update); } /** * 作业票的主数据引用:作业车间 + 申请人 + 监护人,从主数据带出名称落快照 */ private void applyPermitRefs(SafetyWorkPermitDO permit, SafetyWorkPermitSaveReqVO reqVO) { permit.setWorkshopId(reqVO.getWorkshopId()); permit.setWorkshopName(reqVO.getWorkshopId() == null ? "" : refResolver.requireWorkshop(reqVO.getWorkshopId()).getName()); var applicant = refResolver.requireEmployee(reqVO.getApplicantId(), SAFETY_EMPLOYEE_REQUIRED); permit.setApplicantId(applicant.getId()); permit.setApplicant(applicant.getName()); permit.setGuardianId(reqVO.getGuardianId()); permit.setGuardian(reqVO.getGuardianId() == null ? "" : refResolver.requireEmployee(reqVO.getGuardianId(), SAFETY_EMPLOYEE_REQUIRED).getName()); } /** * 审批作业票 * *

批准时校验高危作业(动火/高压带电/有限空间)必须指定监护人, * 保证作业票电子留存的完整性。

*/ @Transactional(rollbackFor = Exception.class) public void approvePermit(SafetyWorkPermitApproveReqVO reqVO) { SafetyWorkPermitDO permit = validatePermitExists(reqVO.getId()); ensureStatus(permit, SafetyPermitStatusEnum.PENDING); var approver = refResolver.requireEmployee(reqVO.getApproverId(), SAFETY_EMPLOYEE_REQUIRED); // 申请人不能审批自己的作业票(按员工编号比较,避免同名误判) if (permit.getApplicantId() != null && permit.getApplicantId().equals(approver.getId())) { throw exception(SAFETY_PERMIT_SELF_APPROVE); } boolean approved = "APPROVED".equals(reqVO.getDecision()); if (approved && SafetyPermitTypeEnum.requiresGuardian(permit.getPermitType()) && permit.getGuardianId() == null) { throw exception(SAFETY_PERMIT_GUARDIAN_REQUIRED); } SafetyWorkPermitDO update = new SafetyWorkPermitDO(); update.setId(permit.getId()); update.setApproverId(approver.getId()); update.setApprover(approver.getName()); update.setApproveTime(LocalDateTime.now()); update.setApproveComment(reqVO.getApproveComment()); update.setStatus(approved ? SafetyPermitStatusEnum.APPROVED.getStatus() : SafetyPermitStatusEnum.REJECTED.getStatus()); permitMapper.updateById(update); } /** * 开始作业(须已批准) */ public void startPermit(Long id) { SafetyWorkPermitDO permit = validatePermitExists(id); if (!SafetyPermitStatusEnum.APPROVED.getStatus().equals(permit.getStatus())) { throw exception(SAFETY_PERMIT_NOT_APPROVED); } SafetyWorkPermitDO update = new SafetyWorkPermitDO(); update.setId(id); update.setActualStartTime(LocalDateTime.now()); update.setStatus(SafetyPermitStatusEnum.IN_PROGRESS.getStatus()); permitMapper.updateById(update); } /** * 结束作业并归档 */ public void closePermit(Long id) { SafetyWorkPermitDO permit = validatePermitExists(id); ensureStatus(permit, SafetyPermitStatusEnum.IN_PROGRESS); SafetyWorkPermitDO update = new SafetyWorkPermitDO(); update.setId(id); update.setActualEndTime(LocalDateTime.now()); update.setStatus(SafetyPermitStatusEnum.CLOSED.getStatus()); permitMapper.updateById(update); } /** * 作废作业票(未完结前均可作废) */ public void cancelPermit(Long id) { SafetyWorkPermitDO permit = validatePermitExists(id); if (SafetyPermitStatusEnum.CLOSED.getStatus().equals(permit.getStatus()) || SafetyPermitStatusEnum.CANCELLED.getStatus().equals(permit.getStatus())) { throw exception(SAFETY_PERMIT_STATUS_ERROR); } SafetyWorkPermitDO update = new SafetyWorkPermitDO(); update.setId(id); update.setStatus(SafetyPermitStatusEnum.CANCELLED.getStatus()); permitMapper.updateById(update); } @Transactional(rollbackFor = Exception.class) public void deletePermit(Long id) { validatePermitExists(id); monitorMapper.deleteByPermitId(id); permitMapper.deleteById(id); } public SafetyWorkPermitDO getPermit(Long id) { return validatePermitExists(id); } public PageResult getPermitPage(SafetyWorkPermitPageReqVO reqVO) { return permitMapper.selectPage(reqVO); } // ==================== 监护记录 ==================== public Long createMonitor(SafetyWorkPermitMonitorSaveReqVO reqVO) { SafetyWorkPermitDO permit = validatePermitExists(reqVO.getPermitId()); if (!SafetyPermitStatusEnum.IN_PROGRESS.getStatus().equals(permit.getStatus()) && !SafetyPermitStatusEnum.APPROVED.getStatus().equals(permit.getStatus())) { throw exception(SAFETY_PERMIT_STATUS_ERROR); } SafetyWorkPermitMonitorDO monitor = BeanUtils.toBean(reqVO, SafetyWorkPermitMonitorDO.class); monitor.setId(null); var guardian = refResolver.requireEmployee(reqVO.getGuardianId(), SAFETY_EMPLOYEE_REQUIRED); monitor.setGuardianId(guardian.getId()); monitor.setGuardian(guardian.getName()); monitor.setMonitorTime(reqVO.getMonitorTime() == null ? LocalDateTime.now() : reqVO.getMonitorTime()); monitorMapper.insert(monitor); return monitor.getId(); } public void deleteMonitor(Long id) { if (id == null || monitorMapper.selectById(id) == null) { throw exception(SAFETY_PERMIT_MONITOR_NOT_EXISTS); } monitorMapper.deleteById(id); } public List getMonitorList(Long permitId) { return monitorMapper.selectListByPermitId(permitId); } // ==================== 展示字段 ==================== /** * 批量回填作业票的车间名与三个人员姓名(申请人/审批人/监护人) */ public void fillPermitRefs(List permits) { if (CollUtil.isEmpty(permits)) { return; } var workshopMap = refResolver.workshopMap(permits.stream().map(SafetyWorkPermitDO::getWorkshopId).toList()); var ids = new java.util.ArrayList(permits.size() * 3); permits.forEach(p -> { ids.add(p.getApplicantId()); ids.add(p.getApproverId()); ids.add(p.getGuardianId()); }); var employeeMap = refResolver.employeeMap(ids); permits.forEach(permit -> { var workshop = workshopMap.get(permit.getWorkshopId()); if (workshop != null) permit.setWorkshopName(workshop.getName()); var applicant = employeeMap.get(permit.getApplicantId()); if (applicant != null) permit.setApplicant(applicant.getName()); var approver = employeeMap.get(permit.getApproverId()); if (approver != null) permit.setApprover(approver.getName()); var guardian = employeeMap.get(permit.getGuardianId()); if (guardian != null) permit.setGuardian(guardian.getName()); }); } /** * 批量回填监护记录的监护人最新姓名 */ public void fillMonitorGuardians(List monitors) { if (CollUtil.isEmpty(monitors)) { return; } var employeeMap = refResolver.employeeMap( monitors.stream().map(SafetyWorkPermitMonitorDO::getGuardianId).toList()); monitors.forEach(monitor -> { var employee = employeeMap.get(monitor.getGuardianId()); if (employee != null) monitor.setGuardian(employee.getName()); }); } public static void fillPermitDisplay(SafetyWorkPermitDO permit, SafetyWorkPermitRespVO respVO) { respVO.setPermitTypeName(SafetyPermitTypeEnum.getNameByType(permit.getPermitType())); respVO.setRiskLevelName(SafetyRiskLevelEnum.getNameByLevel(permit.getRiskLevel())); respVO.setStatusName(SafetyPermitStatusEnum.getNameByStatus(permit.getStatus())); respVO.setGuardianRequired(SafetyPermitTypeEnum.requiresGuardian(permit.getPermitType())); } private void validateTimeRange(SafetyWorkPermitSaveReqVO reqVO) { if (reqVO.getPlanStartTime() != null && reqVO.getPlanEndTime() != null && !reqVO.getPlanEndTime().isAfter(reqVO.getPlanStartTime())) { throw exception(SAFETY_PERMIT_TIME_INVALID); } } private void ensureStatus(SafetyWorkPermitDO permit, SafetyPermitStatusEnum... allowed) { for (SafetyPermitStatusEnum status : allowed) { if (status.getStatus().equals(permit.getStatus())) { return; } } throw exception(SAFETY_PERMIT_STATUS_ERROR); } private SafetyWorkPermitDO validatePermitExists(Long id) { SafetyWorkPermitDO permit = id == null ? null : permitMapper.selectById(id); if (permit == null) { throw exception(SAFETY_PERMIT_NOT_EXISTS); } return permit; } }