tuqin
7 天以前 33fe3368c6692d67e1fae5d2922ff614e5f56404
feat(safety): 安全生产三期 - 隐患排查治理

按客户需求「风险分级管控与隐患排查治理双重预防机制」的第二重落地。

状态流转:
0 待整改 --提交整改--> 20 待验收 --验收通过--> 30 已闭环
└--验收驳回--> 10 整改中(重新整改)

后端:
- 隐患台账 MesSafetyHazard:来源/等级/类别、关联风险单元、位置、发现人、
整改责任人与期限、整改要求、整改措施、验收信息
- 关键校验:
* 只有「待整改/整改中」可提交整改,已提交待验收的不可重复提交
* 只有「待验收」可验收
* 验收驳回必须填写驳回原因
* 已闭环的隐患不允许再修改基本信息(且 update 接口不回写状态与整改/验收字段)
- 超期判定由服务端计算(有期限、未闭环、期限早于今天)
- 反向引用保护:风险单元/风险清单被隐患引用后禁止删除
- 错误码 1_040_905_xxx;字典常量 MES_SAFETY_HAZARD_*

前端:
- 隐患列表:等级/来源字典渲染、超期红色标记、按状态动态显示可用操作
- 登记/修改表单、提交整改弹窗、整改验收弹窗(通过/驳回二选一,驳回必填原因)

SQL(mes_safety_hazard_init.sql,可重复执行):
- 建表 1 张、菜单挂到「安全生产」目录、字典 3 组、6 个按钮权限

