package cn.iocoder.yudao.module.mes.service.safety; 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.risk.MesSafetyRiskPageReqVO; import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.risk.MesSafetyRiskSaveReqVO; import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyRiskDO; import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyHazardMapper; import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyRiskMapper; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import java.math.BigDecimal; import java.util.List; import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; /** * 安全生产 - 风险清单 Service 实现 * * LEC 法(格雷厄姆-金尼法): * D = L × E × C * 等级:D≥320 重大(1);160≤D<320 较大(2);70≤D<160 一般(3);D<70 低(4) * * @author 超级管理员 */ @Service @Validated public class MesSafetyRiskServiceImpl implements MesSafetyRiskService { /** 风险等级阈值 */ private static final BigDecimal LEVEL_1_THRESHOLD = new BigDecimal("320"); private static final BigDecimal LEVEL_2_THRESHOLD = new BigDecimal("160"); private static final BigDecimal LEVEL_3_THRESHOLD = new BigDecimal("70"); @Resource private MesSafetyRiskMapper riskMapper; @Resource private MesSafetyRiskUnitService riskUnitService; @Resource private MesSafetyHazardMapper hazardMapper; @Override public PageResult getRiskPage(MesSafetyRiskPageReqVO pageReqVO) { return riskMapper.selectPage(pageReqVO); } @Override public MesSafetyRiskDO getRisk(Long id) { return riskMapper.selectById(id); } @Override public Long createRisk(MesSafetyRiskSaveReqVO createReqVO) { // 1. 风险单元必须存在 riskUnitService.validateRiskUnitExists(createReqVO.getUnitId()); // 2. 编码唯一 validateCodeUnique(null, createReqVO.getCode()); // 3. 转换并计算 LEC MesSafetyRiskDO risk = BeanUtils.toBean(createReqVO, MesSafetyRiskDO.class); applyLecEvaluation(risk); riskMapper.insert(risk); return risk.getId(); } @Override public void updateRisk(MesSafetyRiskSaveReqVO updateReqVO) { validateRiskExists(updateReqVO.getId()); riskUnitService.validateRiskUnitExists(updateReqVO.getUnitId()); validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode()); MesSafetyRiskDO updateObj = BeanUtils.toBean(updateReqVO, MesSafetyRiskDO.class); applyLecEvaluation(updateObj); riskMapper.updateById(updateObj); } @Override public void deleteRisk(Long id) { validateRiskExists(id); // 已被隐患引用的风险不允许删除 Long hazardCount = hazardMapper.selectCountByRiskId(id); if (hazardCount != null && hazardCount > 0) { throw exception(MES_SAFETY_HAZARD_HAS_REFERENCE); } riskMapper.deleteById(id); } @Override public List getRiskListByUnitId(Long unitId) { return riskMapper.selectListByUnitId(unitId); } @Override public MesSafetyRiskDO validateRiskExists(Long id) { if (id == null) { throw exception(MES_SAFETY_RISK_NOT_EXISTS); } MesSafetyRiskDO risk = riskMapper.selectById(id); if (risk == null) { throw exception(MES_SAFETY_RISK_NOT_EXISTS); } return risk; } @Override public BigDecimal calculateRiskValue(BigDecimal likelihood, BigDecimal exposure, BigDecimal consequence) { if (likelihood == null || exposure == null || consequence == null) { return null; } if (likelihood.compareTo(BigDecimal.ZERO) <= 0 || exposure.compareTo(BigDecimal.ZERO) <= 0 || consequence.compareTo(BigDecimal.ZERO) <= 0) { throw exception(MES_SAFETY_RISK_LEC_INVALID); } // D = L × E × C,保留 2 位小数 return likelihood.multiply(exposure).multiply(consequence) .setScale(2, java.math.RoundingMode.HALF_UP); } @Override public Integer calculateRiskLevel(BigDecimal riskValue) { if (riskValue == null) { return null; } if (riskValue.compareTo(LEVEL_1_THRESHOLD) >= 0) { return 1; // 重大风险 } if (riskValue.compareTo(LEVEL_2_THRESHOLD) >= 0) { return 2; // 较大风险 } if (riskValue.compareTo(LEVEL_3_THRESHOLD) >= 0) { return 3; // 一般风险 } return 4; // 低风险 } /** * 计算并回填 D 值与风险等级 */ private void applyLecEvaluation(MesSafetyRiskDO risk) { // L/E/C 都为空时视为未评估,保持空值 if (risk.getLikelihood() == null && risk.getExposure() == null && risk.getConsequence() == null) { risk.setRiskValue(null); risk.setRiskLevel(null); return; } BigDecimal riskValue = calculateRiskValue(risk.getLikelihood(), risk.getExposure(), risk.getConsequence()); risk.setRiskValue(riskValue); risk.setRiskLevel(calculateRiskLevel(riskValue)); } /** * 校验编码唯一(id 为空表示新增) */ private void validateCodeUnique(Long id, String code) { MesSafetyRiskDO exists = riskMapper.selectByCode(code); if (exists == null) { return; } if (id == null || !Objects.equals(exists.getId(), id)) { throw exception(MES_SAFETY_RISK_CODE_DUPLICATE); } } }