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/pojo/QualityMonitorDetailsEvaluateFile.java            |    1 
 inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderMapper.java                           |    5 
 inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java                     |    7 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java                     |    2 
 cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsDto.java                      |   22 
 cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDto.java                             |    1 
 inspect-server/src/main/resources/mapper/InsOrderMapper.xml                                         |   16 
 cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitor.java                               |    7 
 cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorInspectionEvaluationService.java |  243 ++++++++++
 cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodVerifyService.java                |    4 
 cnas-process/src/main/resources/mapper/QualityMonitorTaskCodeMapper.xml                             |   30 +
 cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsEvaluateDto.java              |    1 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanTaskSwitchVo.java                     |    2 
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java            |  106 ++++
 cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorService.java                     |    8 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java                               |   13 
 inspect-server/src/main/resources/mapper/InsSampleMapper.xml                                        |   23 +
 cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorTaskService.java                 |  222 +++++++++
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java                                   |    6 
 inspect-server/src/main/java/com/ruoyi/inspect/vo/QualityMonitorRawContext.java                     |   23 +
 cnas-process/src/main/resources/mapper/QualityMonitorDetailsMapper.xml                              |   22 
 cnas-process/src/main/java/com/ruoyi/process/excel/QualityMonitorDetailsUpload.java                 |   38 +
 cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetails.java                        |   41 +
 inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java                                   |    1 
 cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluate.java                |   75 +++
 cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsRatify.java                  |    1 
 inspect-server/src/main/java/com/ruoyi/inspect/dto/InsOrderPlanDTO.java                             |    3 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java                   |    2 
 cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorEvaluateResultMapper.java         |    9 
 cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodVerifyController.java          |    7 
 cnas-process/src/main/java/com/ruoyi/process/controller/QualityMonitorController.java               |  165 +++++++
 cnas-process/src/main/java/com/ruoyi/process/service/impl/QualityMonitorServiceImpl.java            |  162 ++++++
 cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java       |   35 +
 cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorTaskCodeMapper.java               |   15 
 cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorMapper.java                       |    2 
 cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorEvaluateResult.java                 |   49 ++
 cnas-process/src/main/resources/mapper/QualityMonitorMapper.xml                                     |    9 
 inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderDeviceRecord.java                       |    2 
 38 files changed, 1,358 insertions(+), 22 deletions(-)

diff --git a/cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodVerifyController.java b/cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodVerifyController.java
index 19def0e..0254a43 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodVerifyController.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/controller/ProcessMethodVerifyController.java
@@ -5,6 +5,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.ruoyi.common.core.domain.Result;
 import com.ruoyi.process.dto.ProcessMethodVerifyDto;
+import com.ruoyi.process.dto.ProcessMethodVerifyCalibrationsFileDto;
 import com.ruoyi.process.pojo.ProcessMethodVerify;
 import com.ruoyi.process.pojo.ProcessMethodVerifyMethodFile;
 import com.ruoyi.process.pojo.ProcessMethodVerifyWorkFile;
@@ -81,6 +82,12 @@
         return Result.success(processMethodVerifyService.getCurrentWorkFiles(userIds));
     }
 
+    @Operation(summary = "瀹炴椂鏌ョ湅鎵�閫夎澶囨牎鍑嗚瘉涔�")
+    @GetMapping("/getCurrentCalibrationFiles")
+    public Result<List<ProcessMethodVerifyCalibrationsFileDto>> getCurrentCalibrationFiles(String deviceIds) {
+        return Result.success(processMethodVerifyService.getCurrentCalibrationFiles(deviceIds));
+    }
+
 
     /**
      * 淇敼鏍囧噯鏂规硶楠岃瘉
diff --git a/cnas-process/src/main/java/com/ruoyi/process/controller/QualityMonitorController.java b/cnas-process/src/main/java/com/ruoyi/process/controller/QualityMonitorController.java
index 31fc4a1..6b9805b 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/controller/QualityMonitorController.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/controller/QualityMonitorController.java
@@ -8,9 +8,14 @@
 import com.ruoyi.process.dto.QualityMonitorDetailsDto;
 import com.ruoyi.process.dto.QualityMonitorDto;
 import com.ruoyi.process.mapper.QualityMonitorDetailsEvaluateFileMapper;
+import com.ruoyi.process.mapper.QualityMonitorDetailsEvaluateMapper;
 import com.ruoyi.process.pojo.*;
 import com.ruoyi.process.service.QualityMonitorDetailsService;
 import com.ruoyi.process.service.QualityMonitorService;
+import com.ruoyi.process.service.QualityMonitorTaskService;
+import com.ruoyi.process.service.QualityMonitorInspectionEvaluationService;
+import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.system.mapper.UserMapper;
 import io.swagger.v3.oas.annotations.tags.Tag;
 import io.swagger.v3.oas.annotations.Operation;
 import lombok.AllArgsConstructor;
@@ -19,8 +24,11 @@
 
 import jakarta.annotation.Resource;
 import jakarta.servlet.http.HttpServletResponse;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
 
 
 /**
@@ -35,6 +43,24 @@
 @RequestMapping("/qualityMonitor")
 public class QualityMonitorController {
 
+    @Operation(summary = "鏂板缓骞村害璐ㄩ噺鎺у埗璁″垝")
+    @PostMapping("/createAnnualPlan")
+    public Result<QualityMonitorDto> createAnnualPlan(@RequestBody QualityMonitor qualityMonitor) {
+        return Result.success(qualityMonitorService.createAnnualPlan(qualityMonitor));
+    }
+
+    @Operation(summary = "鎸夊勾搴︽煡璇㈠綋鍓嶈川閲忔帶鍒惰鍒�")
+    @GetMapping("/getAnnualPlan")
+    public Result<QualityMonitorDto> getAnnualPlan(String monitorYear, Integer qualityMonitorId) {
+        return Result.success(qualityMonitorService.getAnnualPlan(monitorYear, qualityMonitorId));
+    }
+
+    @Operation(summary = "鎻愪氦骞村害璐ㄩ噺鎺у埗璁″垝瀹℃牳")
+    @PostMapping("/submitAnnualPlan")
+    public Result submitAnnualPlan(@RequestBody Map<String, Integer> request) {
+        return Result.success(qualityMonitorService.submitAnnualPlan(request.get("qualityMonitorId")));
+    }
+
     @Resource
     private QualityMonitorService qualityMonitorService;
 
@@ -43,6 +69,58 @@
 
     @Resource
     private QualityMonitorDetailsEvaluateFileMapper qualityMonitorDetailsEvaluateFileMapper;
+
+    @Resource
+    private QualityMonitorDetailsEvaluateMapper qualityMonitorDetailsEvaluateMapper;
+
+    @Resource
+    private QualityMonitorTaskService qualityMonitorTaskService;
+
+    @Resource
+    private QualityMonitorInspectionEvaluationService inspectionEvaluationService;
+
+    @Resource
+    private UserMapper userMapper;
+
+    @Operation(summary = "鑾峰彇璐ㄩ噺鐩戞帶妫�楠岃瘎浠�")
+    @GetMapping("/getInspectionEvaluation")
+    public Result<QualityMonitorDetailsEvaluate> getInspectionEvaluation(Integer qualityMonitorDetailsId) {
+        return Result.success(inspectionEvaluationService.get(qualityMonitorDetailsId));
+    }
+
+    @Operation(summary = "鎻愪氦璐ㄩ噺鐩戞帶妫�楠岃瘎浠�")
+    @PostMapping("/submitInspectionEvaluation")
+    public Result<QualityMonitorDetailsEvaluate> submitInspectionEvaluation(@RequestBody QualityMonitorDetailsEvaluate evaluation) {
+        return Result.success(inspectionEvaluationService.submit(evaluation));
+    }
+
+    @Operation(summary = "鎵瑰噯鎴栭��鍥炶川閲忕洃鎺ф楠岃瘎浠�")
+    @PostMapping("/approveInspectionEvaluation")
+    public Result approveInspectionEvaluation(@RequestBody Map<String, Object> request) {
+        Integer detailId = (Integer) request.get("qualityMonitorDetailsId");
+        boolean approved = Boolean.TRUE.equals(request.get("approved"));
+        inspectionEvaluationService.approve(detailId, approved, (String) request.get("opinion"));
+        return Result.success();
+    }
+
+    @Operation(summary = "涓嬪彂璐ㄩ噺鐩戞帶妫�楠屼换鍔�")
+    @PostMapping("/dispatchInspectionTask")
+    public Result<InsOrder> dispatchInspectionTask(@RequestBody Map<String, Integer> request) {
+        return Result.success(qualityMonitorTaskService.dispatch(request.get("qualityMonitorDetailsId")));
+    }
+
+    @Operation(summary = "鏌ヨ璐ㄩ噺鐩戞帶鏄庣粏鍏宠仈鐨勬楠屼换鍔�")
+    @GetMapping("/getInspectionTask")
+    public Result<InsOrder> getInspectionTask(Integer qualityMonitorDetailsId) {
+        return Result.success(qualityMonitorTaskService.getTask(qualityMonitorDetailsId));
+    }
+
+    @Operation(summary = "鍙栨秷鏈紑濮嬬殑璐ㄩ噺鐩戞帶妫�楠屼换鍔�")
+    @PostMapping("/cancelInspectionTask")
+    public Result cancelInspectionTask(@RequestBody Map<String, Integer> request) {
+        qualityMonitorTaskService.cancel(request.get("qualityMonitorDetailsId"));
+        return Result.success();
+    }
 
     /**
      * 瀵煎叆鐩戞帶璁″垝
@@ -116,6 +194,14 @@
         if (qualityMonitorDetails.getQualityMonitorId() == null) {
             throw new ErrorException("缂哄皯鐩戞帶璁″垝涓昏〃id");
         }
+        qualityMonitorService.validateDetailEditable(qualityMonitorDetails.getQualityMonitorId());
+        normalizeTemplateIds(qualityMonitorDetails);
+        validateAnnualDetail(qualityMonitorDetails);
+        if (qualityMonitorDetails.getSortNo() == null) {
+            qualityMonitorDetails.setSortNo((int) qualityMonitorDetailsService.count(
+                    com.baomidou.mybatisplus.core.toolkit.Wrappers.<QualityMonitorDetails>lambdaQuery()
+                            .eq(QualityMonitorDetails::getQualityMonitorId, qualityMonitorDetails.getQualityMonitorId())) + 1);
+        }
         return Result.success(qualityMonitorDetailsService.save(qualityMonitorDetails));
     }
 
@@ -126,6 +212,15 @@
     @Operation(summary = "淇敼鐩戞帶璁″垝璇︽儏")
     @PostMapping("/updateQualityMonitorDetail")
     public Result updateQualityMonitorDetail(@RequestBody QualityMonitorDetails qualityMonitorDetails) {
+        QualityMonitorDetails stored = qualityMonitorDetailsService.getById(qualityMonitorDetails.getQualityMonitorDetailsId());
+        if (stored == null) throw new ErrorException("璐ㄦ帶椤圭洰涓嶅瓨鍦�");
+        qualityMonitorDetails.setQualityMonitorId(stored.getQualityMonitorId());
+        qualityMonitorService.validateDetailEditable(stored.getQualityMonitorId());
+        normalizeTemplateIds(qualityMonitorDetails);
+        validateAnnualDetail(qualityMonitorDetails);
+        if (qualityMonitorTaskService.getTask(qualityMonitorDetails.getQualityMonitorDetailsId()) != null) {
+            throw new ErrorException("宸蹭笅鍙戞楠屼换鍔$殑璁″垝鏄庣粏涓嶅彲鐩存帴淇敼");
+        }
         return Result.success(qualityMonitorDetailsService.updateById(qualityMonitorDetails));
     }
 
@@ -136,7 +231,64 @@
     @Operation(summary = "鍒犻櫎鐩戞帶璁″垝璇︽儏")
     @DeleteMapping("/delQualityMonitorDetail")
     public Result delQualityMonitorDetail(Integer qualityMonitorDetailsId) {
+        QualityMonitorDetails stored = qualityMonitorDetailsService.getById(qualityMonitorDetailsId);
+        if (stored == null) throw new ErrorException("璐ㄦ帶椤圭洰涓嶅瓨鍦�");
+        qualityMonitorService.validateDetailEditable(stored.getQualityMonitorId());
+        if (qualityMonitorTaskService.getTask(qualityMonitorDetailsId) != null) {
+            throw new ErrorException("宸蹭笅鍙戞楠屼换鍔$殑璁″垝鏄庣粏涓嶅彲鍒犻櫎");
+        }
         return Result.success(qualityMonitorDetailsService.removeById(qualityMonitorDetailsId));
+    }
+
+    private void validateAnnualDetail(QualityMonitorDetails detail) {
+        if (!org.springframework.util.StringUtils.hasText(detail.getMonitorProject())
+                || !org.springframework.util.StringUtils.hasText(detail.getTechnicalStandardText())
+                || !org.springframework.util.StringUtils.hasText(detail.getMonitorWay())
+                || !org.springframework.util.StringUtils.hasText(detail.getCompletionMonth())
+                || !detail.getCompletionMonth().matches("\\d{4}-\\d{2}")
+                || detail.getResponsibleUserId() == null) {
+            throw new ErrorException("璇疯ˉ榻愯川鎺ч」鐩�佹妧鏈爣鍑嗐�佽川鎺ф柟寮忋�佸畬鎴愭椂闄愬拰璐熻矗浜�");
+        }
+        QualityMonitor plan = qualityMonitorService.getById(detail.getQualityMonitorId());
+        if (plan == null || !detail.getCompletionMonth().startsWith(plan.getMonitorYear() + "-")) {
+            throw new ErrorException("瀹屾垚鏃堕檺蹇呴』灞炰簬褰撳墠璁″垝骞村害");
+        }
+        int month = Integer.parseInt(detail.getCompletionMonth().substring(5));
+        if (month < 1 || month > 12) throw new ErrorException("瀹屾垚鏃堕檺鏈堜唤涓嶆纭�");
+        if (userMapper.selectById(detail.getResponsibleUserId()) == null) {
+            throw new ErrorException("璐熻矗浜轰笉瀛樺湪");
+        }
+    }
+
+    private void normalizeTemplateIds(QualityMonitorDetails detail) {
+        String rawIds = detail.getTemplateIds();
+        if (!org.springframework.util.StringUtils.hasText(rawIds) && detail.getTemplateId() != null) {
+            rawIds = String.valueOf(detail.getTemplateId());
+        }
+        if (!org.springframework.util.StringUtils.hasText(rawIds)) {
+            detail.setTemplateIds(null);
+            detail.setTemplateId(null);
+            return;
+        }
+        Set<Integer> templateIds = new LinkedHashSet<>();
+        try {
+            for (String rawId : rawIds.split(",")) {
+                if (!org.springframework.util.StringUtils.hasText(rawId)) continue;
+                int templateId = Integer.parseInt(rawId.trim());
+                if (templateId <= 0) throw new NumberFormatException();
+                templateIds.add(templateId);
+            }
+        } catch (NumberFormatException exception) {
+            throw new ErrorException("鍘熷璁板綍妯℃澘鍙傛暟涓嶆纭�");
+        }
+        if (templateIds.size() > 50) throw new ErrorException("鍘熷璁板綍妯℃澘鏈�澶氶�夋嫨50涓�");
+        if (templateIds.isEmpty()) {
+            detail.setTemplateIds(null);
+            detail.setTemplateId(null);
+            return;
+        }
+        detail.setTemplateIds(templateIds.stream().map(String::valueOf).collect(Collectors.joining(",")));
+        detail.setTemplateId(templateIds.iterator().next());
     }
 
     /**
@@ -211,6 +363,9 @@
     @Operation(summary = "鏂板鐩戞帶璇勪环")
     @PostMapping("/addQualityMonitorEvaluate")
     public Result addQualityMonitorEvaluate(@RequestBody QualityMonitorDetailsEvaluate qualityMonitorDetailsEvaluate) {
+        if (qualityMonitorTaskService.getTask(qualityMonitorDetailsEvaluate.getQualityMonitorDetailsId()) != null) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠岃瘎浠疯浣跨敤妫�楠岃瘎浠峰叆鍙�");
+        }
         return Result.success(qualityMonitorService.addQualityMonitorEvaluate(qualityMonitorDetailsEvaluate));
     }
 
@@ -231,6 +386,10 @@
     @Operation(summary = "鐩戞帶璇勪环鎵瑰噯鎰忚")
     @PostMapping("/addMonitorEvaluateOpinion")
     public Result addMonitorEvaluateOpinion(@RequestBody QualityMonitorDetailsEvaluate qualityMonitorDetailsEvaluate) {
+        QualityMonitorDetailsEvaluate stored = qualityMonitorDetailsEvaluateMapper.selectById(qualityMonitorDetailsEvaluate.getDetailsEvaluateId());
+        if (stored != null && qualityMonitorTaskService.getTask(stored.getQualityMonitorDetailsId()) != null) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠岃瘎浠疯浣跨敤妫�楠岃瘎浠峰鎵瑰叆鍙�");
+        }
         return Result.success(qualityMonitorService.addMonitorEvaluateOpinion(qualityMonitorDetailsEvaluate));
     }
 
@@ -279,6 +438,9 @@
     @Operation(summary = "涓婁紶鐩戞帶瀹屾垚鎶ュ憡")
     @PostMapping("/uploadFinishReport")
     public Result uploadFinishReport(MultipartFile file, Integer qualityMonitorDetailsId) {
+        if (qualityMonitorTaskService.getTask(qualityMonitorDetailsId) != null) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠屼换鍔℃棤闇�涓婁紶瀹屾垚鎶ュ憡");
+        }
         return Result.success(qualityMonitorService.uploadFinishReport(file, qualityMonitorDetailsId));
     }
 
@@ -290,6 +452,9 @@
     @Operation(summary = "鎵瑰噯瀹屾垚鎶ュ憡")
     @PostMapping("/ratifyFinishReport")
     public Result ratifyFinishReport(@RequestBody QualityMonitorDetails qualityMonitorDetails) {
+        if (qualityMonitorTaskService.getTask(qualityMonitorDetails.getQualityMonitorDetailsId()) != null) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠屼换鍔℃棤闇�鎵瑰噯瀹屾垚鎶ュ憡");
+        }
         return Result.success(qualityMonitorService.ratifyFinishReport(qualityMonitorDetails));
     }
 }
diff --git a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsDto.java b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsDto.java
index ccc0a9d..31bbf69 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsDto.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsDto.java
@@ -9,8 +9,30 @@
  * @Date 2025/3/24
  */
 @Data
