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.hazard.*; import cn.iocoder.yudao.module.mes.dal.dataobject.safety.SafetyHazardDO; import cn.iocoder.yudao.module.mes.dal.mysql.safety.SafetyHazardMapper; import cn.iocoder.yudao.module.mes.enums.safety.SafetyHazardLevelEnum; import cn.iocoder.yudao.module.mes.enums.safety.SafetyHazardSourceEnum; import cn.iocoder.yudao.module.mes.enums.safety.SafetyHazardStatusEnum; 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 SafetyHazardService { private static final DateTimeFormatter CODE_DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); @Resource private SafetyHazardMapper hazardMapper; @Resource private SafetyRefResolver refResolver; /** * 隐患上报(含移动端拍照上传的 photoUrls) */ @Transactional(rollbackFor = Exception.class) public Long createHazard(SafetyHazardSaveReqVO reqVO) { reqVO.setId(null); SafetyHazardDO hazard = BeanUtils.toBean(reqVO, SafetyHazardDO.class); hazard.setCode(hazardMapper.selectNextCode("HZ" + LocalDate.now().format(CODE_DATE))); hazard.setTitle(StrUtil.trim(reqVO.getTitle())); hazard.setReporterId(reqVO.getReporterId()); hazard.setReporter(refResolver.requireEmployee(reqVO.getReporterId(), SAFETY_EMPLOYEE_REQUIRED).getName()); hazard.setLevel(StrUtil.blankToDefault(reqVO.getLevel(), SafetyHazardLevelEnum.GENERAL.getLevel())); hazard.setSource(StrUtil.blankToDefault(reqVO.getSource(), SafetyHazardSourceEnum.SELF_CHECK.getSource())); hazard.setReportTime(reqVO.getReportTime() == null ? LocalDateTime.now() : reqVO.getReportTime()); hazard.setStatus(SafetyHazardStatusEnum.REPORTED.getStatus()); hazard.setRemark(reqVO.getRemark() == null ? "" : reqVO.getRemark().trim()); hazardMapper.insert(hazard); return hazard.getId(); } /** * 修改隐患基础信息(仅未分派前允许改关键字段) */ public void updateHazard(SafetyHazardSaveReqVO reqVO) { SafetyHazardDO old = validateHazardExists(reqVO.getId()); if (!SafetyHazardStatusEnum.REPORTED.getStatus().equals(old.getStatus()) && !SafetyHazardStatusEnum.ASSIGNED.getStatus().equals(old.getStatus())) { throw exception(SAFETY_HAZARD_STATUS_ERROR); } SafetyHazardDO update = BeanUtils.toBean(reqVO, SafetyHazardDO.class); // 编号与状态不允许通过普通编辑修改 update.setCode(null); update.setStatus(null); hazardMapper.updateById(update); } /** * 分派整改责任人 */ @Transactional(rollbackFor = Exception.class) public void assignHazard(SafetyHazardAssignReqVO reqVO) { SafetyHazardDO hazard = validateHazardExists(reqVO.getId()); if (!SafetyHazardStatusEnum.REPORTED.getStatus().equals(hazard.getStatus()) && !SafetyHazardStatusEnum.REJECTED.getStatus().equals(hazard.getStatus())) { throw exception(SAFETY_HAZARD_STATUS_ERROR); } if (reqVO.getAssigneeId() == null) { throw exception(SAFETY_HAZARD_ASSIGNEE_REQUIRED); } var assignee = refResolver.requireEmployee(reqVO.getAssigneeId(), SAFETY_HAZARD_ASSIGNEE_REQUIRED); SafetyHazardDO update = new SafetyHazardDO(); update.setId(hazard.getId()); update.setAssigneeId(assignee.getId()); update.setAssignee(assignee.getName()); update.setAssignTime(LocalDateTime.now()); update.setRectifyDeadline(reqVO.getRectifyDeadline()); update.setStatus(SafetyHazardStatusEnum.RECTIFYING.getStatus()); if (StrUtil.isNotBlank(reqVO.getRemark())) { update.setRemark(reqVO.getRemark().trim()); } hazardMapper.updateById(update); } /** * 提交整改结果 */ @Transactional(rollbackFor = Exception.class) public void rectifyHazard(SafetyHazardRectifyReqVO reqVO) { SafetyHazardDO hazard = validateHazardExists(reqVO.getId()); if (!SafetyHazardStatusEnum.RECTIFYING.getStatus().equals(hazard.getStatus()) && !SafetyHazardStatusEnum.REJECTED.getStatus().equals(hazard.getStatus())) { throw exception(SAFETY_HAZARD_STATUS_ERROR); } SafetyHazardDO update = new SafetyHazardDO(); update.setId(hazard.getId()); update.setRectifyDesc(reqVO.getRectifyDesc().trim()); update.setRectifyPhotoUrls(reqVO.getRectifyPhotoUrls()); update.setRectifyTime(LocalDateTime.now()); update.setStatus(SafetyHazardStatusEnum.RECHECKING.getStatus()); hazardMapper.updateById(update); } /** * 复查:通过则闭环,不通过退回整改。 * *校验整改人与复查人不能是同一人,保证闭环的两级分离。
*/ @Transactional(rollbackFor = Exception.class) public void recheckHazard(SafetyHazardRecheckReqVO reqVO) { SafetyHazardDO hazard = validateHazardExists(reqVO.getId()); if (!SafetyHazardStatusEnum.RECHECKING.getStatus().equals(hazard.getStatus())) { throw exception(SAFETY_HAZARD_STATUS_ERROR); } var rechecker = refResolver.requireEmployee(reqVO.getRecheckerId(), SAFETY_EMPLOYEE_REQUIRED); // 用员工编号比较,比姓名比较可靠(同名不同人 / 改名都不会误判) if (hazard.getAssigneeId() != null && hazard.getAssigneeId().equals(rechecker.getId())) { throw exception(SAFETY_HAZARD_SAME_OPERATOR); } boolean passed = "PASSED".equals(reqVO.getRecheckResult()); SafetyHazardDO update = new SafetyHazardDO(); update.setId(hazard.getId()); update.setRecheckerId(rechecker.getId()); update.setRechecker(rechecker.getName()); update.setRecheckTime(LocalDateTime.now()); update.setRecheckResult(reqVO.getRecheckResult()); update.setRecheckRemark(reqVO.getRecheckRemark()); if (passed) { update.setStatus(SafetyHazardStatusEnum.CLOSED.getStatus()); update.setCloseTime(LocalDateTime.now()); } else { update.setStatus(SafetyHazardStatusEnum.REJECTED.getStatus()); } hazardMapper.updateById(update); } public void deleteHazard(Long id) { validateHazardExists(id); hazardMapper.deleteById(id); } public SafetyHazardDO getHazard(Long id) { return validateHazardExists(id); } public PageResult