From 993095c3d36d62e52aaa6cb229c309231205f343 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期一, 19 十月 2026 17:11:52 +0800
Subject: [PATCH] feat(qualityMonitor): 实现质量监控检验任务完整流程 - 新增年度质量监控计划创建、提交审核及按年度查询接口 - 新增质量监控检验任务下发、查询及取消接口 - 新增质量监控检验评价提交与批准/退回流程 - 在检验订单中增加任务来源和质量监控明细ID字段 - 增加质控任务认领、原始记录填写、提交、复核等环节的权限校验 - 质控任务复核通过后发送评价通知给指定评价人 - 质控任务不生成检验报告,由评价环节完成归档 - 新增从已复核质控原始记录读取检测数据和上下文信息的方法 - 增加标准方法验证中实时查看所选设备校准证书的功能 - 补充质量监控计划明细的模板ID规范化及字段校验逻辑 - 在样品查询SQL中关联质控明细表返回任务来源和评价状态

---
 cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java |  162 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 153 insertions(+), 9 deletions(-)

diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java b/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java
index a559f4b..bf60509 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java
@@ -70,6 +70,9 @@
 @Transactional(rollbackFor = Exception.class)
 public class QualityMonitorServiceImpl extends ServiceImpl<QualityMonitorMapper, QualityMonitor> implements QualityMonitorService {
 
+    private static final Set<String> EDITABLE_PLAN_STATES = new HashSet<>(Arrays.asList(
+            "DRAFT", "REVIEW_REJECTED", "APPROVAL_REJECTED"));
+
     @Resource
     private QualityMonitorDetailsService qualityMonitorDetailsService;
     @Resource
@@ -96,6 +99,111 @@
     @Value("${wordUrl}")
     private String wordUrl;
 
+    @Override
+    public QualityMonitorDto createAnnualPlan(QualityMonitor input) {
+        if (input == null || StringUtils.isBlank(input.getMonitorYear())
+                || !input.getMonitorYear().matches("\\d{4}")) {
+            throw new ErrorException("璇烽�夋嫨姝g‘鐨勮鍒掑勾搴�");
+        }
+        if (input.getExamineUserId() == null || userMapper.selectById(input.getExamineUserId()) == null) {
+            throw new ErrorException("璇烽�夋嫨鏈夋晥鐨勫鏍镐汉");
+        }
+        if (StringUtils.isBlank(input.getMonitorName())) {
+            throw new ErrorException("璇峰~鍐欒鍒掑悕绉�");
+        }
+        int userId = SecurityUtils.getUserId().intValue();
+        QualityMonitor plan = new QualityMonitor();
+        plan.setMonitorYear(input.getMonitorYear());
+        plan.setMonitorName(input.getMonitorName().trim());
+        plan.setWriteUserId(userId);
+        plan.setWriteTime(LocalDateTime.now());
+        plan.setExamineUserId(input.getExamineUserId());
+        plan.setWorkflowStatus("DRAFT");
+        baseMapper.insert(plan);
+        return baseMapper.selectPlanDtoById(plan.getQualityMonitorId());
+    }
+
+    @Override
+    public QualityMonitorDto getAnnualPlan(String monitorYear, Integer qualityMonitorId) {
+        QualityMonitor plan;
+        if (qualityMonitorId != null) {
+            plan = baseMapper.selectById(qualityMonitorId);
+            if (plan == null || (StringUtils.isNotBlank(monitorYear)
+                    && !monitorYear.equals(plan.getMonitorYear()))) return null;
+        } else {
+            if (StringUtils.isBlank(monitorYear)) throw new ErrorException("璇烽�夋嫨璁″垝骞村害");
+            plan = baseMapper.selectOne(Wrappers.<QualityMonitor>lambdaQuery()
+                    .eq(QualityMonitor::getMonitorYear, monitorYear)
+                    .orderByDesc(QualityMonitor::getCreateTime, QualityMonitor::getQualityMonitorId)
+                    .last("limit 1"));
+        }
+        return plan == null ? null : baseMapper.selectPlanDtoById(plan.getQualityMonitorId());
+    }
+
+    @Override
+    public boolean submitAnnualPlan(Integer qualityMonitorId) {
+        QualityMonitor plan = requirePlan(qualityMonitorId);
+        int current = SecurityUtils.getUserId().intValue();
+        if (!Integer.valueOf(current).equals(plan.getWriteUserId())) {
+            throw new ErrorException("鍙湁璁″垝缂栧埗浜哄彲浠ユ彁浜ゅ鏍�");
+        }
+        String state = effectiveStatus(plan);
+        if (!EDITABLE_PLAN_STATES.contains(state)) throw new ErrorException("褰撳墠璁″垝鐘舵�佷笉鑳芥彁浜ゅ鏍�");
+        if (qualityMonitorDetailsMapper.selectCount(Wrappers.<QualityMonitorDetails>lambdaQuery()
+                .eq(QualityMonitorDetails::getQualityMonitorId, qualityMonitorId)) == 0) {
+            throw new ErrorException("璇疯嚦灏戞柊澧炰竴鏉¤川鎺ч」鐩悗鍐嶆彁浜ゅ鏍�");
+        }
+        baseMapper.update(null, Wrappers.<QualityMonitor>lambdaUpdate()
+                .eq(QualityMonitor::getQualityMonitorId, qualityMonitorId)
+                .set(QualityMonitor::getWorkflowStatus, "PENDING_REVIEW")
+                .set(QualityMonitor::getExamineStatus, null)
+                .set(QualityMonitor::getExamineRemark, null)
+                .set(QualityMonitor::getRatifyStatus, null));
+        sendPlanNotice(plan, plan.getExamineUserId(), "CNAS璐ㄩ噺鐩戞帶璁″垝瀹℃牳閫氱煡", "璐ㄩ噺鐩戞帶璁″垝寰呭鏍�");
+        return true;
+    }
+
+    @Override
+    public void validateDetailEditable(Integer qualityMonitorId) {
+        QualityMonitor plan = requirePlan(qualityMonitorId);
+        if (!Integer.valueOf(SecurityUtils.getUserId().intValue()).equals(plan.getWriteUserId())) {
+            throw new ErrorException("鍙湁璁″垝缂栧埗浜哄彲浠ョ淮鎶よ川鎺ч」鐩�");
+        }
+        if (!EDITABLE_PLAN_STATES.contains(effectiveStatus(plan))) {
+            throw new ErrorException("褰撳墠璁″垝宸叉彁浜わ紝涓嶈兘淇敼璐ㄦ帶椤圭洰");
+        }
+    }
+
+    private QualityMonitor requirePlan(Integer id) {
+        QualityMonitor plan = id == null ? null : baseMapper.selectById(id);
+        if (plan == null) throw new ErrorException("骞村害璐ㄩ噺鎺у埗璁″垝涓嶅瓨鍦�");
+        return plan;
+    }
+
+    private String effectiveStatus(QualityMonitor plan) {
+        if (StringUtils.isNotBlank(plan.getWorkflowStatus())) return plan.getWorkflowStatus();
+        if (Integer.valueOf(1).equals(plan.getRatifyStatus())) return "APPROVED";
+        if (Integer.valueOf(0).equals(plan.getRatifyStatus())) return "APPROVAL_REJECTED";
+        if (Integer.valueOf(1).equals(plan.getExamineStatus())) return "PENDING_APPROVAL";
+        if (Integer.valueOf(0).equals(plan.getExamineStatus())) return "REVIEW_REJECTED";
+        return "DRAFT";
+    }
+
+    private void sendPlanNotice(QualityMonitor plan, Integer receiverId, String theme, String suffix) {
+        if (receiverId == null) return;
+        int sender = SecurityUtils.getUserId().intValue();
+        User senderUser = userMapper.selectById(sender);
+        InformationNotification info = new InformationNotification();
+        info.setCreateUser(senderUser == null ? "" : senderUser.getName());
+        info.setMessageType("6");
+        info.setTheme(theme);
+        info.setContent(plan.getMonitorName() + suffix);
+        info.setSenderId(sender);
+        info.setConsigneeId(receiverId);
+        info.setJumpPath(MenuJumpPathConstants.QUALITY_MONITOR);
+        informationNotificationService.addInformationNotification(info);
+    }
+
 
     /**
      * 瀵煎叆鐩戞帶璁″垝
@@ -108,17 +216,18 @@
         Integer userId = SecurityUtils.getUserId().intValue();
         // 鏂囦欢鍚嶇О
         String fileName = file.getOriginalFilename().substring(0, file.getOriginalFilename().lastIndexOf("."));
+        if (monitor.getMonitorYear() == null || !monitor.getMonitorYear().matches("\\d{4}")) {
+            throw new ErrorException("璇烽�夋嫨姝g‘鐨勮鍒掑勾搴�");
+        }
+        if (monitor.getExamineUserId() == null) throw new ErrorException("缂哄皯瀹℃牳浜�");
         QualityMonitor qualityMonitor = new QualityMonitor();
         qualityMonitor.setMonitorName(fileName);
         qualityMonitor.setMonitorYear(monitor.getMonitorYear());
         qualityMonitor.setWriteUserId(userId);
         qualityMonitor.setExamineUserId(monitor.getExamineUserId());
         qualityMonitor.setWriteTime(LocalDateTime.now());
+        qualityMonitor.setWorkflowStatus("PENDING_REVIEW");
         baseMapper.insert(qualityMonitor);
-
-        if (monitor.getExamineUserId() == null) {
-            throw new ErrorException("缂哄皯瀹℃牳浜�");
-        }
 
         User user = userMapper.selectById(userId);
         // 娑堟伅鍙戦��
@@ -159,14 +268,27 @@
             EasyExcel.read(file.getInputStream(), QualityMonitorDetailsUpload.class, new AnalysisEventListener<QualityMonitorDetailsUpload>() {
                 @Override
                 public void invoke(QualityMonitorDetailsUpload detailsUpload, AnalysisContext analysisContext) {
-                    // 鍒ゆ柇鏄惁涓虹┖
-                    if (StringUtils.isBlank(detailsUpload.getPlannedTime())) {
-                        throw new ErrorException("璁″垝寮�灞曟椂闂翠笉鑳戒负绌�");
+                    if (StringUtils.isBlank(detailsUpload.getMonitorProject())
+                            || StringUtils.isBlank(detailsUpload.getTechnicalStandardText())
+                            || StringUtils.isBlank(detailsUpload.getMonitorWay())
+                            || StringUtils.isBlank(detailsUpload.getCompletionMonth())) {
+                        throw new ErrorException("璐ㄦ帶椤圭洰銆佹妧鏈爣鍑嗐�佽川鎺ф柟寮忓拰瀹屾垚鏃堕檺涓嶈兘涓虹┖");
                     }
                         // 瀵硅薄澶嶅埗
                     QualityMonitorDetails monitorDetails = new QualityMonitorDetails();
                     BeanUtils.copyProperties(detailsUpload, monitorDetails);
                     monitorDetails.setQualityMonitorId(qualityMonitor.getQualityMonitorId());
+                    if (!detailsUpload.getCompletionMonth().matches("\\d{4}-\\d{2}")) {
+                        throw new ErrorException("瀹屾垚鏃堕檺鏍煎紡搴斾负yyyy-MM");
+                    }
+                    if (StringUtils.isBlank(detailsUpload.getResponsibleName())) {
+                        throw new ErrorException("璐熻矗浜轰笉鑳戒负绌�");
+                    }
+                    List<User> users = userMapper.selectList(Wrappers.<User>lambdaQuery()
+                            .eq(User::getName, detailsUpload.getResponsibleName()));
+                    if (users.size() != 1) throw new ErrorException("璐熻矗浜哄鍚嶄笉瀛樺湪鎴栦笉鍞竴锛�" + detailsUpload.getResponsibleName());
+                    monitorDetails.setResponsibleUserId(users.get(0).getId());
+                    monitorDetails.setItemKind("OTHER");
 
                     detailsUploadList.add(monitorDetails);
                 }
@@ -191,8 +313,16 @@
      */
     @Override
     public boolean examineQualityMonitor(QualityMonitor qualityMonitor) {
-        if (qualityMonitor.getRatifyUserId() == null) {
-            throw new ErrorException("缂哄皯鎵瑰噯浜�");
+        QualityMonitor stored = requirePlan(qualityMonitor.getQualityMonitorId());
+        int current = SecurityUtils.getUserId().intValue();
+        if (!Integer.valueOf(current).equals(stored.getExamineUserId())) {
+            throw new ErrorException("鍙湁鎸囧畾瀹℃牳浜哄彲浠ュ鏍歌鍒�");
+        }
+        if (!"PENDING_REVIEW".equals(effectiveStatus(stored))) {
+            throw new ErrorException("褰撳墠璁″垝涓嶅湪寰呭鏍哥姸鎬�");
+        }
+        if (Integer.valueOf(1).equals(qualityMonitor.getExamineStatus()) && qualityMonitor.getRatifyUserId() == null) {
+            throw new ErrorException("瀹℃牳閫氳繃鏃跺繀椤绘寚瀹氭壒鍑嗕汉");
         }
 
         // 褰撳墠鐧诲綍鐢ㄦ埛
@@ -202,7 +332,11 @@
                 .set(QualityMonitor::getExamineRemark, qualityMonitor.getExamineRemark())
                 .set(QualityMonitor::getExamineStatus, qualityMonitor.getExamineStatus())
                 .set(QualityMonitor::getExamineTime, LocalDateTime.now())
+                .set(QualityMonitor::getWorkflowStatus,
+                        Integer.valueOf(1).equals(qualityMonitor.getExamineStatus()) ? "PENDING_APPROVAL" : "REVIEW_REJECTED")
         );
+
+        if (!Integer.valueOf(1).equals(qualityMonitor.getExamineStatus())) return true;
 
         QualityMonitor monitor = baseMapper.selectById(qualityMonitor.getQualityMonitorId());
 
@@ -247,12 +381,22 @@
      */
     @Override
     public boolean ratifyQualityMonitor(QualityMonitor qualityMonitor) {
+        QualityMonitor stored = requirePlan(qualityMonitor.getQualityMonitorId());
+        int current = SecurityUtils.getUserId().intValue();
+        if (!Integer.valueOf(current).equals(stored.getRatifyUserId())) {
+            throw new ErrorException("鍙湁鎸囧畾鎵瑰噯浜哄彲浠ユ壒鍑嗚鍒�");
+        }
+        if (!"PENDING_APPROVAL".equals(effectiveStatus(stored))) {
+            throw new ErrorException("褰撳墠璁″垝涓嶅湪寰呮壒鍑嗙姸鎬�");
+        }
         // 褰撳墠鐧诲綍鐢ㄦ埛
         baseMapper.update(null, Wrappers.<QualityMonitor>lambdaUpdate()
                 .eq(QualityMonitor::getQualityMonitorId, qualityMonitor.getQualityMonitorId())
                 .set(QualityMonitor::getRatifyRemark, qualityMonitor.getRatifyRemark())
                 .set(QualityMonitor::getRatifyStatus, qualityMonitor.getRatifyStatus())
                 .set(QualityMonitor::getRatifyTime, LocalDateTime.now())
+                .set(QualityMonitor::getWorkflowStatus,
+                        Integer.valueOf(1).equals(qualityMonitor.getRatifyStatus()) ? "APPROVED" : "APPROVAL_REJECTED")
         );
         return true;
     }

--
Gitblit v1.9.3