+@Schema(description = "璐ㄩ噺鐩戞帶璁″垝鏄庣粏鍙婁换鍔¤瘎浠风姸鎬�")
 public class QualityMonitorDetailsDto extends QualityMonitorDetails {
 
+    @Schema(description = "璐熻矗浜哄鍚�")
+    private String responsibleName;
+
+    @Schema(description = "璇勪环浜哄鍚�")
+    private String evaluatorName;
+
+    @Schema(description = "鍏宠仈妫�楠屼换鍔D")
+    private Integer inspectionTaskId;
+
+    @Schema(description = "璐ㄩ噺鐩戞帶浠诲姟缂栧彿")
+    private String inspectionTaskCode;
+
+    @Schema(description = "妫�楠屼换鍔$姸鎬�")
+    private Integer inspectionTaskState;
+
+    private Integer inspectionTaskOrderState;
+
+    private Integer planRatifyStatus;
+
+    private Integer planRatifyUserId;
+
     @Schema(description = "瀹炴柦鐘舵��, 0: 鏈紑濮�, 1:寰呮壒鍑�, 2:涓嶆壒鍑�, 3:宸叉壒鍑�")
     private Integer detailsRatifyStatus;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsEvaluateDto.java b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsEvaluateDto.java
index b1a2241..6cffefe 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsEvaluateDto.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDetailsEvaluateDto.java
@@ -10,6 +10,7 @@
  * Description:
  */
 @Data
+@Schema(description = "璐ㄩ噺鎺у埗璇勪环璁板綍瀵煎嚭鏁版嵁")
 public class QualityMonitorDetailsEvaluateDto extends QualityMonitorDetailsEvaluate {
     @Schema(description = "鎵瑰噯鏃堕棿")
     private String ratifyTimeStr;
diff --git a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDto.java b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDto.java
index 3db11c2..8b5464f 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDto.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/dto/QualityMonitorDto.java
@@ -9,6 +9,7 @@
  * @Date 2024/11/6
  */
 @Data
+@Schema(description = "骞村害璐ㄩ噺鐩戞帶璁″垝鍙婄紪鍒跺鏍镐汉鍛樹俊鎭�")
 public class QualityMonitorDto extends QualityMonitor {
 
     @Schema(description = "缂栧埗浜�")
diff --git a/cnas-process/src/main/java/com/ruoyi/process/excel/QualityMonitorDetailsUpload.java b/cnas-process/src/main/java/com/ruoyi/process/excel/QualityMonitorDetailsUpload.java
index d6489af..4cb97bc 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/excel/QualityMonitorDetailsUpload.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/excel/QualityMonitorDetailsUpload.java
@@ -1,6 +1,7 @@
 package com.ruoyi.process.excel;
 
 import com.alibaba.excel.annotation.ExcelProperty;
+import io.swagger.v3.oas.annotations.media.Schema;
 import lombok.Data;
 
 /**
@@ -8,26 +9,51 @@
  * @Date 2024/11/8
  */
 @Data
+@Schema(description = "骞村害璐ㄩ噺鐩戞帶璁″垝鏄庣粏瀵煎叆琛�")
 public class QualityMonitorDetailsUpload {
 
-    @ExcelProperty("鐩戞帶鐩殑")
+    @Schema(description = "璁″垝鍐呮帓搴忓彿")
+    @ExcelProperty("搴忓彿")
+    private Integer sortNo;
+
+    @Schema(description = "璐ㄦ帶椤圭洰")
+    @ExcelProperty("璐ㄦ帶椤圭洰")
+    private String monitorProject;
+
+    @Schema(description = "鎶�鏈爣鍑�")
+    @ExcelProperty("鎶�鏈爣鍑�")
+    private String technicalStandardText;
+
+    @Schema(description = "璐ㄦ帶鏂瑰紡")
+    @ExcelProperty("璐ㄦ帶鏂瑰紡")
+    private String monitorWay;
+
+    @Schema(description = "璁″垝瀹屾垚鏈堜唤")
+    @ExcelProperty("瀹屾垚鏃堕檺")
+    private String completionMonth;
+
+    @Schema(description = "璐熻矗浜哄鍚�")
+    @ExcelProperty("璐熻矗浜�")
+    private String responsibleName;
+
+    @Schema(description = "鐩戞帶鐩殑锛屽吋瀹瑰巻鍙插鍏ュ垪")
+    @ExcelProperty(value = "鐩戞帶鐩殑", index = -1)
     private String monitorPurpose;
 
+    @Schema(description = "璁″垝寮�灞曟椂闂达紝鍏煎鍘嗗彶瀵煎叆鍒�")
     @ExcelProperty("璁″垝寮�灞曟椂闂�")
     private String plannedTime;
 
-    @ExcelProperty("鐩戞帶椤圭洰")
-    private String monitorProject;
-
+    @Schema(description = "鍙傚姞鍗曚綅鎴栦汉鍛�")
     @ExcelProperty("鍙傚姞鍗曚綅锛堜汉鍛橈級")
     private String participant;
 
+    @Schema(description = "棰勭畻閲戦")
     @ExcelProperty("棰勭畻锛堝厓锛�")
     private String budget;
 
+    @Schema(description = "缁勭粐浜哄憳")
     @ExcelProperty("缁勭粐浜哄憳")
     private String organization;
 
-    @ExcelProperty("鐩戞帶鏂瑰紡")
-    private String monitorWay;
 }
diff --git a/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorEvaluateResultMapper.java b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorEvaluateResultMapper.java
new file mode 100644
index 0000000..bc03a21
--- /dev/null
+++ b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorEvaluateResultMapper.java
@@ -0,0 +1,9 @@
+package com.ruoyi.process.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.ruoyi.process.pojo.QualityMonitorEvaluateResult;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface QualityMonitorEvaluateResultMapper extends BaseMapper<QualityMonitorEvaluateResult> {
+}
diff --git a/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorMapper.java b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorMapper.java
index bf4f81e..edbfde7 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorMapper.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorMapper.java
@@ -24,5 +24,7 @@
      */
     IPage<QualityMonitorDto> pageQualityMonitor(Page page, @Param("ew") QueryWrapper<QualityMonitor> qualityMonitorQueryWrapper);
 
+    QualityMonitorDto selectPlanDtoById(@Param("id") Integer id);
+
 }
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorTaskCodeMapper.java b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorTaskCodeMapper.java
new file mode 100644
index 0000000..f06f647
--- /dev/null
+++ b/cnas-process/src/main/java/com/ruoyi/process/mapper/QualityMonitorTaskCodeMapper.java
@@ -0,0 +1,15 @@
+package com.ruoyi.process.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+@Mapper
+public interface QualityMonitorTaskCodeMapper {
+    void ensureYear(@Param("year") int year);
+
+    int lockCurrent(@Param("year") int year);
+
+    void updateCurrent(@Param("year") int year, @Param("value") int value);
+
+    int countStandardTemplateBinding(@Param("standardId") int standardId, @Param("templateId") int templateId);
+}
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitor.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitor.java
index a56d3f8..3c16f7c 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitor.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitor.java
@@ -18,6 +18,7 @@
 @Schema(description = "璐ㄩ噺鐩戞帶璁″垝涓昏〃")
 public class QualityMonitor {
 
+    @Schema(description = "骞村害璐ㄩ噺鐩戞帶璁″垝ID")
     @TableId(type = IdType.AUTO)
     private Integer qualityMonitorId;
 
@@ -27,6 +28,12 @@
     @Schema(description = "璁″垝骞翠唤")
     private String monitorYear;
 
+    @Schema(description = "娴佺▼鐘舵�侊細DRAFT/PENDING_REVIEW/REVIEW_REJECTED/PENDING_APPROVAL/APPROVAL_REJECTED/APPROVED")
+    private String workflowStatus;
+
+    @Schema(description = "鍏煎鏃ф暟鎹殑鐗堟湰鏍囪锛屾柊娴佺▼涓嶅啀浣跨敤")
+    private Integer isCurrent;
+
     @Schema(description = "澶栭敭锛氱敤鎴穒d锛堢紪鍒朵汉锛�")
     private Integer writeUserId;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetails.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetails.java
index 667aabe..4fd0053 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetails.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetails.java
@@ -6,6 +6,7 @@
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.time.LocalDate;
 
 /**
  * 璐ㄩ噺鐩戞帶璁″垝璇︽儏琛�
@@ -18,11 +19,24 @@
 @Schema(description = "璐ㄩ噺鐩戞帶璁″垝璇︽儏琛�")
 public class QualityMonitorDetails {
 
+    @Schema(description = "璐ㄩ噺鐩戞帶璁″垝鏄庣粏ID")
     @TableId(type = IdType.AUTO)
     private Integer qualityMonitorDetailsId;
 
     @Schema(description = "鐩戞帶璁″垝id")
     private Integer qualityMonitorId;
+
+    @Schema(description = "璁″垝鍐呮帓搴忓彿")
+    private Integer sortNo;
+
+    @Schema(description = "骞村害璁″垝琛ㄤ腑鐨勬妧鏈爣鍑嗘枃鏈�")
+    private String technicalStandardText;
+
+    @Schema(description = "瀹屾垚鏈堜唤锛屾牸寮弝yyy-MM")
+    private String completionMonth;
+
+    @Schema(description = "璐熻矗浜虹敤鎴稩D")
+    private Integer responsibleUserId;
 
     @Schema(description = "鐩戞帶鐩殑")
     private String monitorPurpose;
@@ -44,6 +58,32 @@
 
     @Schema(description = "鐩戞帶鏂瑰紡")
     private String monitorWay;
+
+    @Schema(description = "浜嬮」绫诲埆锛欼NSPECTION妫�娴嬬被锛孫THER闈炴娴嬬被")
+    private String itemKind;
+
+    @Schema(description = "妫�娴嬫爣鍑咺D")
+    private Integer standardMethodId;
+
+    @Schema(description = "鍏煎鏃ф暟鎹殑棣栦釜鍘熷璁板綍妯℃澘ID")
+    private Integer templateId;
+
+    @Schema(description = "鍘熷璁板綍妯℃澘ID闆嗗悎锛屽涓狪D浣跨敤鑻辨枃閫楀彿鍒嗛殧")
+    private String templateIds;
+
+    @Schema(description = "妫�娴嬪憳鐢ㄦ埛ID")
+    private Integer inspectorUserId;
+    @Schema(description = "鍘嗗彶璁″垝澶嶆牳浜虹敤鎴稩D锛屾柊娴佺▼涓嶄娇鐢�")
+    private Integer reviewerUserId;
+    @Schema(description = "璇勪环浜虹敤鎴稩D")
+    private Integer evaluatorUserId;
+    @Schema(description = "妫�娴嬫牱鍝佸悕绉�")
+    private String sampleName;
+    @Schema(description = "妫�娴嬫牱鍝佽鏍�")
+    private String sampleSpec;
+    @Schema(description = "璐ㄦ帶妫�楠屼换鍔℃埅姝㈡棩鏈�")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate dueDate;
 
     @Schema(description = "瀹屾垚鎶ュ憡鍦板潃")
     private String finishReportUrl;
@@ -85,6 +125,7 @@
     private LocalDateTime updateTime;
 
     // 瀵煎嚭浣跨敤
+    @Schema(description = "瀵煎嚭琛屽簭鍙凤紝闈炴暟鎹簱瀛楁")
     @TableField(select = false, exist = false)
     private Integer index;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluate.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluate.java
index 47a0666..622320e 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluate.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluate.java
@@ -6,6 +6,8 @@
 import lombok.Data;
 
 import java.time.LocalDateTime;
+import java.time.LocalDate;
+import java.util.List;
 
 /**
  * 璐ㄩ噺鐩戞帶璁″垝璇︽儏璇勪环琛�
@@ -18,12 +20,85 @@
 @Schema(description = "璐ㄩ噺鐩戞帶璁″垝璇︽儏璇勪环琛�")
 public class QualityMonitorDetailsEvaluate {
 
+    @Schema(description = "璐ㄩ噺鎺у埗璇勪环璁板綍ID")
     @TableId(type = IdType.AUTO)
     private Integer detailsEvaluateId;
 
     @Schema(description = "鐩戞帶璁″垝璇︽儏id")
     private Integer qualityMonitorDetailsId;
 
+    @Schema(description = "鍏宠仈鐨勮川鎺ф楠屼换鍔D")
+    private Integer insOrderId;
+    @Schema(description = "鐩戠潱鍛樼敤鎴稩D锛屼笌璇勪环浜哄垎寮�")
+    private Integer supervisorUserId;
+    @Schema(description = "琚洃鐫f娴嬪憳鐢ㄦ埛ID")
+    private Integer inspectorUserId;
+    @Schema(description = "妫�娴嬫牱鍝佸悕绉�")
+    private String sampleName;
+    @Schema(description = "妫�娴嬫牱鍝佽鏍�")
+    private String sampleSpec;
+    @Schema(description = "鍘嗗彶鍗曚竴妫�娴嬫椂闂达紱鏂拌瘎浠烽〉闈粠鍘熷璁板綍璇诲彇鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime inspectionTime;
+    @Schema(description = "鍘嗗彶璁惧鍚嶇О锛涙柊璇勪环椤甸潰浠庡師濮嬭褰曡鍙栨墍鐢ㄨ澶�")
+    private String equipmentDescription;
+    @Schema(description = "鍘嗗彶璁惧瑙勬牸锛涙柊璇勪环椤甸潰浠庡師濮嬭褰曡鍙栨墍鐢ㄨ澶�")
+    private String equipmentSpec;
+    @Schema(description = "鍘嗗彶璁惧缂栧彿锛涙柊璇勪环椤甸潰浠庡師濮嬭褰曡鍙栨墍鐢ㄨ澶�")
+    private String equipmentCode;
+    @Schema(description = "妫�娴嬫儏鍐靛強璇勪环")
+    private String evaluationConclusion;
+    @Schema(description = "绾犳鎺柦鍙婇闃叉帾鏂�")
+    private String correctiveAction;
+    @Schema(description = "楠岃瘉鎯呭喌")
+    private String verificationResult;
+    @Schema(description = "绾犳鎺柦璐d换浜虹敤鎴稩D")
+    private Integer correctiveResponsibleUserId;
+    @Schema(description = "绾犳鎺柦瀹屾垚鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate correctiveDueDate;
+    @Schema(description = "楠岃瘉璐d换浜虹敤鎴稩D")
+    private Integer verificationResponsibleUserId;
+    @Schema(description = "楠岃瘉鏃ユ湡")
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    private LocalDate verificationDate;
+
+    @Schema(description = "妫�娴嬫暟鎹笌鐩叉牱鍊煎鐓ц锛岄潪鏁版嵁搴撳瓧娈�")
+    @TableField(exist = false)
+    private List<QualityMonitorEvaluateResult> resultRows;
+
+    @Schema(description = "璇勪环浜哄凡鏍稿鍘熷璁板綍妫�娴嬫暟鎹紝鎻愪氦鏍¢獙浣跨敤")
+    @TableField(exist = false)
+    private Boolean measurementConfirmed;
+
+    @Schema(description = "褰撳墠鐧诲綍鐢ㄦ埛鑳藉惁缂栧啓璇勪环锛岄潪鏁版嵁搴撳瓧娈�")
+    @TableField(exist = false)
+    private Boolean evaluationEditable;
+
+    @Schema(description = "褰撳墠涓嶈兘缂栧啓璇勪环鐨勫師鍥狅紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private String evaluationEditReason;
+
+    @Schema(description = "璐ㄦ帶妫�楠屼换鍔$姸鎬侊紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private Integer inspectionTaskState;
+
+    @Schema(description = "宸插鏍稿師濮嬭褰曚腑鐨勬娴嬫棩鏈熼泦鍚堬紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private List<String> rawInspectionDates;
+
+    @Schema(description = "宸插鏍稿師濮嬭褰曚腑鐨勮澶囧悕绉帮紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private String rawEquipmentDescription;
+
+    @Schema(description = "宸插鏍稿師濮嬭褰曚腑鐨勮澶囪鏍硷紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private String rawEquipmentSpec;
+
+    @Schema(description = "宸插鏍稿師濮嬭褰曚腑鐨勮澶囩紪鍙凤紝闈炴暟鎹簱瀛楁")
+    @TableField(exist = false)
+    private String rawEquipmentCode;
+
     @Schema(description = "璇勫鐩殑")
     private String reviewPurpose;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluateFile.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluateFile.java
index d2ef6cf..bf80ecc 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluateFile.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsEvaluateFile.java
@@ -18,6 +18,7 @@
 @Schema(description = "璐ㄩ噺鐩戞帶璁″垝璇︽儏璇勪环闄勪欢琛�")
 public class QualityMonitorDetailsEvaluateFile {
 
+    @Schema(description = "璇勪环闄勪欢ID")
     @TableId(type = IdType.AUTO)
     private Integer evaluateFileId;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsRatify.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsRatify.java
index a29da16..1458721 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsRatify.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorDetailsRatify.java
@@ -18,6 +18,7 @@
 @Schema(description = "璐ㄩ噺鐩戞帶璁″垝璇︽儏鎵瑰噯琛�")
 public class QualityMonitorDetailsRatify {
 
+    @Schema(description = "璐ㄩ噺鐩戞帶璁″垝鏄庣粏鎵瑰噯璁板綍ID")
     @TableId(type = IdType.AUTO)
     private Integer detailsRatifyId;
 
diff --git a/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorEvaluateResult.java b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorEvaluateResult.java
new file mode 100644
index 0000000..4cc7436
--- /dev/null
+++ b/cnas-process/src/main/java/com/ruoyi/process/pojo/QualityMonitorEvaluateResult.java
@@ -0,0 +1,49 @@
+package com.ruoyi.process.pojo;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.TableField;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+@TableName("cnas_quality_monitor_evaluate_result")
+@Schema(description = "璐ㄩ噺鎺у埗璇勪环璁板綍鐨勬娴嬫暟鎹笌鐩叉牱鍊煎鐓�")
+public class QualityMonitorEvaluateResult {
+
+    @Schema(description = "璇勪环鏁版嵁琛孖D")
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @Schema(description = "璐ㄩ噺鎺у埗璇勪环璁板綍ID")
+    private Integer detailsEvaluateId;
+
+    @Schema(description = "琚洃鐫f娴嬪憳鐢ㄦ埛ID")
+    private Integer inspectorUserId;
+
+    @Schema(description = "浠庡凡澶嶆牳鍘熷璁板綍甯﹀叆鐨勫綋鏃ユ娴嬫暟鎹�")
+    private String measuredValue;
+
+    @Schema(description = "璇勪环浜哄綍鍏ョ殑鐩叉牱鍊�")
+    private String referenceValue;
+
+    @Schema(description = "妫�娴嬫暟鎹崟浣�")
+    private String unit;
+
+    @Schema(description = "鍒ゅ畾瑙勫垯锛屽吋瀹瑰巻鍙茶瘎浠疯褰�")
+    private String acceptanceRule;
+
+    @Schema(description = "妫�娴嬪�间笌鍙傝�冨�煎亸宸紝鍏煎鍘嗗彶璇勪环璁板綍")
+    private String deviation;
+
+    @Schema(description = "鍗曡鍒ゅ畾缁撴灉锛屽吋瀹瑰巻鍙茶瘎浠疯褰�")
+    private String result;
+
+    @Schema(description = "鏁版嵁琛屾帓搴忓彿")
+    private Integer sortNo;
+
+    @Schema(description = "鍘熷璁板綍涓殑妫�娴嬮」鐩悕绉帮紝浠呯敤浜庡睍绀�")
+    @TableField(exist = false)
+    private String itemName;
+}
diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodVerifyService.java b/cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodVerifyService.java
index b0aad2d..097f8a6 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodVerifyService.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/ProcessMethodVerifyService.java
@@ -4,6 +4,7 @@
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.ruoyi.process.dto.ProcessMethodVerifyDto;
+import com.ruoyi.process.dto.ProcessMethodVerifyCalibrationsFileDto;
 import com.ruoyi.process.pojo.ProcessMethodVerify;
 import com.ruoyi.process.pojo.ProcessMethodVerifyMethodFile;
 import com.ruoyi.process.pojo.ProcessMethodVerifyWorkFile;
@@ -45,6 +46,9 @@
     /** 瀹炴椂璇诲彇鎵�閫夋娴嬩汉鍛樼殑浠昏亴鎺堟潈璇佷功銆� */
     List<ProcessMethodVerifyWorkFile> getCurrentWorkFiles(String userIds);
 
+    /** 瀹炴椂璇诲彇鎵�閫夎澶囩殑鏍″噯璇佷功銆� */
+    List<ProcessMethodVerifyCalibrationsFileDto> getCurrentCalibrationFiles(String deviceIds);
+
     /**
      * 淇敼鏍囧噯鏂规硶楠岃瘉
      * @param methodVerifyDto
diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorInspectionEvaluationService.java b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorInspectionEvaluationService.java
new file mode 100644
index 0000000..f94a4e1
--- /dev/null
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorInspectionEvaluationService.java
@@ -0,0 +1,243 @@
+package com.ruoyi.process.service;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.inspect.mapper.InsOrderStateMapper;
+import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.inspect.pojo.InsOrderState;
+import com.ruoyi.inspect.service.InsOrderPlanService;
+import com.ruoyi.inspect.vo.QualityMonitorRawContext;
+import com.ruoyi.process.mapper.QualityMonitorDetailsEvaluateMapper;
+import com.ruoyi.process.mapper.QualityMonitorDetailsMapper;
+import com.ruoyi.process.mapper.QualityMonitorEvaluateResultMapper;
+import com.ruoyi.process.mapper.QualityMonitorMapper;
+import com.ruoyi.process.pojo.QualityMonitor;
+import com.ruoyi.process.pojo.QualityMonitorDetails;
+import com.ruoyi.process.pojo.QualityMonitorDetailsEvaluate;
+import com.ruoyi.process.pojo.QualityMonitorEvaluateResult;
+import com.ruoyi.system.mapper.UserMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+@Service
+public class QualityMonitorInspectionEvaluationService {
+    @Resource
+    private QualityMonitorTaskService taskService;
+    @Resource
+    private QualityMonitorDetailsMapper detailsMapper;
+    @Resource
+    private QualityMonitorMapper monitorMapper;
+    @Resource
+    private QualityMonitorDetailsEvaluateMapper evaluateMapper;
+    @Resource
+    private QualityMonitorEvaluateResultMapper resultMapper;
+    @Resource
+    private InsOrderStateMapper stateMapper;
+    @Resource
+    private UserMapper userMapper;
+    @Resource
+    private InsOrderPlanService insOrderPlanService;
+
+    public QualityMonitorDetailsEvaluate get(Integer detailId) {
+        QualityMonitorDetails detail = requireDetail(detailId);
+        QualityMonitorDetailsEvaluate evaluation = evaluateMapper.selectOne(Wrappers.<QualityMonitorDetailsEvaluate>lambdaQuery()
+                .eq(QualityMonitorDetailsEvaluate::getQualityMonitorDetailsId, detailId).last("limit 1"));
+        InsOrder task = taskService.getTask(detailId);
+        if (evaluation == null) {
+            evaluation = new QualityMonitorDetailsEvaluate();
+            evaluation.setQualityMonitorDetailsId(detailId);
+            evaluation.setInspectorUserId(detail.getInspectorUserId());
+            evaluation.setSampleName(detail.getSampleName());
+            evaluation.setSampleSpec(detail.getSampleSpec());
+        }
+        List<QualityMonitorEvaluateResult> rows = evaluation.getDetailsEvaluateId() == null
+                ? List.of() : resultMapper.selectList(Wrappers.<QualityMonitorEvaluateResult>lambdaQuery()
+                                                      .eq(QualityMonitorEvaluateResult::getDetailsEvaluateId, evaluation.getDetailsEvaluateId())
+                                                      .orderByAsc(QualityMonitorEvaluateResult::getSortNo, QualityMonitorEvaluateResult::getId));
+        if (task != null && Integer.valueOf(5).equals(task.getInsState())) {
+            List<Map<String, String>> rawRows = insOrderPlanService.getQualityMonitorRawResults(task.getId());
+            fillRawContext(evaluation, insOrderPlanService.getQualityMonitorRawContext(task.getId()));
+            if (!rows.isEmpty() && rows.size() != rawRows.size())
+                throw new ErrorException("鍘熷璁板綍妫�娴嬫暟鎹凡鍙樺寲锛岃閲嶆柊鏍稿璇勪环璁板綍");
+            if (rows.isEmpty()) {
+                rows = new java.util.ArrayList<>();
+                for (int i = 0; i < rawRows.size(); i++) {
+                    QualityMonitorEvaluateResult row = new QualityMonitorEvaluateResult();
+                    row.setInspectorUserId(detail.getInspectorUserId());
+                    row.setMeasuredValue(rawRows.get(i).get("result"));
+                    row.setUnit(rawRows.get(i).get("unit"));
+                    row.setSortNo(i);
+                    rows.add(row);
+                }
+            }
+            for (int i = 0; i < rows.size(); i++) rows.get(i).setItemName(rawRows.get(i).get("itemName"));
+        }
+        evaluation.setResultRows(rows);
+        evaluation.setInspectionTaskState(task == null ? null : task.getInsState());
+        if (Integer.valueOf(1).equals(evaluation.getIsFinish())) {
+            evaluation.setEvaluationEditReason("璇勪环宸叉壒鍑嗗綊妗o紝鍙兘鏌ョ湅");
+        } else if (task == null) {
+            evaluation.setEvaluationEditReason("璐ㄦ帶妫�楠屼换鍔″皻鏈笅鍙戯紝鏆備笉鑳藉~鍐欒瘎浠�");
+        } else if (!Integer.valueOf(5).equals(task.getInsState())) {
+            evaluation.setEvaluationEditReason("鍘熷璁板綍澶嶆牳閫氳繃鍚庯紝鎸囧畾璇勪环浜烘墠鍙互濉啓");
+        } else if (detail.getEvaluatorUserId() == null) {
+            evaluation.setEvaluationEditReason("璁″垝椤圭洰灏氭湭鎸囧畾璇勪环浜�");
+        } else if (!Integer.valueOf(SecurityUtils.getUserId().intValue()).equals(detail.getEvaluatorUserId())) {
+            var evaluator = userMapper.selectById(detail.getEvaluatorUserId());
+            evaluation.setEvaluationEditReason("褰撳墠璐﹀彿涓嶆槸鎸囧畾璇勪环浜猴紙"
+                    + (evaluator == null ? "鐢ㄦ埛ID " + detail.getEvaluatorUserId() : evaluator.getName())
+                    + "锛夛紝鍙兘鏌ョ湅");
+        } else {
+            evaluation.setEvaluationEditable(true);
+        }
+        return evaluation;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public QualityMonitorDetailsEvaluate submit(QualityMonitorDetailsEvaluate input) {
+        if (input == null) throw new ErrorException("璇勪环鍐呭涓嶈兘涓虹┖");
+        QualityMonitorDetails detail = requireDetail(input.getQualityMonitorDetailsId());
+        InsOrder task = taskService.getTask(detail.getQualityMonitorDetailsId());
+        if (task == null || !QualityMonitorTaskService.SOURCE.equals(task.getTaskSource()))
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠屼换鍔′笉瀛樺湪");
+        if (!Integer.valueOf(5).equals(task.getInsState()))
+            throw new ErrorException("鍘熷璁板綍澶嶆牳閫氳繃鍚庢墠鑳借瘎浠�");
+        if (!Integer.valueOf(SecurityUtils.getUserId().intValue()).equals(detail.getEvaluatorUserId()))
+            throw new ErrorException("鍙湁鎸囧畾璇勪环浜哄彲浠ュ~鍐欒瘎浠�");
+        if (input.getSupervisorUserId() == null
+                || input.getSupervisorUserId().equals(detail.getInspectorUserId())
+                || input.getSupervisorUserId().equals(detail.getEvaluatorUserId())
+                || userMapper.selectById(input.getSupervisorUserId()) == null)
+            throw new ErrorException("璇烽�夋嫨涓庢娴嬪憳銆佽瘎浠蜂汉涓嶅悓鐨勭洃鐫e憳");
+        Long pendingCount = stateMapper.selectCount(Wrappers.<InsOrderState>lambdaQuery()
+                .eq(InsOrderState::getInsOrderId, task.getId()).ne(InsOrderState::getInsState, 5));
+        if (pendingCount > 0) throw new ErrorException("鍘熷璁板綍澶嶆牳閫氳繃鍚庢墠鑳借瘎浠�");
+        List<Map<String, String>> rawRows = insOrderPlanService.getQualityMonitorRawResults(task.getId());
+        QualityMonitorRawContext rawContext = insOrderPlanService.getQualityMonitorRawContext(task.getId());
+        if (rawContext.getDetectionDates().isEmpty() || rawContext.getDevices().isEmpty())
+            throw new ErrorException("鍘熷璁板綍缂哄皯妫�娴嬫椂闂存垨鎵�鐢ㄨ澶囷紝璇峰厛琛ラ綈骞跺鏍�");
+        if (!StringUtils.hasText(input.getEvaluationConclusion()) || input.getResultRows() == null
+                || input.getResultRows().size() != rawRows.size())
+            throw new ErrorException("璇峰~鍐欒瘎浠峰唴瀹瑰苟鏍稿鍏ㄩ儴鍘熷璁板綍妫�娴嬫暟鎹�");
+        if (!Boolean.TRUE.equals(input.getMeasurementConfirmed()))
+            throw new ErrorException("璇峰厛鏍稿宸插鏍稿師濮嬭褰曠殑妫�娴嬫暟鎹�");
+        QualityMonitorDetailsEvaluate existing = evaluateMapper.selectOne(Wrappers.<QualityMonitorDetailsEvaluate>lambdaQuery()
+                .eq(QualityMonitorDetailsEvaluate::getQualityMonitorDetailsId, detail.getQualityMonitorDetailsId())
+                .last("FOR UPDATE"));
+        if (existing != null && Integer.valueOf(1).equals(existing.getIsFinish()))
+            throw new ErrorException("宸叉壒鍑嗙殑璇勪环涓嶅彲淇敼");
+        QualityMonitor plan = monitorMapper.selectById(detail.getQualityMonitorId());
+        QualityMonitorDetailsEvaluate saved = existing == null ? new QualityMonitorDetailsEvaluate() : existing;
+        saved.setQualityMonitorDetailsId(detail.getQualityMonitorDetailsId());
+        saved.setInsOrderId(task.getId());
+        saved.setInspectorUserId(detail.getInspectorUserId());
+        saved.setImplementUserId(detail.getEvaluatorUserId());
+        saved.setRatifyUserId(plan.getRatifyUserId());
+        saved.setSupervisorUserId(input.getSupervisorUserId());
+        saved.setSampleName(detail.getSampleName());
+        saved.setSampleSpec(detail.getSampleSpec());
+        // 鏃堕棿鍜岃澶囧缁堢敱宸插鏍稿師濮嬭褰曡鍙栵紝璇勪环璇锋眰涓殑鍚屽悕瀛楁涓嶅弬涓庝繚瀛樸��
+        saved.setImplementCondition(input.getEvaluationConclusion());
+        saved.setImplementResult(input.getImplementResult());
+        saved.setEvaluationConclusion(input.getEvaluationConclusion());
+        saved.setCorrectiveAction(input.getCorrectiveAction());
+        saved.setVerificationResult(input.getVerificationResult());
+        saved.setCorrectiveResponsibleUserId(input.getCorrectiveResponsibleUserId());
+        saved.setCorrectiveDueDate(input.getCorrectiveDueDate());
+        saved.setVerificationResponsibleUserId(input.getVerificationResponsibleUserId());
+        saved.setVerificationDate(input.getVerificationDate());
+        saved.setReviewPurpose(detail.getMonitorPurpose());
+        saved.setReviewUser(userMapper.selectById(detail.getEvaluatorUserId()).getName());
+        saved.setReviewTime(LocalDateTime.now().toLocalDate().toString());
+        if (!StringUtils.hasText(saved.getImplementResult())) {
+            saved.setImplementResult(input.getEvaluationConclusion());
+        }
+        saved.setIsFinish(0);
+        if (existing == null) evaluateMapper.insert(saved);
+        else evaluateMapper.updateById(saved);
+        resultMapper.delete(Wrappers.<QualityMonitorEvaluateResult>lambdaQuery()
+                .eq(QualityMonitorEvaluateResult::getDetailsEvaluateId, saved.getDetailsEvaluateId()));
+        int index = 0;
+        for (QualityMonitorEvaluateResult row : input.getResultRows()) {
+            Map<String, String> rawRow = rawRows.get(index);
+            if (!Objects.equals(row.getMeasuredValue(), rawRow.get("result")))
+                throw new ErrorException("妫�娴嬫暟鎹笌宸插鏍稿師濮嬭褰曚笉涓�鑷达紝璇峰埛鏂板悗閲嶆柊鏍稿");
+            if (!StringUtils.hasText(row.getReferenceValue())) throw new ErrorException("璇峰~鍐欑洸鏍峰��");
+            row.setId(null);
+            row.setDetailsEvaluateId(saved.getDetailsEvaluateId());
+            row.setInspectorUserId(detail.getInspectorUserId());
+            row.setMeasuredValue(rawRow.get("result"));
+            row.setUnit(rawRow.get("unit"));
+            row.setSortNo(index++);
+            resultMapper.insert(row);
+        }
+        return get(detail.getQualityMonitorDetailsId());
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void approve(Integer detailId, boolean approved, String opinion) {
+        QualityMonitorDetails detail = requireDetail(detailId);
+        QualityMonitor plan = monitorMapper.selectById(detail.getQualityMonitorId());
+        int current = SecurityUtils.getUserId().intValue();
+        if (plan == null || !Integer.valueOf(current).equals(plan.getRatifyUserId())
+                || Integer.valueOf(current).equals(detail.getInspectorUserId()))
+            throw new ErrorException("鍙湁鎸囧畾鎵瑰噯浜哄彲浠ュ鎵硅瘎浠�");
+        QualityMonitorDetailsEvaluate evaluation = evaluateMapper.selectOne(Wrappers.<QualityMonitorDetailsEvaluate>lambdaQuery()
+                .eq(QualityMonitorDetailsEvaluate::getQualityMonitorDetailsId, detailId).last("FOR UPDATE"));
+        if (evaluation == null || Integer.valueOf(1).equals(evaluation.getIsFinish()))
+            throw new ErrorException("璇勪环涓嶅瓨鍦ㄦ垨宸叉壒鍑�");
+        if (approved && StringUtils.hasText(evaluation.getCorrectiveAction())
+                && (!StringUtils.hasText(evaluation.getVerificationResult())
+                || evaluation.getCorrectiveResponsibleUserId() == null
+                || evaluation.getCorrectiveDueDate() == null
+                || evaluation.getVerificationResponsibleUserId() == null
+                || evaluation.getVerificationDate() == null))
+            throw new ErrorException("璇疯ˉ榻愮籂姝f帾鏂借矗浠讳汉銆佸畬鎴愭棩鏈熷拰楠岃瘉淇℃伅鍚庡啀鎵瑰噯");
+        evaluation.setRatifyOpinion(opinion);
+        evaluation.setRatifyTime(LocalDateTime.now());
+        evaluation.setIsFinish(approved ? 1 : 0);
+        evaluateMapper.updateById(evaluation);
+    }
+
+    private QualityMonitorDetails requireDetail(Integer detailId) {
+        QualityMonitorDetails detail = detailId == null ? null : detailsMapper.selectById(detailId);
+        if (detail == null || !"INSPECTION".equals(detail.getItemKind()))
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�娴嬫槑缁嗕笉瀛樺湪");
+        return detail;
+    }
+
+    private void fillRawContext(QualityMonitorDetailsEvaluate evaluation, QualityMonitorRawContext context) {
+        evaluation.setRawInspectionDates(context.getDetectionDates().stream()
+                .map(date -> date.format(DateTimeFormatter.ISO_LOCAL_DATE)).collect(Collectors.toList()));
+        List<String> names = new ArrayList<>();
+        List<String> specs = new ArrayList<>();
+        List<String> codes = new ArrayList<>();
+        for (Map<String, String> device : context.getDevices()) {
+            names.add(deviceText(device, "device_name", "deviceName"));
+            specs.add(deviceText(device, "specification_model", "specificationModel"));
+            String managementNumber = deviceText(device, "management_number", "managementNumber");
+            codes.add("-".equals(managementNumber)
+                    ? deviceText(device, "factory_no", "factoryNo") : managementNumber);
+        }
+        evaluation.setRawEquipmentDescription(String.join("\n", names));
+        evaluation.setRawEquipmentSpec(String.join("\n", specs));
+        evaluation.setRawEquipmentCode(String.join("\n", codes));
+    }
+
+    private String deviceText(Map<String, String> device, String snakeCase, String camelCase) {
+        String value = device.get(snakeCase);
+        if (!StringUtils.hasText(value)) value = device.get(camelCase);
+        return StringUtils.hasText(value) ? value : "-";
+    }
+}
diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorService.java b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorService.java
index 29662c5..55182c4 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorService.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorService.java
@@ -19,6 +19,14 @@
  */
 public interface QualityMonitorService extends IService<QualityMonitor> {
 
+    QualityMonitorDto createAnnualPlan(QualityMonitor qualityMonitor);
+
+    QualityMonitorDto getAnnualPlan(String monitorYear, Integer qualityMonitorId);
+
+    boolean submitAnnualPlan(Integer qualityMonitorId);
+
+    void validateDetailEditable(Integer qualityMonitorId);
+
     /**
      * 瀵煎叆鐩戞帶璁″垝
      * @param file
diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorTaskService.java b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorTaskService.java
new file mode 100644
index 0000000..2a04d28
--- /dev/null
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/QualityMonitorTaskService.java
@@ -0,0 +1,222 @@
+package com.ruoyi.process.service;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.basic.pojo.StandardMethod;
+import com.ruoyi.basic.pojo.StandardTemplate;
+import com.ruoyi.basic.service.StandardMethodService;
+import com.ruoyi.basic.service.StandardTemplateService;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.core.domain.entity.InformationNotification;
+import com.ruoyi.system.service.InformationNotificationService;
+import com.ruoyi.framework.exception.ErrorException;
+import com.ruoyi.inspect.mapper.InsOrderMapper;
+import com.ruoyi.inspect.mapper.InsOrderStandardTemplateMapper;
+import com.ruoyi.inspect.mapper.InsSampleMapper;
+import com.ruoyi.inspect.mapper.InsOrderStateMapper;
+import com.ruoyi.inspect.pojo.InsOrder;
+import com.ruoyi.inspect.pojo.InsOrderStandardTemplate;
+import com.ruoyi.inspect.pojo.InsSample;
+import com.ruoyi.inspect.pojo.InsOrderState;
+import com.ruoyi.inspect.service.InsOrderService;
+import com.ruoyi.process.mapper.QualityMonitorDetailsMapper;
+import com.ruoyi.process.mapper.QualityMonitorMapper;
+import com.ruoyi.process.mapper.QualityMonitorTaskCodeMapper;
+import com.ruoyi.process.pojo.QualityMonitor;
+import com.ruoyi.process.pojo.QualityMonitorDetails;
+import com.ruoyi.system.mapper.UserMapper;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.YearMonth;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+@Service
+public class QualityMonitorTaskService {
+    public static final String SOURCE = "QUALITY_MONITOR";
+    private static final String LABORATORY = "鏂拌仛";
+
+    @Resource private QualityMonitorDetailsMapper detailsMapper;
+    @Resource private QualityMonitorMapper monitorMapper;
+    @Resource private QualityMonitorTaskCodeMapper codeMapper;
+    @Resource private InsOrderMapper orderMapper;
+    @Resource private InsSampleMapper sampleMapper;
+    @Resource private InsOrderStateMapper stateMapper;
+    @Resource private InsOrderStandardTemplateMapper snapshotMapper;
+    @Resource private InsOrderService orderService;
+    @Resource private StandardMethodService standardMethodService;
+    @Resource private StandardTemplateService standardTemplateService;
+    @Resource private UserMapper userMapper;
+    @Resource private InformationNotificationService notificationService;
+
+    @Transactional(rollbackFor = Exception.class)
+    public InsOrder dispatch(Integer detailsId) {
+        if (detailsId == null) throw new ErrorException("缂哄皯璐ㄩ噺鐩戞帶璁″垝鏄庣粏ID");
+        QualityMonitorDetails detail = detailsMapper.selectOne(Wrappers.<QualityMonitorDetails>lambdaQuery()
+                .eq(QualityMonitorDetails::getQualityMonitorDetailsId, detailsId).last("FOR UPDATE"));
+        if (detail == null) throw new ErrorException("璐ㄩ噺鐩戞帶璁″垝鏄庣粏涓嶅瓨鍦�");
+        InsOrder existing = getTask(detailsId);
+        if (existing != null) return existing;
+        QualityMonitor plan = monitorMapper.selectById(detail.getQualityMonitorId());
+        if (plan == null || !Integer.valueOf(1).equals(plan.getExamineStatus())
+                || !Integer.valueOf(1).equals(plan.getRatifyStatus())) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶璁″垝瀹℃牳銆佹壒鍑嗛�氳繃鍚庢墠鑳戒笅鍙�");
+        }
+        if (!"INSPECTION".equals(detail.getItemKind())) throw new ErrorException("浠呮娴嬬被鏄庣粏鍙笅鍙戞楠屼换鍔�");
+        if (!StringUtils.hasText(detail.getTechnicalStandardText())
+                || !StringUtils.hasText(detail.getMonitorWay())
+                || !StringUtils.hasText(detail.getCompletionMonth())
+                || detail.getResponsibleUserId() == null) {
+            throw new ErrorException("璇疯ˉ榻愯川鎺ч」鐩�佹妧鏈爣鍑嗐�佽川鎺ф柟寮忋�佸畬鎴愭椂闄愬拰璐熻矗浜�");
+        }
+        List<Integer> templateIds = resolveTemplateIds(detail);
+        if (detail.getStandardMethodId() == null || templateIds.isEmpty()
+                || detail.getInspectorUserId() == null
+                || detail.getEvaluatorUserId() == null || detail.getDueDate() == null
+                || !StringUtils.hasText(detail.getSampleName())) {
+            throw new ErrorException("璇疯ˉ榻愭爣鍑嗐�佸師濮嬭褰曟ā鏉裤�佹娴嬪憳銆佽瘎浠蜂汉銆佹牱鍝佸拰瀹屾垚鏈熼檺");
+        }
+        if (detail.getInspectorUserId().equals(detail.getEvaluatorUserId())) {
+            throw new ErrorException("妫�娴嬪憳涓嶈兘鍚屾椂鎷呬换璇勪环浜�");
+        }
+        if (userMapper.selectById(detail.getInspectorUserId()) == null
+                || userMapper.selectById(detail.getEvaluatorUserId()) == null) {
+            throw new ErrorException("妫�娴嬪憳鎴栬瘎浠蜂汉涓嶅瓨鍦�");
+        }
+        StandardMethod standard = standardMethodService.getById(detail.getStandardMethodId());
+        if (standard == null) throw new ErrorException("妫�娴嬫爣鍑嗕笉瀛樺湪");
+        List<StandardTemplate> templates = new ArrayList<>();
+        for (Integer templateId : templateIds) {
+            StandardTemplate template = standardTemplateService.getById(templateId);
+            if (template == null || !StringUtils.hasText(template.getThing())) {
+                throw new ErrorException("鍘熷璁板綍妯℃澘涓嶅瓨鍦ㄦ垨鍐呭涓虹┖锛�" + templateId);
+            }
+            if (codeMapper.countStandardTemplateBinding(standard.getId(), template.getId()) == 0) {
+                throw new ErrorException("鍘熷璁板綍妯℃澘鏈粦瀹氬綋鍓嶆娴嬫爣鍑嗭細" + template.getName());
+            }
+            templates.add(template);
+        }
+        LocalDate today = LocalDate.now(ZoneId.of("Asia/Shanghai"));
+        YearMonth completionMonth;
+        try {
+            completionMonth = YearMonth.parse(detail.getCompletionMonth());
+        } catch (Exception exception) {
+            throw new ErrorException("璁″垝瀹屾垚鏃堕檺鏍煎紡涓嶆纭�");
+        }
+        if (YearMonth.from(detail.getDueDate()).isAfter(completionMonth)) {
+            throw new ErrorException("妫�楠屼换鍔℃埅姝㈡棩涓嶈兘鏅氫簬璁″垝瀹屾垚鏈堜唤");
+        }
+        int year = today.getYear();
+        codeMapper.ensureYear(year);
+        int sequence = codeMapper.lockCurrent(year) + 1;
+        codeMapper.updateCurrent(year, sequence);
+        String code = String.format("QC-%d-%04d", year, sequence);
+
+        InsOrder order = new InsOrder();
+        order.setEntrustCode(code);
+        order.setTaskSource(SOURCE);
+        order.setQualityMonitorDetailsId(detailsId);
+        order.setOrderType(SOURCE);
+        order.setTypeSource(-1);
+        order.setState(1);
+        order.setInsState(0);
+        order.setLaboratory(LABORATORY);
+        order.setSample(detail.getSampleName());
+        order.setPartDetail(detail.getSampleSpec());
+        order.setAppointed(detail.getDueDate());
+        order.setExamineTime(LocalDateTime.now());
+        orderMapper.insert(order);
+
+        InsSample sample = new InsSample();
+        sample.setInsOrderId(order.getId());
+        sample.setSampleCode(code);
+        sample.setLaboratory(LABORATORY);
+        sample.setSample(detail.getSampleName());
+        sample.setQuantity(1);
+        sampleMapper.insert(sample);
+
+        for (int index = 0; index < templates.size(); index++) {
+            StandardTemplate template = templates.get(index);
+            InsOrderStandardTemplate snapshot = new InsOrderStandardTemplate();
+            snapshot.setInsOrderId(order.getId());
+            snapshot.setTemplateId(template.getId());
+            snapshot.setName(template.getName());
+            snapshot.setNumber(template.getNumber());
+            snapshot.setRemark(template.getRemark());
+            snapshot.setThing(template.getThing());
+            snapshot.setSort(index);
+            snapshotMapper.insert(snapshot);
+        }
+
+        orderService.upInsOrder(order.getId(), sample.getId(), detail.getDueDate().toString(),
+                detail.getInspectorUserId(), LABORATORY);
+        InformationNotification notice = new InformationNotification();
+        notice.setCreateUser(userMapper.selectById(SecurityUtils.getUserId().intValue()).getName());
+        notice.setSenderId(SecurityUtils.getUserId().intValue());
+        notice.setConsigneeId(detail.getInspectorUserId());
+        notice.setMessageType("2");
+        notice.setTheme("璐ㄩ噺鐩戞帶妫�楠屼换鍔�");
+        notice.setContent("璐ㄩ噺鐩戞帶浠诲姟 " + code + " 寰呮娴嬶紝璇锋煡鐪嬭瘎浠疯姹傚拰鍘熷璁板綍妯℃澘");
+        notice.setJumpPath("InspectionTask");
+        notificationService.addInformationNotification(notice);
+        return orderMapper.selectById(order.getId());
+    }
+
+    private List<Integer> resolveTemplateIds(QualityMonitorDetails detail) {
+        String rawIds = detail.getTemplateIds();
+        if (!StringUtils.hasText(rawIds) && detail.getTemplateId() != null) {
+            rawIds = String.valueOf(detail.getTemplateId());
+        }
+        Set<Integer> result = new LinkedHashSet<>();
+        if (!StringUtils.hasText(rawIds)) return new ArrayList<>();
+        try {
+            for (String rawId : rawIds.split(",")) {
+                if (!StringUtils.hasText(rawId)) continue;
+                int templateId = Integer.parseInt(rawId.trim());
+                if (templateId <= 0) throw new NumberFormatException();
+                result.add(templateId);
+            }
+        } catch (NumberFormatException exception) {
+            throw new ErrorException("鍘熷璁板綍妯℃澘鍙傛暟涓嶆纭�");
+        }
+        if (result.size() > 50) throw new ErrorException("鍘熷璁板綍妯℃澘鏈�澶氶�夋嫨50涓�");
+        return new ArrayList<>(result);
+    }
+
+    public InsOrder getTask(Integer detailsId) {
+        if (detailsId == null) return null;
+        return orderMapper.selectOne(Wrappers.<InsOrder>lambdaQuery()
+                .eq(InsOrder::getQualityMonitorDetailsId, detailsId).last("limit 1"));
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void cancel(Integer detailsId) {
+        InsOrder task = getTask(detailsId);
+        if (task == null || !SOURCE.equals(task.getTaskSource()) || !Integer.valueOf(1).equals(task.getState())) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶浠诲姟涓嶅瓨鍦ㄦ垨宸插彇娑�");
+        }
+        QualityMonitorDetails detail = detailsMapper.selectById(detailsId);
+        QualityMonitor plan = detail == null ? null : monitorMapper.selectById(detail.getQualityMonitorId());
+        int current = SecurityUtils.getUserId().intValue();
+        if (plan == null || !(Integer.valueOf(current).equals(plan.getRatifyUserId())
+                || Integer.valueOf(current).equals(task.getCreateUser()))) {
+            throw new ErrorException("鍙湁涓嬪彂浜烘垨璁″垝鎵瑰噯浜哄彲浠ュ彇娑堜换鍔�");
+        }
+        Long startedStates = stateMapper.selectCount(Wrappers.<InsOrderState>lambdaQuery()
+                .eq(InsOrderState::getInsOrderId, task.getId()).ne(InsOrderState::getInsState, 0));
+        if (startedStates > 0) throw new ErrorException("浠诲姟宸插紑濮嬶紝涓嶈兘鍙栨秷");
+        Long filledSnapshots = snapshotMapper.selectCount(Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                .eq(InsOrderStandardTemplate::getInsOrderId, task.getId())
+                .isNotNull(InsOrderStandardTemplate::getRecordValues));
+        if (filledSnapshots > 0) throw new ErrorException("鍘熷璁板綍宸插~鍐欙紝涓嶈兘鍙栨秷浠诲姟");
+        task.setState(5);
+        orderMapper.updateById(task);
+    }
+}
diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java b/cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java
index 876d601..c8a5363 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java
@@ -17,10 +17,12 @@
 import com.ruoyi.common.core.domain.entity.User;
 import com.ruoyi.common.utils.QueryWrappers;
 import com.ruoyi.device.mapper.DeviceMapper;
+import com.ruoyi.device.pojo.Device;
 import com.ruoyi.framework.exception.ErrorException;
 import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
 import com.ruoyi.inspect.util.UserUtils;
 import com.ruoyi.process.dto.ProcessMethodVerifyDto;
+import com.ruoyi.process.dto.ProcessMethodVerifyCalibrationsFileDto;
 import com.ruoyi.process.dto.ProcessMethodVerifyExportWordDto;
 import com.ruoyi.process.mapper.ProcessMethodVerifyCalibrationsFileMapper;
 import com.ruoyi.process.mapper.ProcessMethodVerifyMapper;
@@ -191,6 +193,39 @@
         return files;
     }
 
+    @Override
+    public List<ProcessMethodVerifyCalibrationsFileDto> getCurrentCalibrationFiles(String deviceIds) {
+        List<ProcessMethodVerifyCalibrationsFileDto> files = new ArrayList<>();
+        if (StringUtils.isBlank(deviceIds)) return files;
+
+        Set<Integer> ids = new LinkedHashSet<>();
+        for (String value : StrUtil.split(deviceIds, ",")) {
+            try {
+                ids.add(Integer.valueOf(value.trim()));
+            } catch (NumberFormatException e) {
+                throw new ErrorException("璁惧ID鏃犳晥");
+            }
+        }
+        if (ids.size() > 50) throw new ErrorException("鏈�澶氭煡鐪�50鍙拌澶囩殑鏍″噯璇佷功");
+
+        for (Integer deviceId : ids) {
+            String fileName = processMethodVerifyCalibrationsFileMapper.selectCalibrationsFile(deviceId);
+            if (StringUtils.isBlank(fileName)) continue;
+            Device device = deviceMapper.selectById(deviceId);
+            if (device == null) continue;
+
+            ProcessMethodVerifyCalibrationsFileDto file = new ProcessMethodVerifyCalibrationsFileDto();
+            file.setDeviceId(deviceId);
+            file.setDeviceName(device.getDeviceName());
+            file.setManagementNumber(device.getManagementNumber());
+            file.setFileName(fileName);
+            file.setFileUrl(fileName);
+            file.setType(fileName.matches("(?i).*\\.(png|jpe?g|gif|bmp|webp)$") ? 1 : 2);
+            files.add(file);
+        }
+        return files;
+    }
+
     /**
      * 淇敼鏍囧噯鏂规硶楠岃瘉
      *
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;
     }
diff --git a/cnas-process/src/main/resources/mapper/QualityMonitorDetailsMapper.xml b/cnas-process/src/main/resources/mapper/QualityMonitorDetailsMapper.xml
index aa500be..0bf5862 100644
--- a/cnas-process/src/main/resources/mapper/QualityMonitorDetailsMapper.xml
+++ b/cnas-process/src/main/resources/mapper/QualityMonitorDetailsMapper.xml
@@ -4,12 +4,21 @@
     <select id="pageQualityMonitorDetail" resultType="com.ruoyi.process.dto.QualityMonitorDetailsDto">
         select * from (
         SELECT cqmd.*,
+        ru.name responsible_name,
+        eu.name evaluator_name,
+        qot.id inspection_task_id,
+        qot.entrust_code inspection_task_code,
+        qot.ins_state inspection_task_state,
+        qot.state inspection_task_order_state,
+        qmp.ratify_status plan_ratify_status,
+        qmp.ratify_user_id plan_ratify_user_id,
         CASE
         WHEN cqmder.is_finish = 1 THEN 3
         WHEN cqmder.is_finish = 0 THEN 2
         WHEN cqmder.quality_monitor_details_id IS NOT NULL THEN 1
         ELSE 0 END details_ratify_status,
         CASE
+        WHEN cqmd.item_kind = 'INSPECTION' THEN -1
         WHEN cqmd.ratify_status = 1 THEN 3
         WHEN cqmd.ratify_status = 0 THEN 2
         WHEN cqmd.finish_report_url IS NOT NULL THEN 1
@@ -18,8 +27,13 @@
         WHEN cqmde.is_finish = 1 THEN 3
         WHEN cqmde.ratify_user_id IS NOT NULL THEN 2
         WHEN cqmde.implement_user_id IS NOT NULL and cqmde.details_evaluate_id is not null THEN 1
+        WHEN qot.ins_state = 5 THEN 1
         ELSE 0 END evaluate_status
         FROM cnas_quality_monitor_details cqmd
+        LEFT JOIN user ru ON ru.id = cqmd.responsible_user_id
+        LEFT JOIN user eu ON eu.id = cqmd.evaluator_user_id
+        LEFT JOIN cnas_quality_monitor qmp ON qmp.quality_monitor_id = cqmd.quality_monitor_id
+        LEFT JOIN ins_order qot ON qot.quality_monitor_details_id = cqmd.quality_monitor_details_id
         LEFT JOIN cnas_quality_monitor_details_evaluate cqmde
         ON cqmd.quality_monitor_details_id = cqmde.quality_monitor_details_id
         LEFT JOIN cnas_quality_monitor_details_ratify cqmder
@@ -29,12 +43,6 @@
         <if test="ew.customSqlSegment != null and ew.customSqlSegment != ''">
             ${ew.customSqlSegment}
         </if>
-        order by quality_monitor_id, monitor_purpose, -- 鎻愬彇骞翠唤
-        CAST(SUBSTRING(planned_time, 1, 4) AS SIGNED) ASC,
-        -- 鎻愬彇鏈堜唤
-        CASE
-        WHEN planned_time LIKE '% - %' THEN 1
-        ELSE CAST(SUBSTRING_INDEX(SUBSTRING(planned_time, 6), '鏈�', 1) AS SIGNED)
-        END ASC
+        order by quality_monitor_id, COALESCE(sort_no, 2147483647), quality_monitor_details_id
     </select>
 </mapper>
diff --git a/cnas-process/src/main/resources/mapper/QualityMonitorMapper.xml b/cnas-process/src/main/resources/mapper/QualityMonitorMapper.xml
index c04764c..3b9ccfc 100644
--- a/cnas-process/src/main/resources/mapper/QualityMonitorMapper.xml
+++ b/cnas-process/src/main/resources/mapper/QualityMonitorMapper.xml
@@ -17,4 +17,13 @@
             ${ew.customSqlSegment}
         </if>
     </select>
+
+    <select id="selectPlanDtoById" resultType="com.ruoyi.process.dto.QualityMonitorDto">
+        SELECT cqm.*, u1.name write_name, u2.name examine_name, u3.name ratify_name
+        FROM cnas_quality_monitor cqm
+        LEFT JOIN user u1 ON u1.id = cqm.write_user_id
+        LEFT JOIN user u2 ON u2.id = cqm.examine_user_id
+        LEFT JOIN user u3 ON u3.id = cqm.ratify_user_id
+        WHERE cqm.quality_monitor_id = #{id}
+    </select>
 </mapper>
diff --git a/cnas-process/src/main/resources/mapper/QualityMonitorTaskCodeMapper.xml b/cnas-process/src/main/resources/mapper/QualityMonitorTaskCodeMapper.xml
new file mode 100644
index 0000000..80f9669
--- /dev/null
+++ b/cnas-process/src/main/resources/mapper/QualityMonitorTaskCodeMapper.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="com.ruoyi.process.mapper.QualityMonitorTaskCodeMapper">
+    <insert id="ensureYear">
+        INSERT INTO cnas_quality_monitor_task_code_seq(task_year, current_value)
+        VALUES(#{year}, 0)
+        ON DUPLICATE KEY UPDATE task_year = task_year
+    </insert>
+
+    <select id="lockCurrent" resultType="java.lang.Integer">
+        SELECT current_value
+        FROM cnas_quality_monitor_task_code_seq
+        WHERE task_year = #{year}
+        FOR UPDATE
+    </select>
+
+    <update id="updateCurrent">
+        UPDATE cnas_quality_monitor_task_code_seq
+        SET current_value = #{value}
+        WHERE task_year = #{year}
+    </update>
+
+    <select id="countStandardTemplateBinding" resultType="java.lang.Integer">
+        SELECT COUNT(*)
+        FROM standard_tree st
+        JOIN standard_tree_template_binding b ON b.standard_tree_id = st.id
+        WHERE st.standard_method_id = #{standardId}
+          AND b.template_id = #{templateId}
+    </select>
+</mapper>
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsOrderPlanDTO.java b/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsOrderPlanDTO.java
index 1dd2780..a54388b 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsOrderPlanDTO.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/dto/InsOrderPlanDTO.java
@@ -27,6 +27,9 @@
     @Schema(description = "濮旀墭缂栧彿")
     private String entrustCode;
 
+    @Schema(description = "浠诲姟鏉ユ簮锛欵NTRUSTED鎴朡UALITY_MONITOR")
+    private String taskSource;
+
     @Schema(description = "妫�楠岀被鍨�")
     private Integer typeSource;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderMapper.java b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderMapper.java
index d1662ae..88472a3 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderMapper.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/mapper/InsOrderMapper.java
@@ -29,6 +29,11 @@
 @Mapper
 public interface InsOrderMapper extends BaseMapper<InsOrder> {
 
+
+    Integer selectQualityMonitorEvaluatorId(@Param("orderId") Integer orderId);
+
+    Integer selectQualityMonitorInspectorId(@Param("orderId") Integer orderId);
+
     //鑾峰彇妫�楠屼笅鍗曟暟鎹�
     IPage<SampleOrderDto> selectInsOrderPage(@Param("page") IPage<InsOrder> page, @Param("ew") QueryWrapper<SampleOrderDto> ew, @Param("laboratory") String laboratory, @Param("isOrderAll") String isOrderAll, @Param("receiptSubmitted") String receiptSubmitted);
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
index 9558674..56cf817 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrder.java
@@ -36,6 +36,12 @@
     @Schema(description = "妫�楠岀被鍒�")
     private String orderType;
 
+    @Schema(description = "浠诲姟鏉ユ簮锛欵NTRUSTED濮旀墭锛孮UALITY_MONITOR璐ㄩ噺鐩戞帶")
+    private String taskSource;
+
+    @Schema(description = "璐ㄩ噺鐩戞帶璁″垝鏄庣粏ID")
+    private Integer qualityMonitorDetailsId;
+
     @Schema(description = "涓嬪崟瀹㈡埛")
     private String custom;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderDeviceRecord.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderDeviceRecord.java
index a3e7571..ec3e32d 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderDeviceRecord.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderDeviceRecord.java
@@ -16,8 +16,10 @@
  */
 @Data
 @TableName("device_record")
+@Schema(description = "妫�楠屼换鍔¤澶囦娇鐢ㄨ褰�")
 public class InsOrderDeviceRecord {
 
+    @Schema(description = "璁惧浣跨敤璁板綍ID")
     @TableId(value = "id", type = IdType.AUTO)
     private Integer id;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
index f2c2ec1..395a0cc 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderStandardTemplate.java
@@ -17,8 +17,10 @@
  */
 @Data
 @TableName(value = "ins_order_standard_template")
+@Schema(description = "妫�楠屼换鍔″師濮嬭褰曟ā鏉垮揩鐓�")
 public class InsOrderStandardTemplate {
 
+    @Schema(description = "妯℃澘蹇収ID")
     @TableId(type = IdType.AUTO)
     private Integer id;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java
index 22624de..13d80ad 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/pojo/InsOrderTemplateDevice.java
@@ -9,8 +9,10 @@
 /** 璁㈠崟妯℃澘涓庢楠岃澶囧叧绯汇�� */
 @Data
 @TableName("ins_order_template_device")
+@Schema(description = "鍘熷璁板綍妯℃澘閫夌敤璁惧鍏崇郴")
 public class InsOrderTemplateDevice {
 
+    @Schema(description = "妯℃澘璁惧鍏崇郴ID")
     @TableId(type = IdType.AUTO)
     private Integer id;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
index 5c438a0..b795317 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/InsOrderPlanService.java
@@ -13,6 +13,7 @@
 import com.ruoyi.inspect.pojo.InsProduct;
 import com.ruoyi.inspect.vo.InsOrderPlanTaskSwitchVo;
 import com.ruoyi.inspect.vo.InsOrderPlanVO;
+import com.ruoyi.inspect.vo.QualityMonitorRawContext;
 import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.multipart.MultipartFile;
 
@@ -37,6 +38,12 @@
 
     void saveOrderDevices(SaveOrderDevicesDto saveOrderDevicesDto);
 
+    /** 浠庡凡澶嶆牳鐨勮川鎺т换鍔″師濮嬭褰曡鍙栧鍑烘娴嬫暟鎹�� */
+    List<Map<String, String>> getQualityMonitorRawResults(Integer orderId);
+
+    /** 浠庡凡澶嶆牳鐨勫師濮嬭褰曡幏鍙栨瘡涓ā鏉跨殑妫�娴嬫棩鏈熷強鎵�閫夎澶囥�� */
+    QualityMonitorRawContext getQualityMonitorRawContext(Integer orderId);
+
     Map<String, Object> doInsOrder(Integer id, String laboratory);
 
     int upPlanUser(Integer userId, Integer orderId,String sonLaboratory);
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
index 50d6ad8..9e811fd 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java
@@ -49,6 +49,7 @@
 import com.ruoyi.inspect.util.ReportSignaturePictureUtils;
 import com.ruoyi.inspect.vo.InsOrderPlanTaskSwitchVo;
 import com.ruoyi.inspect.vo.InsOrderPlanVO;
+import com.ruoyi.inspect.vo.QualityMonitorRawContext;
 import com.ruoyi.inspect.vo.InsSampleUserVO;
 import com.ruoyi.performance.mapper.AuxiliaryOutputWorkingHoursMapper;
 import com.ruoyi.performance.pojo.AuxiliaryOutputWorkingHours;
@@ -223,6 +224,10 @@
     public boolean claimInsOrderPlan(InsOrderPlanDTO entity) {
         if (Objects.isNull(entity)) {
             return false;
+        }
+        InsOrder claimOrder = insOrderMapper.selectById(entity.getInsSampleId());
+        if (claimOrder != null && "QUALITY_MONITOR".equals(claimOrder.getTaskSource())) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶浠诲姟鐢辫鍒掓寚瀹氭娴嬪憳锛屼笉鑳借棰�");
         }
         Integer userId = SecurityUtils.getUserId().intValue();
         InsSampleUser insSampleUser = new InsSampleUser(entity.getInsSampleId().intValue(), userId, 0, entity.getSonLaboratory());
@@ -612,6 +617,16 @@
     public void saveTemplateContext(SaveTemplateContextDto dto) {
         if (dto.getOrderId() == null || dto.getSampleId() == null || dto.getTemplateId() == null) {
             throw new ErrorException("璁㈠崟銆佹牱鍝佸拰妯℃澘涓嶈兘涓虹┖");
+        }
+        InsOrder sourceOrder = insOrderMapper.selectById(dto.getOrderId());
+        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())
+                && !Objects.equals(sourceOrder.getState(), 1)) {
+            throw new ErrorException("宸插彇娑堢殑璐ㄩ噺鐩戞帶浠诲姟涓嶈兘濉啓鍘熷璁板綍");
+        }
+        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())
+                && !Objects.equals(SecurityUtils.getUserId().intValue(),
+                    insOrderMapper.selectQualityMonitorInspectorId(dto.getOrderId()))) {
+            throw new ErrorException("鍙湁璁″垝鎸囧畾鐨勬娴嬪憳鍙互濉啓璐ㄩ噺鐩戞帶鍘熷璁板綍");
         }
         InsSample sample = insSampleMapper.selectById(dto.getSampleId());
         if (sample == null || !Objects.equals(sample.getInsOrderId(), dto.getOrderId())) {
@@ -1027,6 +1042,10 @@
     //浜ゆ帴
     @Override
     public int upPlanUser(Integer userId, Integer orderId, String sonLaboratory) {
+        InsOrder handoffOrder = insOrderMapper.selectById(orderId);
+        if (handoffOrder != null && "QUALITY_MONITOR".equals(handoffOrder.getTaskSource())) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶浠诲姟妫�娴嬪憳鐢辫鍒掓寚瀹氾紝涓嶈兘鐩存帴浜ゆ帴");
+        }
         InsSampleUser insSampleUser = new InsSampleUser();
         insSampleUser.setUserId(userId);
         insSampleUser.setInsSampleId(orderId);
@@ -1211,6 +1230,23 @@
         InsOrder order = insOrderMapper.selectOne(Wrappers.<InsOrder>lambdaQuery()
                 .eq(InsOrder::getId, orderId)
                 .last("FOR UPDATE"));
+        if (order == null) throw new ErrorException("妫�楠屼换鍔′笉瀛樺湪");
+        if ("QUALITY_MONITOR".equals(order.getTaskSource()) && !Objects.equals(order.getState(), 1)) {
+            throw new ErrorException("宸插彇娑堢殑璐ㄩ噺鐩戞帶浠诲姟涓嶈兘鎻愪氦");
+        }
+        if ("QUALITY_MONITOR".equals(order.getTaskSource())
+                && (verifyUser == null || userMapper.selectById(verifyUser) == null)) {
+            throw new ErrorException("璇烽�夋嫨璐ㄩ噺鐩戞帶浠诲姟澶嶆牳浜�");
+        }
+        if ("QUALITY_MONITOR".equals(order.getTaskSource())
+                && Objects.equals(verifyUser, insOrderMapper.selectQualityMonitorInspectorId(orderId))) {
+            throw new ErrorException("妫�娴嬪憳涓嶈兘澶嶆牳鑷繁鐨勮川閲忕洃鎺у師濮嬭褰�");
+        }
+        if ("QUALITY_MONITOR".equals(order.getTaskSource())
+                && !Objects.equals(SecurityUtils.getUserId().intValue(),
+                    insOrderMapper.selectQualityMonitorInspectorId(orderId))) {
+            throw new ErrorException("鍙湁璁″垝鎸囧畾鐨勬娴嬪憳鍙互鎻愪氦璐ㄩ噺鐩戞帶浠诲姟");
+        }
         boolean isInspectionReport = Objects.equals(INSPECTION_REPORT, reportType);
         registerInsResults = isInspectionReport && Boolean.TRUE.equals(registerInsResults);
         // 1. 濮旀墭缂栧彿鍏ㄥ眬鍞竴锛屾彁浜ゅ鏍告椂鍐嶆鏍¢獙锛岄槻姝㈠苟鍙戞垨鍘嗗彶鏁版嵁缁曡繃鐢熸垚鏍¢獙銆�
@@ -1675,11 +1711,25 @@
         if (orderId == null || laboratory == null || type == null) {
             throw new ErrorException("澶嶆牳鍙傛暟涓嶅畬鏁�");
         }
+        InsOrder verifyOrder = insOrderMapper.selectById(orderId);
+        if (verifyOrder == null) throw new ErrorException("妫�楠屼换鍔′笉瀛樺湪");
+        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource()) && !Objects.equals(verifyOrder.getState(), 1)) {
+            throw new ErrorException("宸插彇娑堢殑璐ㄩ噺鐩戞帶浠诲姟涓嶈兘澶嶆牳");
+        }
         ensureDirectTemplateOrderState(orderId);
         InsOrderState currentState = insOrderStateMapper.selectOne(Wrappers.<InsOrderState>lambdaQuery()
                 .eq(InsOrderState::getInsOrderId, orderId)
                 .eq(InsOrderState::getLaboratory, laboratory)
                 .last("limit 1"));
+        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource())
+                && (currentState == null || currentState.getVerifyUser() == null
+                    || !Objects.equals(SecurityUtils.getUserId().intValue(), currentState.getVerifyUser()))) {
+            throw new ErrorException("鍙湁鎻愪氦鏃舵寚瀹氱殑澶嶆牳浜哄彲浠ュ鏍歌川閲忕洃鎺т换鍔�");
+        }
+        if ("QUALITY_MONITOR".equals(verifyOrder.getTaskSource()) && Objects.equals(type, 1)
+                && currentState != null && Objects.equals(currentState.getInsState(), 5)) {
+            return 1;
+        }
         InsReport existingReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
                 .eq(InsReport::getInsOrderId, orderId)
                 .last("limit 1"));
