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

---
 cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java |  158 ++++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 121 insertions(+), 37 deletions(-)

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 5edf27f..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
@@ -1,6 +1,6 @@
 package com.ruoyi.process.service.impl;
 
-import cn.hutool.core.bean.BeanUtil;
+import  cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONArray;
 import cn.hutool.json.JSONObject;
@@ -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;
@@ -32,9 +34,9 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
-import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
 import org.apache.poi.xwpf.usermodel.XWPFDocument;
 import org.apache.poi.xwpf.usermodel.XWPFParagraph;
+import org.apache.poi.xwpf.usermodel.XWPFRun;
 import org.apache.poi.xwpf.usermodel.XWPFTable;
 import org.apache.poi.xwpf.usermodel.XWPFTableCell;
 
@@ -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;
+    }
+
     /**
      * 淇敼鏍囧噯鏂规硶楠岃瘉
      *
@@ -243,6 +278,13 @@
         processMethodVerifyMethodFileService.remove(Wrappers.<ProcessMethodVerifyMethodFile>lambdaQuery()
                 .eq(ProcessMethodVerifyMethodFile::getMethodVerifyId, methodVerifyId));
 
+        // 鍒犻櫎鎵�鏈変笂宀楄瘉
+        processMethodVerifyWorkFileService.remove(Wrappers.<ProcessMethodVerifyWorkFile>lambdaQuery()
+                .eq(ProcessMethodVerifyWorkFile::getMethodVerifyId, methodVerifyId));
+
+        // 鍒犻櫎鎵�鏈夋牎鍑嗚瘉涔�
+        processMethodVerifyCalibrationsFileService.remove(Wrappers.<ProcessMethodVerifyCalibrationsFile>lambdaQuery()
+                .eq(ProcessMethodVerifyCalibrationsFile::getMethodVerifyId, methodVerifyId));
 
         baseMapper.deleteById(methodVerifyId);
 
@@ -404,44 +446,39 @@
         }
     }
 
-    /** 鏂扮増鏂规硶楠岃瘉涓昏〃瀵煎嚭锛涘師濮嬭褰曞拰楠岃瘉缁撴灉浠嶄綔涓洪檮浠朵繚瀛樸�� */
+    /** 鎸夊鎴锋彁渚涚殑妯悜鏂规硶楠岃瘉琛ㄥ鍑猴紱鍘熷璁板綍鍜岄獙璇佺粨鏋滀粛浣滀负闄勪欢淇濆瓨銆� */
     private void exportWordVerification(ProcessMethodVerify verify, HttpServletResponse response) {
         JSONObject content = JSONUtil.parseObj(verify.getVerificationContent());
-        String[][] checks = {
-                {"personnel", "1. 浜哄憳鑳藉姏"}, {"equipment", "2. 妫�娴嬭澶�"},
-                {"reagents", "3. 璇曞墏瑕佹眰"}, {"conditions", "4. 璇曢獙鏉′欢"},
-                {"sample", "5. 鏍峰搧澶勭疆"}, {"steps", "6. 璇曢獙姝ラ"},
-                {"processing", "7. 鏁版嵁澶勭悊"}, {"qualityControl", "璐ㄩ噺鎺у埗鏂规硶"},
-                {"result", "鏂规硶楠岃瘉缁撴灉"}
-        };
-        try (XWPFDocument document = new XWPFDocument()) {
-            XWPFParagraph title = document.createParagraph();
-            title.setAlignment(ParagraphAlignment.CENTER);
-            title.createRun().setText("鏂规硶楠岃瘉璁板綍");
+        String[] checkKeys = {"personnel", "equipment", "reagents", "conditions", "sample", "steps",
+                "processing", "qualityControl", "result"};
+        InputStream templateStream = getClass().getResourceAsStream("/static/method-verification-customer.docx");
+        if (templateStream == null) throw new IllegalStateException("鏂规硶楠岃瘉瀵煎嚭妯℃澘涓嶅瓨鍦�");
+        try (InputStream inputStream = templateStream; XWPFDocument document = new XWPFDocument(inputStream)) {
+            XWPFTable table = document.getTables().get(0);
+            setTemplateCell(table, 0, 1, verify.getStandardName());
+            setTemplateCell(table, 0, 3, verify.getStandardCode());
+            setTemplateCell(table, 1, 1, verify.getVerifyItem());
 
-            XWPFTable basic = document.createTable(2, 4);
-            setCells(basic, 0, "鏍囧噯鍚嶇О", verify.getStandardName(), "鏍囧噯缂栧彿", verify.getStandardCode());
-            setCells(basic, 1, "楠岃瘉椤圭洰", verify.getVerifyItem(), "", "");
-
-            XWPFTable table = document.createTable(checks.length + 1, 4);
-            setCells(table, 0, "楠岃瘉鍐呭", "鏂规硶瑕佹眰", "瀹為獙瀹よ兘鍔�", "鏄惁婊¤冻");
             JSONArray entries = content.getJSONArray("checks");
-            for (int i = 0; i < checks.length; i++) {
-                JSONObject entry = findCheck(entries, checks[i][0]);
+            for (int i = 0; i < checkKeys.length; i++) {
+                JSONObject entry = findCheck(entries, checkKeys[i]);
                 Integer satisfied = entry == null ? null : entry.getInt("satisfied");
-                setCells(table, i + 1, checks[i][1], field(entry, "requirement"),
-                        field(entry, "capability"), satisfied == null ? "" : satisfied == 1 ? "鏄�" : "鍚�");
+                int row = i + 3;
+                if (row <= 8) {
+                    setTemplateCell(table, row, 1, field(entry, "requirement"));
+                    setTemplateCell(table, row, 2, field(entry, "capability"));
+                    setTemplateCell(table, row, 3, satisfiedText(satisfied));
+                } else {
+                    setTemplateCell(table, row, 1, mergedCheckText(entry));
+                    setTemplateCell(table, row, 2, satisfiedText(satisfied));
+                }
             }
+            setTemplateCell(table, 12, 1, field(content, "conclusion"));
+            setTemplateCell(table, 13, 1, decisionText(field(content, "reviewDecision"),
+                    "璇勫浜哄憳", field(content, "reviewer"), field(content, "reviewDate")));
+            setTemplateCell(table, 14, 1, decisionText(field(content, "approvalDecision"),
+                    "鎶�鏈礋璐d汉", field(content, "approver"), field(content, "approvalDate")));
 
-            XWPFTable conclusion = document.createTable(1, 2);
-            setCells(conclusion, 0, "楠岃瘉缁撹", field(content, "conclusion"));
-            XWPFTable decisions = document.createTable(2, 4);
-            setCells(decisions, 0, "璇勫鎰忚", field(content, "reviewDecision"),
-                    "璇勫浜哄憳锛�" + field(content, "reviewer"), field(content, "reviewDate"));
-            setCells(decisions, 1, "瀹℃壒鎰忚", field(content, "approvalDecision"),
-                    "鎶�鏈礋璐d汉锛�" + field(content, "approver"), field(content, "approvalDate"));
-
-            document.createParagraph().createRun().setText("闄勪欢1 鍘熷璁板綍锛涢檮浠�2 楠岃瘉缁撴灉锛堥檮浠跺湪绯荤粺涓煡鐪嬶級");
             response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
             String fileName = URLEncoder.encode("鏂规硶楠岃瘉璁板綍", "UTF-8");
             response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx");
@@ -464,10 +501,42 @@
         return object == null ? "" : StrUtil.nullToEmpty(object.getStr(key));
     }
 
-    private void setCells(XWPFTable table, int row, String... values) {
-        for (int i = 0; i < values.length; i++) {
-            XWPFTableCell cell = table.getRow(row).getCell(i);
-            cell.setText(StrUtil.nullToEmpty(values[i]));
+    private String satisfiedText(Integer satisfied) {
+        return satisfied == null ? "" : satisfied == 1 ? "鏄�" : "鍚�";
+    }
+
+    private String mergedCheckText(JSONObject entry) {
+        String requirement = field(entry, "requirement").trim();
+        String capability = field(entry, "capability").trim();
+        if (requirement.isEmpty()) return capability;
+        if (capability.isEmpty() || requirement.equals(capability)) return requirement;
+        return "鏂规硶瑕佹眰锛�" + requirement + "\n瀹為獙瀹よ兘鍔涳細" + capability;
+    }
+
+    private String decisionText(String decision, String personLabel, String person, String date) {
+        StringBuilder value = new StringBuilder();
+        for (String choice : new String[]{"鍚屾剰浣跨敤", "涓嶈兘浣跨敤", "闄愬埗浣跨敤"}) {
+            if (value.length() > 0) value.append("  ");
+            value.append(choice.equals(decision) ? "鈽�" : "鈻�").append(choice);
+        }
+        value.append("\n").append(personLabel).append("锛�").append(person);
+        if (StringUtils.isNotBlank(date)) {
+            String[] parts = date.split("-");
+            value.append("    鏃ユ湡锛�");
+            value.append(parts.length == 3 ? parts[0] + "骞�" + parts[1] + "鏈�" + parts[2] + "鏃�" : date);
+        }
+        return value.toString();
+    }
+
+    private void setTemplateCell(XWPFTable table, int row, int cellIndex, String value) {
+        XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
+        XWPFParagraph paragraph = cell.getParagraphs().get(0);
+        XWPFRun run = paragraph.getRuns().get(0);
+        String[] lines = StrUtil.nullToEmpty(value).replace("\r\n", "\n").split("\n", -1);
+        run.setText(lines[0], 0);
+        for (int i = 1; i < lines.length; i++) {
+            run.addBreak();
+            run.setText(lines[i]);
         }
     }
 
@@ -526,9 +595,24 @@
         }
         // 纭鏃ユ湡
         processMethodVerifyExportWordDto.setConfirmDateStr(processMethodVerify.getConfirmDate() != null ? processMethodVerify.getConfirmDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")): null);
+        // 璇勫鎰忚
+        processMethodVerifyExportWordDto.setReviewResultStr(buildOpinionCheckBox(processMethodVerify.getReviewResult()));
+        processMethodVerifyExportWordDto.setReviewDateStr(processMethodVerify.getReviewDate() != null ? processMethodVerify.getReviewDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")): null);
+        // 瀹℃壒鎰忚
+        processMethodVerifyExportWordDto.setApprovalResultStr(buildOpinionCheckBox(processMethodVerify.getApprovalResult()));
+        processMethodVerifyExportWordDto.setApprovalDateStr(processMethodVerify.getApprovalDate() != null ? processMethodVerify.getApprovalDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")): null);
         return processMethodVerifyExportWordDto;
     }
 
+    /**
+     * 鎷兼帴"鍚屾剰浣跨敤/涓嶈兘浣跨敤/闄愬埗浣跨敤"鍕鹃�夐」灞曠ず涓�
+     */
+    private String buildOpinionCheckBox(String result) {
+        return ("鍚屾剰浣跨敤".equals(result) ? "鈽�" : "鈻�") + "鍚屾剰浣跨敤銆�"
+                + ("涓嶈兘浣跨敤".equals(result) ? "鈽�" : "鈻�") + "涓嶈兘浣跨敤銆�"
+                + ("闄愬埗浣跨敤".equals(result) ? "鈽�" : "鈻�") + "闄愬埗浣跨敤";
+    }
+
 
     /**
      * 娣诲姞涓婂矖璇�

--
Gitblit v1.9.3