注:本期只提交代码,SQL 尚未执行、未部署,待安全生产全部能力点完成后统一发布。
已添加11个文件
已修改4个文件
1071 ■■■■■ 文件已修改
sql/mysql/mes_safety_hazard_init.sql 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/MesSafetyHazardController.java 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardCheckReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardPageReqVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardRectifyReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardRespVO.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardSaveReqVO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/safety/MesSafetyHazardDO.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/safety/MesSafetyHazardMapper.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyHazardService.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyHazardServiceImpl.java 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyRiskServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyRiskUnitServiceImpl.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/mes_safety_hazard_init.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,136 @@
-- =====================================================================
-- å®‰å…¨ç”Ÿäº§æ¨¡å— â€”— ä¸‰æœŸï¼šé𐿂£æŽ’查治理
--
-- å®šä½ï¼šåŒé‡é¢„防机制的第二重。与一期「风险分级管控」配套使用:
--       é𐿂£å¯å…³è”到具体风险点,形成「风险识别 â†’ é𐿂£æŽ’查 â†’ æ•´æ”¹é—­çŽ¯ã€çš„å®Œæ•´é“¾è·¯ã€‚
--
-- çŠ¶æ€æµè½¬ï¼š
--   0 å¾…整改 â”€â”€æäº¤æ•´æ”¹â”€â”€> 20 å¾…验收 â”€â”€éªŒæ”¶é€šè¿‡â”€â”€> 30 å·²é—­çޝ
--                              â”‚
--                              â””──验收驳回──> 10 æ•´æ”¹ä¸­ï¼ˆé‡æ–°æ•´æ”¹ï¼‰
--
-- å†…容:
--   1. å»ºè¡¨ mes_safety_hazard(隐患台账)
--   2. å­—典:隐患来源 / é𐿂£ç­‰çº§ / é𐿂£çŠ¶æ€
--   3. èœå•:挂在「安全生产」目录下
--   4. æŽˆæƒè¶…级管理员
--
-- ç‰¹æ€§ï¼šå¯é‡å¤æ‰§è¡Œ
-- =====================================================================
-- ---------------------------------------------------------------------
-- 1. é𐿂£å°è´¦
-- ---------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS `mes_safety_hazard` (
  `id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',
  `code` varchar(64) NOT NULL COMMENT '隐患编码',
  `name` varchar(200) NOT NULL COMMENT '隐患名称/简述',
  `source` tinyint DEFAULT NULL COMMENT '隐患来源(字典 mes_safety_hazard_source)',
  `hazard_level` tinyint DEFAULT NULL COMMENT '隐患等级(字典 mes_safety_hazard_level:1一般 2重大)',
  `category` tinyint DEFAULT NULL COMMENT '隐患类别(复用 mes_safety_risk_category)',
  `unit_id` bigint DEFAULT NULL COMMENT '关联风险单元编号',
  `risk_id` bigint DEFAULT NULL COMMENT '关联风险清单编号',
  `workshop_id` bigint DEFAULT NULL COMMENT '所属车间编号',
  `location` varchar(255) DEFAULT NULL COMMENT '具体位置',
  `description` varchar(1000) DEFAULT NULL COMMENT '隐患描述',
  `found_date` date DEFAULT NULL COMMENT '发现日期',
  `founder_user_id` bigint DEFAULT NULL COMMENT '发现人编号',
  `charge_user_id` bigint DEFAULT NULL COMMENT '整改责任人编号',
  `deadline` date DEFAULT NULL COMMENT '整改期限',
  `rectify_requirement` varchar(1000) DEFAULT NULL COMMENT '整改要求',
  `status` tinyint NOT NULL DEFAULT '0' COMMENT '状态:0待整改 10整改中 20待验收 30已闭环',
  `measure` varchar(1000) DEFAULT NULL COMMENT '整改措施',
  `finish_date` date DEFAULT NULL COMMENT '整改完成日期',
  `check_user_id` bigint DEFAULT NULL COMMENT '验收人编号',
  `check_time` datetime DEFAULT NULL COMMENT '验收时间',
  `check_result` varchar(500) DEFAULT NULL COMMENT '验收意见/结论',
  `reject_reason` varchar(500) DEFAULT NULL COMMENT '验收驳回原因',
  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
  `creator` varchar(64) DEFAULT '' COMMENT '创建者',
  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `updater` varchar(64) DEFAULT '' COMMENT '更新者',
  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `uk_code` (`code`) USING BTREE,
  KEY `idx_status` (`status`) USING BTREE,
  KEY `idx_workshop` (`workshop_id`) USING BTREE,
  KEY `idx_risk` (`risk_id`) USING BTREE,
  KEY `idx_deadline` (`deadline`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC COMMENT='安全生产-隐患排查台账';
-- ---------------------------------------------------------------------
-- 2. å­—å…¸
-- ---------------------------------------------------------------------
DELETE FROM system_dict_data WHERE dict_type IN
  ('mes_safety_hazard_source', 'mes_safety_hazard_level', 'mes_safety_hazard_status');
DELETE FROM system_dict_type WHERE type IN
  ('mes_safety_hazard_source', 'mes_safety_hazard_level', 'mes_safety_hazard_status');
INSERT INTO system_dict_type (name, type, status, remark, creator, create_time, updater, update_time, deleted)
VALUES ('安全生产-隐患来源', 'mes_safety_hazard_source', 0, '隐患发现的途径', '1', NOW(), '1', NOW(), b'0'),
       ('安全生产-隐患等级', 'mes_safety_hazard_level', 0, '1一般隐患 2重大隐患', '1', NOW(), '1', NOW(), b'0'),
       ('安全生产-隐患状态', 'mes_safety_hazard_status', 0, '整改闭环状态', '1', NOW(), '1', NOW(), b'0');
INSERT INTO system_dict_data (sort, label, value, dict_type, status, color_type, creator, create_time, updater, update_time, deleted)
VALUES
-- é𐿂£æ¥æº
(1, '日常排查',  '1', 'mes_safety_hazard_source', 0, 'default',    '1', NOW(), '1', NOW(), b'0'),
(2, '专项检查',  '2', 'mes_safety_hazard_source', 0, 'processing', '1', NOW(), '1', NOW(), b'0'),
(3, '上级检查',  '3', 'mes_safety_hazard_source', 0, 'warning',    '1', NOW(), '1', NOW(), b'0'),
(4, '员工上报',  '4', 'mes_safety_hazard_source', 0, 'default',    '1', NOW(), '1', NOW(), b'0'),
(5, '其他',      '5', 'mes_safety_hazard_source', 0, 'default',    '1', NOW(), '1', NOW(), b'0'),
-- é𐿂£ç­‰çº§
(1, '一般隐患',  '1', 'mes_safety_hazard_level', 0, 'warning', '1', NOW(), '1', NOW(), b'0'),
(2, '重大隐患',  '2', 'mes_safety_hazard_level', 0, 'error',   '1', NOW(), '1', NOW(), b'0'),
-- é𐿂£çŠ¶æ€
(1, '待整改',   '0',  'mes_safety_hazard_status', 0, 'error',      '1', NOW(), '1', NOW(), b'0'),
(2, '整改中',   '10', 'mes_safety_hazard_status', 0, 'processing', '1', NOW(), '1', NOW(), b'0'),
(3, '待验收',   '20', 'mes_safety_hazard_status', 0, 'warning',    '1', NOW(), '1', NOW(), b'0'),
(4, '已闭环',   '30', 'mes_safety_hazard_status', 0, 'success',    '1', NOW(), '1', NOW(), b'0');
-- ---------------------------------------------------------------------
-- 3. èœå•(挂在 1065910 å®‰å…¨ç”Ÿäº§ä¸‹ï¼‰
-- ---------------------------------------------------------------------
DELETE FROM system_role_menu WHERE menu_id BETWEEN 1065927 AND 1065940;
DELETE FROM system_menu WHERE id BETWEEN 1065927 AND 1065940;
INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon,
                         component, component_name, status, visible, keep_alive, always_show,
                         creator, create_time, updater, update_time, deleted)
VALUES
(1065927, '隐患排查治理', '', 2, 4, 1065910, 'hazard', 'ep:search',
 'safety/hazard/index', 'SafetyHazard', 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065928, '隐患查询', 'mes:safety-hazard:query',   3, 1, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065929, '隐患登记', 'mes:safety-hazard:create',  3, 2, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065930, '隐患更新', 'mes:safety-hazard:update',  3, 3, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065931, '隐患删除', 'mes:safety-hazard:delete',  3, 4, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065932, '提交整改', 'mes:safety-hazard:rectify', 3, 5, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0'),
(1065933, '整改验收', 'mes:safety-hazard:check',   3, 6, 1065927, '', '', NULL, NULL, 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0');
-- ---------------------------------------------------------------------
-- 4. æŽˆæƒè¶…级管理员
-- ---------------------------------------------------------------------
INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted)
SELECT 1, m.id, '1', NOW(), '1', NOW(), b'0'
FROM system_menu m
WHERE m.id BETWEEN 1065927 AND 1065940;
-- ---------------------------------------------------------------------
-- 5. éªŒè¯
-- ---------------------------------------------------------------------
SELECT id, parent_id, name, path, component, permission, type
FROM system_menu WHERE id BETWEEN 1065910 AND 1065940 ORDER BY id;
SELECT dict_type, label, value FROM system_dict_data
WHERE dict_type LIKE 'mes_safety_hazard%' ORDER BY dict_type, sort;
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/MesSafetyHazardController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,183 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
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.MesSafetyHazardCheckReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardRectifyReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardRespVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkshopDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyHazardDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyRiskDO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyRiskUnitDO;
import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkshopMapper;
import cn.iocoder.yudao.module.mes.service.safety.MesSafetyHazardService;
import cn.iocoder.yudao.module.mes.service.safety.MesSafetyRiskService;
import cn.iocoder.yudao.module.mes.service.safety.MesSafetyRiskUnitService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.*;
import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
 * å®‰å…¨ç”Ÿäº§ - é𐿂£ Controller
 *
 * @author è¶…级管理员
 */
@Tag(name = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患排查治理")
@RestController
@RequestMapping("/mes/safety/hazard")
@Validated
public class MesSafetyHazardController {
    @Resource
    private MesSafetyHazardService hazardService;
    @Resource
    private MesSafetyRiskUnitService riskUnitService;
    @Resource
    private MesSafetyRiskService riskService;
    @Resource
    private MesMdWorkshopMapper workshopMapper;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "登记隐患")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:create')")
    public CommonResult<Long> createHazard(@Valid @RequestBody MesSafetyHazardSaveReqVO createReqVO) {
        return success(hazardService.createHazard(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新隐患")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:update')")
    public CommonResult<Boolean> updateHazard(@Valid @RequestBody MesSafetyHazardSaveReqVO updateReqVO) {
        hazardService.updateHazard(updateReqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除隐患")
    @Parameter(name = "id", description = "编号", required = true)
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:delete')")
    public CommonResult<Boolean> deleteHazard(@RequestParam("id") Long id) {
        hazardService.deleteHazard(id);
        return success(true);
    }
    @PutMapping("/rectify")
    @Operation(summary = "提交整改")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:rectify')")
    public CommonResult<Boolean> rectifyHazard(@Valid @RequestBody MesSafetyHazardRectifyReqVO reqVO) {
        hazardService.rectifyHazard(reqVO);
        return success(true);
    }
    @PutMapping("/check")
    @Operation(summary = "整改验收")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:check')")
    public CommonResult<Boolean> checkHazard(@Valid @RequestBody MesSafetyHazardCheckReqVO reqVO) {
        hazardService.checkHazard(reqVO);
        return success(true);
    }
    @GetMapping("/get")
    @Operation(summary = "获得隐患")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:query')")
    public CommonResult<MesSafetyHazardRespVO> getHazard(@RequestParam("id") Long id) {
        MesSafetyHazardDO hazard = hazardService.getHazard(id);
        if (hazard == null) {
            return success(null);
        }
        List<MesSafetyHazardRespVO> list = buildRespList(Collections.singletonList(hazard));
        return success(list.isEmpty() ? null : list.get(0));
    }
    @GetMapping("/page")
    @Operation(summary = "获得隐患分页")
    @PreAuthorize("@ss.hasPermission('mes:safety-hazard:query')")
    public CommonResult<PageResult<MesSafetyHazardRespVO>> getHazardPage(@Valid MesSafetyHazardPageReqVO pageReqVO) {
        PageResult<MesSafetyHazardDO> pageResult = hazardService.getHazardPage(pageReqVO);
        List<MesSafetyHazardRespVO> list = buildRespList(pageResult.getList());
        return success(new PageResult<>(list, pageResult.getTotal()));
    }
    /**
     * è½¬æ¢ä¸º VO å¹¶å¡«å……车间、人员、关联风险信息、超期标记
     */
    private List<MesSafetyHazardRespVO> buildRespList(List<MesSafetyHazardDO> list) {
        if (list == null || list.isEmpty()) {
            return Collections.emptyList();
        }
        List<MesSafetyHazardRespVO> respList = BeanUtils.toBean(list, MesSafetyHazardRespVO.class);
        // è½¦é—´åç§°
        Set<Long> workshopIds = list.stream().map(MesSafetyHazardDO::getWorkshopId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, String> workshopNameMap = new HashMap<>();
        if (!workshopIds.isEmpty()) {
            List<MesMdWorkshopDO> workshops = workshopMapper.selectByIds(workshopIds);
            workshops.forEach(w -> workshopNameMap.put(w.getId(), w.getName()));
        }
        // äººå‘˜å§“名(发现人 / æ•´æ”¹è´£ä»»äºº / éªŒæ”¶äººï¼‰
        Set<Long> userIds = new HashSet<>();
        list.forEach(h -> {
            if (h.getFounderUserId() != null) userIds.add(h.getFounderUserId());
            if (h.getChargeUserId() != null) userIds.add(h.getChargeUserId());
            if (h.getCheckUserId() != null) userIds.add(h.getCheckUserId());
        });
        Map<Long, AdminUserRespDTO> userMap = userIds.isEmpty()
                ? Collections.emptyMap() : adminUserApi.getUserMap(userIds);
        // é£Žé™©å•元名称
        Set<Long> unitIds = list.stream().map(MesSafetyHazardDO::getUnitId)
                .filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, MesSafetyRiskUnitDO> unitMap = riskUnitService.getRiskUnitMap(unitIds);
        LocalDate today = LocalDate.now();
        for (MesSafetyHazardRespVO vo : respList) {
            vo.setWorkshopName(workshopNameMap.get(vo.getWorkshopId()));
            AdminUserRespDTO founder = userMap.get(vo.getFounderUserId());
            if (founder != null) vo.setFounderUserName(founder.getNickname());
            AdminUserRespDTO charge = userMap.get(vo.getChargeUserId());
            if (charge != null) vo.setChargeUserName(charge.getNickname());
            AdminUserRespDTO checker = userMap.get(vo.getCheckUserId());
            if (checker != null) vo.setCheckUserName(checker.getNickname());
            MesSafetyRiskUnitDO unit = unitMap.get(vo.getUnitId());
            if (unit != null) vo.setUnitName(unit.getName());
            if (vo.getRiskId() != null) {
                MesSafetyRiskDO risk = riskService.getRisk(vo.getRiskId());
                if (risk != null) vo.setRiskName(risk.getName());
            }
            // è¶…期判断:有期限、未闭环、且期限早于今天
            vo.setOverdue(vo.getDeadline() != null
                    && !MesSafetyHazardDO.STATUS_CLOSED.equals(vo.getStatus())
                    && vo.getDeadline().isBefore(today));
        }
        return respList;
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardCheckReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患整改验收 Request VO")
@Data
public class MesSafetyHazardCheckReqVO {
    @Schema(description = "隐患编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @NotNull(message = "隐患编号不能为空")
    private Long id;
    @Schema(description = "验收是否通过", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
    @NotNull(message = "验收结论不能为空")
    private Boolean pass;
    @Schema(description = "验收意见(通过时填写)", example = "已整改到位")
    private String checkResult;
    @Schema(description = "驳回原因(不通过时必填)", example = "防护罩固定不牢")
    private String rejectReason;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class MesSafetyHazardPageReqVO extends PageParam {
    @Schema(description = "隐患编码", example = "YH-001")
    private String code;
    @Schema(description = "隐患名称", example = "配电箱缺防护罩")
    private String name;
    @Schema(description = "隐患来源", example = "1")
    private Integer source;
    @Schema(description = "隐患等级", example = "1")
    private Integer hazardLevel;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "所属车间编号", example = "7")
    private Long workshopId;
    @Schema(description = "整改责任人编号", example = "1")
    private Long chargeUserId;
    @Schema(description = "关联风险清单编号", example = "1")
    private Long riskId;
    @Schema(description = "发现日期-èµ·", example = "2026-11-01")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDate foundDateStart;
    @Schema(description = "发现日期-æ­¢", example = "2026-11-30")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDate foundDateEnd;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardRectifyReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患提交整改 Request VO")
@Data
public class MesSafetyHazardRectifyReqVO {
    @Schema(description = "隐患编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @NotNull(message = "隐患编号不能为空")
    private Long id;
    @Schema(description = "整改措施", requiredMode = Schema.RequiredMode.REQUIRED,
            example = "已加装防护罩并固定")
    @NotEmpty(message = "整改措施不能为空")
    private String measure;
    @Schema(description = "整改完成日期", example = "2026-11-05")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDate finishDate;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,109 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDate;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患 Response VO")
@Data
public class MesSafetyHazardRespVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "隐患编码", example = "YH-001")
    private String code;
    @Schema(description = "隐患名称", example = "配电箱缺防护罩")
    private String name;
    @Schema(description = "隐患来源", example = "1")
    private Integer source;
    @Schema(description = "隐患等级", example = "1")
    private Integer hazardLevel;
    @Schema(description = "隐患类别", example = "1")
    private Integer category;
    @Schema(description = "关联风险单元编号", example = "1")
    private Long unitId;
    @Schema(description = "关联风险单元名称")
    private String unitName;
    @Schema(description = "关联风险清单编号", example = "1")
    private Long riskId;
    @Schema(description = "关联风险名称")
    private String riskName;
    @Schema(description = "所属车间编号", example = "7")
    private Long workshopId;
    @Schema(description = "所属车间名称")
    private String workshopName;
    @Schema(description = "具体位置")
    private String location;
    @Schema(description = "隐患描述")
    private String description;
    @Schema(description = "发现日期")
    private LocalDate foundDate;
    @Schema(description = "发现人编号", example = "1")
    private Long founderUserId;
    @Schema(description = "发现人姓名")
    private String founderUserName;
    @Schema(description = "整改责任人编号", example = "1")
    private Long chargeUserId;
    @Schema(description = "整改责任人姓名")
    private String chargeUserName;
    @Schema(description = "整改期限")
    private LocalDate deadline;
    @Schema(description = "整改要求")
    private String rectifyRequirement;
    @Schema(description = "状态", example = "0")
    private Integer status;
    @Schema(description = "是否超期", example = "false")
    private Boolean overdue;
    @Schema(description = "整改措施")
    private String measure;
    @Schema(description = "整改完成日期")
    private LocalDate finishDate;
    @Schema(description = "验收人编号", example = "1")
    private Long checkUserId;
    @Schema(description = "验收人姓名")
    private String checkUserName;
    @Schema(description = "验收时间")
    private LocalDateTime checkTime;
    @Schema(description = "验收意见")
    private String checkResult;
    @Schema(description = "验收驳回原因")
    private String rejectReason;
    @Schema(description = "备注")
    private String remark;
    @Schema(description = "创建时间")
    private LocalDateTime createTime;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/safety/vo/hazard/MesSafetyHazardSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,73 @@
package cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
@Schema(description = "管理后台 - å®‰å…¨ç”Ÿäº§-隐患登记/修改 Request VO")
@Data
public class MesSafetyHazardSaveReqVO {
    @Schema(description = "编号", example = "1024")
    private Long id;
    @Schema(description = "隐患编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "YH-001")
    @NotEmpty(message = "隐患编码不能为空")
    private String code;
    @Schema(description = "隐患名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "配电箱缺防护罩")
    @NotEmpty(message = "隐患名称不能为空")
    private String name;
    @Schema(description = "隐患来源", example = "1")
    private Integer source;
    @Schema(description = "隐患等级", example = "1")
    private Integer hazardLevel;
    @Schema(description = "隐患类别", example = "1")
    private Integer category;
    @Schema(description = "关联风险单元编号", example = "1")
    private Long unitId;
    @Schema(description = "关联风险清单编号", example = "1")
    private Long riskId;
    @Schema(description = "所属车间编号", example = "7")
    private Long workshopId;
    @Schema(description = "具体位置", example = "造纸车间配电房")
    private String location;
    @Schema(description = "隐患描述")
    private String description;
    @Schema(description = "发现日期")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDate foundDate;
    @Schema(description = "发现人编号", example = "1")
    private Long founderUserId;
    @Schema(description = "整改责任人编号", example = "1")
    @NotNull(message = "整改责任人不能为空")
    private Long chargeUserId;
    @Schema(description = "整改期限")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
    private LocalDate deadline;
    @Schema(description = "整改要求")
    private String rectifyRequirement;
    @Schema(description = "备注")
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/safety/MesSafetyHazardDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,164 @@
package cn.iocoder.yudao.module.mes.dal.dataobject.safety;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
 * å®‰å…¨ç”Ÿäº§ - é𐿂£æŽ’查台账 DO
 *
 * çŠ¶æ€æµè½¬ï¼š
 * 0 å¾…整改 â”€â”€æäº¤æ•´æ”¹â”€â”€> 20 å¾…验收 â”€â”€éªŒæ”¶é€šè¿‡â”€â”€> 30 å·²é—­çޝ
 *                            â””──验收驳回──> 10 æ•´æ”¹ä¸­ï¼ˆé‡æ–°æ•´æ”¹ï¼‰
 *
 * @author è¶…级管理员
 */
@TableName("mes_safety_hazard")
@KeySequence("mes_safety_hazard_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class MesSafetyHazardDO extends BaseDO {
    /** çŠ¶æ€ï¼šå¾…æ•´æ”¹ */
    public static final Integer STATUS_PENDING = 0;
    /** çŠ¶æ€ï¼šæ•´æ”¹ä¸­ï¼ˆéªŒæ”¶é©³å›žåŽå›žåˆ°æ­¤çŠ¶æ€ï¼‰ */
    public static final Integer STATUS_RECTIFYING = 10;
    /** çŠ¶æ€ï¼šå¾…éªŒæ”¶ */
    public static final Integer STATUS_TO_CHECK = 20;
    /** çŠ¶æ€ï¼šå·²é—­çŽ¯ */
    public static final Integer STATUS_CLOSED = 30;
    @TableId
    private Long id;
    /**
     * é𐿂£ç¼–码
     */
    private String code;
    /**
     * é𐿂£åç§° / ç®€è¿°
     */
    private String name;
    /**
     * é𐿂£æ¥æº
     *
     * å­—å…¸ mes_safety_hazard_source:1日常排查 2专项检查 3上级检查 4员工上报 5其他
     */
    private Integer source;
    /**
     * é𐿂£ç­‰çº§
     *
     * å­—å…¸ mes_safety_hazard_level:1一般隐患 2重大隐患
     */
    private Integer hazardLevel;
    /**
     * é𐿂£ç±»åˆ«
     *
     * å¤ç”¨ mes_safety_risk_category
     */
    private Integer category;
    /**
     * å…³è”风险单元编号
     */
    private Long unitId;
    /**
     * å…³è”风险清单编号
     */
    private Long riskId;
    /**
     * æ‰€å±žè½¦é—´ç¼–号
     */
    private Long workshopId;
    /**
     * å…·ä½“位置
     */
    private String location;
    /**
     * é𐿂£æè¿°
     */
    private String description;
    /**
     * å‘现日期
     */
    private LocalDate foundDate;
    /**
     * å‘现人编号
     */
    private Long founderUserId;
    /**
     * æ•´æ”¹è´£ä»»äººç¼–号
     */
    private Long chargeUserId;
    /**
     * æ•´æ”¹æœŸé™
     */
    private LocalDate deadline;
    /**
     * æ•´æ”¹è¦æ±‚
     */
    private String rectifyRequirement;
    /**
     * çŠ¶æ€ï¼š0待整改 10整改中 20待验收 30已闭环
     */
    private Integer status;
    /**
     * æ•´æ”¹æŽªæ–½
     */
    private String measure;
    /**
     * æ•´æ”¹å®Œæˆæ—¥æœŸ
     */
    private LocalDate finishDate;
    /**
     * éªŒæ”¶äººç¼–号
     */
    private Long checkUserId;
    /**
     * éªŒæ”¶æ—¶é—´
     */
    private LocalDateTime checkTime;
    /**
     * éªŒæ”¶æ„è§
     */
    private String checkResult;
    /**
     * éªŒæ”¶é©³å›žåŽŸå› 
     */
    private String rejectReason;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/safety/MesSafetyHazardMapper.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.mes.dal.mysql.safety;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardPageReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyHazardDO;
import org.apache.ibatis.annotations.Mapper;
import java.time.LocalDate;
import java.util.List;
/**
 * å®‰å…¨ç”Ÿäº§ - é𐿂£å°è´¦ Mapper
 *
 * @author è¶…级管理员
 */
@Mapper
public interface MesSafetyHazardMapper extends BaseMapperX<MesSafetyHazardDO> {
    default PageResult<MesSafetyHazardDO> selectPage(MesSafetyHazardPageReqVO reqVO) {
        return selectPage(reqVO, new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .likeIfPresent(MesSafetyHazardDO::getCode, reqVO.getCode())
                .likeIfPresent(MesSafetyHazardDO::getName, reqVO.getName())
                .eqIfPresent(MesSafetyHazardDO::getSource, reqVO.getSource())
                .eqIfPresent(MesSafetyHazardDO::getHazardLevel, reqVO.getHazardLevel())
                .eqIfPresent(MesSafetyHazardDO::getStatus, reqVO.getStatus())
                .eqIfPresent(MesSafetyHazardDO::getWorkshopId, reqVO.getWorkshopId())
                .eqIfPresent(MesSafetyHazardDO::getChargeUserId, reqVO.getChargeUserId())
                .eqIfPresent(MesSafetyHazardDO::getRiskId, reqVO.getRiskId())
                .geIfPresent(MesSafetyHazardDO::getFoundDate, reqVO.getFoundDateStart())
                .leIfPresent(MesSafetyHazardDO::getFoundDate, reqVO.getFoundDateEnd())
                .orderByDesc(MesSafetyHazardDO::getId));
    }
    default MesSafetyHazardDO selectByCode(String code) {
        return selectOne(MesSafetyHazardDO::getCode, code);
    }
    /**
     * æŸ¥è¯¢æœªé—­çŽ¯ä¸”å·²è¶…æœŸçš„éšæ‚£ï¼ˆç”¨äºŽé€¾æœŸæé†’/统计)
     */
    default List<MesSafetyHazardDO> selectOverdueList(LocalDate today) {
        return selectList(new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .lt(MesSafetyHazardDO::getDeadline, today)
                .ne(MesSafetyHazardDO::getStatus, MesSafetyHazardDO.STATUS_CLOSED)
                .orderByAsc(MesSafetyHazardDO::getDeadline));
    }
    /**
     * æŒ‰çŠ¶æ€ç»Ÿè®¡æ•°é‡
     */
    default Long selectCountByStatus(Integer status) {
        return selectCount(new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .eq(MesSafetyHazardDO::getStatus, status));
    }
    /**
     * é£Žé™©å•元被引用数量(用于删除单元前的校验)
     */
    default Long selectCountByUnitId(Long unitId) {
        return selectCount(new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .eq(MesSafetyHazardDO::getUnitId, unitId));
    }
    /**
     * é£Žé™©æ¸…单被引用数量
     */
    default Long selectCountByRiskId(Long riskId) {
        return selectCount(new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .eq(MesSafetyHazardDO::getRiskId, riskId));
    }
    default List<MesSafetyHazardDO> selectAllList() {
        return selectList(new LambdaQueryWrapperX<MesSafetyHazardDO>()
                .orderByDesc(MesSafetyHazardDO::getId));
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/DictTypeConstants.java
@@ -125,4 +125,9 @@
    String MES_SAFETY_RISK_CATEGORY = "mes_safety_risk_category";   // é£Žé™©ç±»åˆ«
    String MES_SAFETY_ACCIDENT_TYPE = "mes_safety_accident_type";   // äº‹æ•…类型
    String MES_SAFETY_UNIT_STATUS = "mes_safety_unit_status";       // é£Žé™©å•元状态
    // é𐿂£æŽ’查治理
    String MES_SAFETY_HAZARD_SOURCE = "mes_safety_hazard_source";   // é𐿂£æ¥æº
    String MES_SAFETY_HAZARD_LEVEL = "mes_safety_hazard_level";     // é𐿂£ç­‰çº§ï¼ˆ1一般 2重大)
    String MES_SAFETY_HAZARD_STATUS = "mes_safety_hazard_status";   // é𐿂£çŠ¶æ€
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java
@@ -854,4 +854,11 @@
    ErrorCode MES_SAFETY_RISK_CODE_DUPLICATE = new ErrorCode(1_040_904_011, "风险编码已存在");
    ErrorCode MES_SAFETY_RISK_LEC_INVALID = new ErrorCode(1_040_904_012, "LEC è¯„估值不合法:L、E、C éƒ½å¿…须大于 0");
    // ========== MES å®‰å…¨ç”Ÿäº§ - é𐿂£æŽ’查治理(1-040-905-000) ==========
    ErrorCode MES_SAFETY_HAZARD_NOT_EXISTS = new ErrorCode(1_040_905_000, "隐患记录不存在");
    ErrorCode MES_SAFETY_HAZARD_CODE_DUPLICATE = new ErrorCode(1_040_905_001, "隐患编码已存在");
    ErrorCode MES_SAFETY_HAZARD_STATUS_INVALID = new ErrorCode(1_040_905_002, "当前隐患状态不允许该操作");
    ErrorCode MES_SAFETY_HAZARD_REJECT_REASON_REQUIRED = new ErrorCode(1_040_905_003, "验收不通过时必须填写驳回原因");
    ErrorCode MES_SAFETY_HAZARD_HAS_REFERENCE = new ErrorCode(1_040_905_004, "该记录已被隐患引用,无法删除");
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyHazardService.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.mes.service.safety;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardCheckReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardRectifyReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyHazardDO;
/**
 * å®‰å…¨ç”Ÿäº§ - é𐿂£ Service æŽ¥å£
 *
 * çŠ¶æ€æµè½¬ï¼š0 å¾…整改 â†’ 20 å¾…验收 â†’ 30 å·²é—­çŽ¯ï¼›éªŒæ”¶é©³å›ž â†’ 10 æ•´æ”¹ä¸­
 *
 * @author è¶…级管理员
 */
public interface MesSafetyHazardService {
    PageResult<MesSafetyHazardDO> getHazardPage(MesSafetyHazardPageReqVO pageReqVO);
    MesSafetyHazardDO getHazard(Long id);
    Long createHazard(MesSafetyHazardSaveReqVO createReqVO);
    void updateHazard(MesSafetyHazardSaveReqVO updateReqVO);
    void deleteHazard(Long id);
    /**
     * æäº¤æ•´æ”¹ï¼š0待整改 / 10整改中 â†’ 20待验收
     */
    void rectifyHazard(MesSafetyHazardRectifyReqVO reqVO);
    /**
     * æ•´æ”¹éªŒæ”¶ï¼š20待验收 â†’ 30已闭环(通过)或 10整改中(驳回)
     */
    void checkHazard(MesSafetyHazardCheckReqVO reqVO);
    MesSafetyHazardDO validateHazardExists(Long id);
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyHazardServiceImpl.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
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.hazard.MesSafetyHazardCheckReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardRectifyReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.hazard.MesSafetyHazardSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyHazardDO;
import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyHazardMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.time.LocalDate;
import java.time.LocalDateTime;
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 å®žçް
 *
 * @author è¶…级管理员
 */
@Service
@Validated
public class MesSafetyHazardServiceImpl implements MesSafetyHazardService {
    @Resource
    private MesSafetyHazardMapper hazardMapper;
    @Override
    public PageResult<MesSafetyHazardDO> getHazardPage(MesSafetyHazardPageReqVO pageReqVO) {
        return hazardMapper.selectPage(pageReqVO);
    }
    @Override
    public MesSafetyHazardDO getHazard(Long id) {
        return hazardMapper.selectById(id);
    }
    @Override
    public Long createHazard(MesSafetyHazardSaveReqVO createReqVO) {
        validateCodeUnique(null, createReqVO.getCode());
        MesSafetyHazardDO hazard = BeanUtils.toBean(createReqVO, MesSafetyHazardDO.class);
        // æ–°å»ºå³ä¸ºå¾…整改
        hazard.setStatus(MesSafetyHazardDO.STATUS_PENDING);
        if (hazard.getFoundDate() == null) {
            hazard.setFoundDate(LocalDate.now());
        }
        hazardMapper.insert(hazard);
        return hazard.getId();
    }
    @Override
    public void updateHazard(MesSafetyHazardSaveReqVO updateReqVO) {
        MesSafetyHazardDO exists = validateHazardExists(updateReqVO.getId());
        // å·²é—­çŽ¯çš„éšæ‚£ä¸å…è®¸å†ä¿®æ”¹åŸºæœ¬ä¿¡æ¯
        if (MesSafetyHazardDO.STATUS_CLOSED.equals(exists.getStatus())) {
            throw exception(MES_SAFETY_HAZARD_STATUS_INVALID);
        }
        validateCodeUnique(updateReqVO.getId(), updateReqVO.getCode());
        MesSafetyHazardDO updateObj = BeanUtils.toBean(updateReqVO, MesSafetyHazardDO.class);
        // çŠ¶æ€ä¸Žæ•´æ”¹/验收信息不通过此接口修改
        updateObj.setStatus(null);
        updateObj.setMeasure(null);
        updateObj.setFinishDate(null);
        updateObj.setCheckUserId(null);
        updateObj.setCheckTime(null);
        updateObj.setCheckResult(null);
        updateObj.setRejectReason(null);
        hazardMapper.updateById(updateObj);
    }
    @Override
    public void deleteHazard(Long id) {
        validateHazardExists(id);
        hazardMapper.deleteById(id);
    }
    @Override
    public void rectifyHazard(MesSafetyHazardRectifyReqVO reqVO) {
        MesSafetyHazardDO hazard = validateHazardExists(reqVO.getId());
        // åªæœ‰ã€Œå¾…整改」「整改中」可以提交整改;已提交待验收或已闭环不可重复提交
        if (!MesSafetyHazardDO.STATUS_PENDING.equals(hazard.getStatus())
                && !MesSafetyHazardDO.STATUS_RECTIFYING.equals(hazard.getStatus())) {
            throw exception(MES_SAFETY_HAZARD_STATUS_INVALID);
        }
        MesSafetyHazardDO updateObj = new MesSafetyHazardDO();
        updateObj.setId(reqVO.getId());
        updateObj.setMeasure(reqVO.getMeasure());
        updateObj.setFinishDate(reqVO.getFinishDate() != null ? reqVO.getFinishDate() : LocalDate.now());
        updateObj.setStatus(MesSafetyHazardDO.STATUS_TO_CHECK);
        // é‡æ–°æäº¤æ—¶æ¸…空上一次的驳回原因
        updateObj.setRejectReason("");
        hazardMapper.updateById(updateObj);
    }
    @Override
    public void checkHazard(MesSafetyHazardCheckReqVO reqVO) {
        MesSafetyHazardDO hazard = validateHazardExists(reqVO.getId());
        // åªæœ‰ã€Œå¾…验收」可以验收
        if (!MesSafetyHazardDO.STATUS_TO_CHECK.equals(hazard.getStatus())) {
            throw exception(MES_SAFETY_HAZARD_STATUS_INVALID);
        }
        MesSafetyHazardDO updateObj = new MesSafetyHazardDO();
        updateObj.setId(reqVO.getId());
        updateObj.setCheckResult(reqVO.getCheckResult());
        updateObj.setCheckTime(LocalDateTime.now());
        if (Boolean.TRUE.equals(reqVO.getPass())) {
            // éªŒæ”¶é€šè¿‡ â†’ é—­çޝ
            updateObj.setStatus(MesSafetyHazardDO.STATUS_CLOSED);
            updateObj.setRejectReason("");
        } else {
            // éªŒæ”¶é©³å›ž â†’ å¿…须填原因,退回整改中
            if (reqVO.getRejectReason() == null || reqVO.getRejectReason().trim().isEmpty()) {
                throw exception(MES_SAFETY_HAZARD_REJECT_REASON_REQUIRED);
            }
            updateObj.setStatus(MesSafetyHazardDO.STATUS_RECTIFYING);
            updateObj.setRejectReason(reqVO.getRejectReason());
        }
        hazardMapper.updateById(updateObj);
    }
    @Override
    public MesSafetyHazardDO validateHazardExists(Long id) {
        if (id == null) {
            throw exception(MES_SAFETY_HAZARD_NOT_EXISTS);
        }
        MesSafetyHazardDO hazard = hazardMapper.selectById(id);
        if (hazard == null) {
            throw exception(MES_SAFETY_HAZARD_NOT_EXISTS);
        }
        return hazard;
    }
    private void validateCodeUnique(Long id, String code) {
        MesSafetyHazardDO exists = hazardMapper.selectByCode(code);
        if (exists == null) {
            return;
        }
        if (id == null || !Objects.equals(exists.getId(), id)) {
            throw exception(MES_SAFETY_HAZARD_CODE_DUPLICATE);
        }
    }
}
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyRiskServiceImpl.java
@@ -5,6 +5,7 @@
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;
@@ -40,6 +41,9 @@
    @Resource
    private MesSafetyRiskUnitService riskUnitService;
    @Resource
    private MesSafetyHazardMapper hazardMapper;
    @Override
    public PageResult<MesSafetyRiskDO> getRiskPage(MesSafetyRiskPageReqVO pageReqVO) {
@@ -77,6 +81,11 @@
    @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);
    }
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/safety/MesSafetyRiskUnitServiceImpl.java
@@ -5,6 +5,7 @@
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.unit.MesSafetyRiskUnitPageReqVO;
import cn.iocoder.yudao.module.mes.controller.admin.safety.vo.unit.MesSafetyRiskUnitSaveReqVO;
import cn.iocoder.yudao.module.mes.dal.dataobject.safety.MesSafetyRiskUnitDO;
import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyHazardMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyRiskMapper;
import cn.iocoder.yudao.module.mes.dal.mysql.safety.MesSafetyRiskUnitMapper;
import jakarta.annotation.Resource;
@@ -35,6 +36,9 @@
    @Resource
    private MesSafetyRiskMapper riskMapper;
    @Resource
    private MesSafetyHazardMapper hazardMapper;
    @Override
    public PageResult<MesSafetyRiskUnitDO> getRiskUnitPage(MesSafetyRiskUnitPageReqVO pageReqVO) {
@@ -71,6 +75,11 @@
        if (riskCount != null && riskCount > 0) {
            throw exception(MES_SAFETY_RISK_UNIT_HAS_RISK);
        }
        // å·²è¢«é𐿂£å¼•用的单元也不允许删除
        Long hazardCount = hazardMapper.selectCountByUnitId(id);
        if (hazardCount != null && hazardCount > 0) {
            throw exception(MES_SAFETY_HAZARD_HAS_REFERENCE);
        }
        riskUnitMapper.deleteById(id);
    }