@@ -1738,6 +1788,24 @@
             insOrderMapper.update(null, Wrappers.<InsOrder>lambdaUpdate()
                     .eq(InsOrder::getId, orderId)
                     .set(InsOrder::getInsState, 5));
+
+            // 璐ㄩ噺鐩戞帶浠诲姟鍙繚鐣欏師濮嬭褰曪紝鍚庣画鐢辫川閲忕洃鎺ц瘎浠峰畬鎴愬綊妗c��
+            if ("QUALITY_MONITOR".equals(order.getTaskSource())) {
+                Integer evaluatorId = insOrderMapper.selectQualityMonitorEvaluatorId(orderId);
+                if (evaluatorId != null) {
+                    InformationNotification notice = new InformationNotification();
+                    notice.setCreateUser(insProductMapper.selectUserById(SecurityUtils.getUserId().intValue()).get("name"));
+                    notice.setSenderId(SecurityUtils.getUserId().intValue());
+                    notice.setConsigneeId(evaluatorId);
+                    notice.setMessageType("2");
+                    notice.setTheme("璐ㄩ噺鐩戞帶璇勪环閫氱煡");
+                    notice.setContent("璐ㄩ噺鐩戞帶浠诲姟 " + order.getEntrustCode() + " 鐨勫師濮嬭褰曞凡澶嶆牳锛岃濉啓璇勪环璁板綍");
+                    notice.setViewStatus(false);
+                    notice.setJumpPath(MenuJumpPathConstants.QUALITY_MONITOR);
+                    informationNotificationService.addInformationNotification(notice);
+                }
+                return 1;
+            }
 
             // 妫�楠屼汉鍛樻彁浜ゅ鏍告椂涓嶇敓鎴愭姤鍛婏紱鍏ㄩ儴璇曢獙瀹ゅ鏍搁�氳繃鍚庣敓鎴愭姤鍛娿��
             if (report == null || StringUtils.isBlank(report.getUrl())) {
@@ -2009,6 +2077,10 @@
      * @param orderId
      */
     private void generateReport(Integer orderId, Integer reportType, Integer writeUserId) {
+        InsOrder sourceOrder = insOrderMapper.selectById(orderId);
+        if (sourceOrder != null && "QUALITY_MONITOR".equals(sourceOrder.getTaskSource())) {
+            throw new ErrorException("璐ㄩ噺鐩戞帶妫�楠屼换鍔′笉鐢熸垚鎶ュ憡");
+        }
         /*鏍峰搧涓嬬殑椤圭洰鍙鏈変竴涓」鐩笉鍚堟牸鍒欐楠岀粨鏋滀负0,鍚﹀垯涓�1*/
         //杩欓噷鐨刬nsSamples鏄鍗曚笅鐨勬墍鏈夋牱鍝佸寘鎷�("/")
         List<InsSample> insSamples = insSampleMapper.selectList(Wrappers.<InsSample>lambdaQuery().eq(InsSample::getInsOrderId, orderId));
@@ -3774,6 +3846,40 @@
     /**
      * 鏂拌仛鐩寸粦妯℃澘璁㈠崟锛氭ā鏉夸腑姣忎竴涓�滃鍑哄�尖�濇壒娉ㄩ兘瀵瑰簲鎶ュ憡涓殑涓�鏉℃娴嬮」鐩��
      */
+    @Override
+    public List<Map<String, String>> getQualityMonitorRawResults(Integer orderId) {
+        requireReviewedQualityMonitorOrder(orderId);
+        return buildTemplateDetectionResultRows(orderId);
+    }
+
+    @Override
+    public QualityMonitorRawContext getQualityMonitorRawContext(Integer orderId) {
+        requireReviewedQualityMonitorOrder(orderId);
+        QualityMonitorRawContext context = new QualityMonitorRawContext();
+        List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
+                Wrappers.<InsOrderStandardTemplate>lambdaQuery()
+                        .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
+                        .orderByAsc(InsOrderStandardTemplate::getSort, InsOrderStandardTemplate::getId));
+        context.setDetectionDates(snapshots.stream().map(InsOrderStandardTemplate::getDetectionTime)
+                .filter(Objects::nonNull).distinct().sorted().collect(Collectors.toList()));
+        List<Integer> deviceIds = insOrderTemplateDeviceMapper.selectDeviceIdsByOrderId(orderId);
+        if (CollectionUtils.isNotEmpty(deviceIds)) {
+            context.setDevices(insOrderMapper.selectDeviceListByIds(new LinkedHashSet<>(deviceIds)));
+        }
+        return context;
+    }
+
+    private void requireReviewedQualityMonitorOrder(Integer orderId) {
+        InsOrder order = insOrderMapper.selectById(orderId);
+        if (order == null || !"QUALITY_MONITOR".equals(order.getTaskSource())
+                || !Objects.equals(order.getInsState(), 5)) {
+            throw new ErrorException("璐ㄦ帶浠诲姟鍘熷璁板綍澶嶆牳閫氳繃鍚庢墠鑳借鍙栨娴嬫暟鎹�");
+        }
+        Long pendingCount = insOrderStateMapper.selectCount(Wrappers.<InsOrderState>lambdaQuery()
+                .eq(InsOrderState::getInsOrderId, orderId).ne(InsOrderState::getInsState, 5));
+        if (pendingCount > 0) throw new ErrorException("璐ㄦ帶浠诲姟浠嶆湁鏈鏍哥殑鍘熷璁板綍");
+    }
+
     private List<Map<String, String>> buildTemplateDetectionResultRows(Integer orderId) {
         List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
                 Wrappers.<InsOrderStandardTemplate>lambdaQuery()
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
index 1b50799..8f5e640 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/IfsOrderVO.java
@@ -77,4 +77,5 @@
     @Schema(description = "鍚勫師濮嬭褰曟ā鏉跨殑鐜鏉′欢")
     private List<TemplateConditionVo> templateConditions;
 
+
 }
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanTaskSwitchVo.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanTaskSwitchVo.java
index 975f842..536b451 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanTaskSwitchVo.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanTaskSwitchVo.java
@@ -9,6 +9,8 @@
 
     private String entrustCode;
 
+    private String taskSource;
+
     private String sampleType;
 
     @Schema(description = "绱ф�ョ▼搴�")
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java
index 81b8515..9cdb715 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/InsOrderPlanVO.java
@@ -12,6 +12,19 @@
     @Schema(description = "濮旀墭缂栧彿")
     private String entrustCode;
 
+    private String taskSource;
+    private Integer qualityMonitorDetailsId;
+    private String monitorProject;
+    private String dueDate;
+    private String monitorPurpose;
+    private String monitorWay;
+    private String sampleSpec;
+    private String standardCode;
+    private Integer evaluatorUserId;
+    private Integer planRatifyUserId;
+    private Integer evaluateStatus;
+    private String technicalStandardText;
+
     @Schema(description = "鏍峰搧鍚嶇О")
     private String sample;
 
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/vo/QualityMonitorRawContext.java b/inspect-server/src/main/java/com/ruoyi/inspect/vo/QualityMonitorRawContext.java
new file mode 100644
index 0000000..7ecc129
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/vo/QualityMonitorRawContext.java
@@ -0,0 +1,23 @@
+package com.ruoyi.inspect.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 宸插鏍哥殑璐ㄦ帶鍘熷璁板綍涓敤浜庤瘎浠疯〃鐨勬棩鏈熷拰璁惧銆�
+ */
+@Data
+@Schema(description = "宸插鏍歌川鎺у師濮嬭褰曚腑鐨勬娴嬫棩鏈熶笌鎵�鐢ㄨ澶�")
+public class QualityMonitorRawContext {
+
+    @Schema(description = "鍚勫師濮嬭褰曟ā鏉跨殑妫�娴嬫棩鏈燂紝鍘婚噸骞舵寜鏃ユ湡鍗囧簭鎺掑垪")
+    private List<LocalDate> detectionDates = new ArrayList<>();
+
+    @Schema(description = "浠诲姟瀹為檯閫夌敤鐨勮澶囦俊鎭紝鎸夎澶囬�夋嫨椤哄簭鎺掑垪")
+    private List<Map<String, String>> devices = new ArrayList<>();
+}
diff --git a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
index 3f6eabd..8298947 100644
--- a/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsOrderMapper.xml
@@ -4,6 +4,22 @@
         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.inspect.mapper.InsOrderMapper">
 
+    <select id="selectQualityMonitorEvaluatorId" resultType="java.lang.Integer">
+        SELECT cqmd.evaluator_user_id
+        FROM ins_order io
+        JOIN cnas_quality_monitor_details cqmd
+          ON cqmd.quality_monitor_details_id = io.quality_monitor_details_id
+        WHERE io.id = #{orderId}
+    </select>
+
+    <select id="selectQualityMonitorInspectorId" resultType="java.lang.Integer">
+        SELECT cqmd.inspector_user_id
+        FROM ins_order io
+        JOIN cnas_quality_monitor_details cqmd
+          ON cqmd.quality_monitor_details_id = io.quality_monitor_details_id
+        WHERE io.id = #{orderId}
+    </select>
+
     <resultMap id="selectSampleDefectsMap" type="com.ruoyi.inspect.vo.SampleDefectsFatherVo">
         <id column="id" property="id"/>
         <result column="sample" property="sample"/>
diff --git a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
index ad6224c..60b24f3 100644
--- a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
@@ -205,6 +205,24 @@
         io.appointed,
         io.send_time,
         io.order_type,
+        io.task_source,
+        io.quality_monitor_details_id,
+        cqmd.monitor_project,
+        cqmd.due_date,
+        cqmd.monitor_purpose,
+        cqmd.monitor_way,
+        cqmd.sample_spec,
+        cqmd.evaluator_user_id,
+        cqmd.technical_standard_text,
+        qmp.ratify_user_id plan_ratify_user_id,
+        CASE
+            WHEN cqmde.is_finish = 1 THEN 3
+            WHEN cqmde.ratify_user_id IS NOT NULL THEN 2
+            WHEN cqmde.details_evaluate_id IS NOT NULL THEN 1
+            WHEN io.ins_state = 5 THEN 1
+            ELSE 0
+        END evaluate_status,
+        qsm.code standard_code,
         case when
         io.type_source = 0
         then io.sample_view
@@ -230,6 +248,10 @@
         iiq.material_prop AS material_prop
         FROM
         ins_order io
+        LEFT JOIN cnas_quality_monitor_details cqmd ON cqmd.quality_monitor_details_id = io.quality_monitor_details_id
+        LEFT JOIN cnas_quality_monitor qmp ON qmp.quality_monitor_id = cqmd.quality_monitor_id
+        LEFT JOIN cnas_quality_monitor_details_evaluate cqmde ON cqmde.quality_monitor_details_id = cqmd.quality_monitor_details_id
+        LEFT JOIN standard_method qsm ON qsm.id = cqmd.standard_method_id
         LEFT JOIN ins_sample isa ON isa.ins_order_id = io.id
         LEFT JOIN ins_report ira ON ira.ins_order_id = io.id
         LEFT JOIN ifs_inventory_quantity iiq ON iiq.id = io.ifs_inventory_id
@@ -310,6 +332,7 @@
         SELECT
         io.id,
         io.entrust_code,
+        io.task_source,
         io.type,
         io.appointed,
         io.send_time,

--
Gitblit v1.9.3