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中关联质控明细表返回任务来源和评价状态

---
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java |  165 +++++++++++++++++++++++++++++++++++++++++++++---------
 1 files changed, 137 insertions(+), 28 deletions(-)

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 7524220..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;
@@ -84,7 +85,6 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.regex.Pattern;
 import java.util.stream.Collectors;
-import java.util.stream.Stream;
 
 /**
  * 妫�楠屼换鍔�-涓氬姟瀹炵幇灞�
@@ -225,6 +225,10 @@
         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());
         return insSampleUserMapper.insert(insSampleUser) > 0;
@@ -275,10 +279,10 @@
         // 鏌ヨ璁㈠崟Id
         InsOrder order = insOrderMapper.selectFirstSubmit(dto.getId());
         if (order == null) {
-            return BeanUtil.isEmpty(insProducts) ? Collections.emptyList() : insProducts;
+            return CollectionUtils.isEmpty(insProducts) ? Collections.emptyList() : insProducts;
         }
         insProducts = mergeDirectTemplateSnapshots(order, dto.getId(), dto.getLaboratory(), insProducts);
-        if (BeanUtil.isEmpty(insProducts)) {
+        if (CollectionUtils.isEmpty(insProducts)) {
             return Collections.emptyList();
         }
         getTemplateThing(order, insProducts, dto.getLaboratory());
@@ -380,6 +384,9 @@
 
     @Override
     public int uploadFile(Integer orderId, MultipartFile file) {
+        if (orderId == null || file == null || file.isEmpty()) {
+            throw new ErrorException("璇烽�夋嫨瑕佷笂浼犵殑闄勪欢");
+        }
         String urlString;
         String pathName;
         String path;
@@ -406,7 +413,7 @@
             urlString = realpath + "/" + pathName;
             file.transferTo(new File(urlString));
             insOrderFile.setFileUrl(pathName);
-            return insOrderFileMapper.insert(insOrderFile);
+            return insOrderFileMapper.insert(insOrderFile) > 0 ? insOrderFile.getId() : 0;
         } catch (Exception e) {
             e.printStackTrace();
             System.err.println("闄勪欢涓婁紶閿欒");
@@ -610,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())) {
@@ -1025,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);
@@ -1209,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. 濮旀墭缂栧彿鍏ㄥ眬鍞竴锛屾彁浜ゅ鏍告椂鍐嶆鏍¢獙锛岄槻姝㈠苟鍙戞垨鍘嗗彶鏁版嵁缁曡繃鐢熸垚鏍¢獙銆�
@@ -1673,19 +1711,34 @@
         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"));
         boolean directTemplateOrder = isDirectTemplateOrder(orderId);
         if (currentState != null && Objects.equals(currentState.getInsState(), 5)
                 && existingReport != null && StringUtils.isNotBlank(existingReport.getUrl())
+                && Objects.equals(existingReport.getIsPass(), 1)
                 && (!directTemplateOrder || StringUtils.isNotBlank(existingReport.getInspectionUrl()))) {
-            // 闃叉澶嶆牳椤甸潰閲嶅鐐瑰嚮鎴栫綉缁滈噸璇曢噸澶嶇敓鎴愭姤鍛娿�侀噸澶嶅彂璧锋姤鍛婂鎵广��
+            // 浠呭浠嶅湪鎶ュ憡缂栧埗涓殑璁板綍闃查噸澶嶏紱閫�鍥炴楠屼换鍔$殑鎶ュ憡闇�瑕佺户缁墽琛屽苟鎭㈠ is_pass銆�
             return 1;
         }
         if (directTemplateOrder && Objects.equals(type, 1)
@@ -1735,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())) {
@@ -1800,11 +1871,11 @@
                     message += "\n鎵规鍙�: " + ifsInventoryQuantity.getUpdateBatchNo();
                 }
                 //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡  鎻愪氦澶嶆牳
-                try {
-                    WxCpUtils.inform(sendUserAccount, message, null);
-                } catch (Exception e) {
-                    throw new RuntimeException(e);
-                }
+//                try {
+//                    WxCpUtils.inform(sendUserAccount, message, null);
+//                } catch (Exception e) {
+//                    throw new RuntimeException(e);
+//                }
             });
 
 
@@ -1964,10 +2035,12 @@
         if (StringUtils.isBlank(tempPdfName)) {
             throw new ErrorException("鎶ュ憡棰勮鏂囦欢鐢熸垚澶辫触");
         }
-        // generateReport 浼氬啓鍏ュ綋鍓嶆椂闂达紱閲嶆柊鐢熸垚涓嶅簲鏀瑰彉鍘熷鎵硅褰曟椂闂淬��
+        // generateReport 浼氬啓鍏ュ綋鍓嶆椂闂达紝閮ㄥ垎鎶ュ憡杩樹細鏆傛椂鍐欏叆 is_pass=0锛�
+        // 閲嶆柊鐢熸垚涓嶅簲鏀瑰彉鍘熷鎵硅褰曟椂闂村拰鍙鐘舵�併��
         insReportMapper.update(null, Wrappers.<InsReport>lambdaUpdate()
                 .eq(InsReport::getId, reportId)
                 .set(InsReport::getWriteTime, report.getWriteTime())
+                .set(InsReport::getIsPass, report.getIsPass())
                 .set(InsReport::getTempUrlPdf, "/word/" + tempPdfName));
     }
 
@@ -2004,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));
@@ -2133,7 +2210,7 @@
                 insProducts = insUnqualifiedRetestProductMapper.getInsProductUnqualifiedRetest6(dto.getId(), dto.getLaboratory(), dto.getRawMaterialTag(), dto.getRetestTag());
                 break;
         }
-        if (BeanUtil.isEmpty(insProducts)) {
+        if (CollectionUtils.isEmpty(insProducts)) {
             return null;
         }
         InsOrder order = insOrderMapper.selectFirstSubmit(dto.getId());
@@ -3535,7 +3612,7 @@
         String insTime = formatReportTestDate(detectionDates);
         String insTimeEn = formatReportTestDateEn(detectionDates, monthNames);
 
-        // 妫�楠岀幆澧冿細浼樺厛姹囨�诲悇妯℃澘濉啓鐨勬俯婀垮害锛屾姤鍛婂睍绀哄叏閮ㄦā鏉跨殑鏈�灏忓�煎埌鏈�澶у�笺��
+        // 妫�楠岀幆澧冿細浼樺厛姹囨�诲悇妯℃澘濉啓鐨勬俯搴︼紝鎶ュ憡灞曠ず鍏ㄩ儴妯℃澘鐨勬渶灏忓�煎埌鏈�澶у�笺��
         String environment = buildReportEnvironment(orderId, insOrder);
         String finalEnvironment = environment;
         LocalDateTime finalSendTime = sendTime;
@@ -3619,6 +3696,7 @@
         String finalResultRemark = resultRemark;
         String equipmentText = buildEquipmentText(finalDeviceList);
         String issueDate = now.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
+        String conclusionDate = now.format(DateTimeFormatter.ofPattern("yyyy 骞� MM 鏈� dd 鏃�"));
         String finalReportModelStr = finalModelStr;
         LocalDateTime finalReportSendTime = finalSendTime;
         XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
@@ -3662,6 +3740,7 @@
                     put("customer", finalCustomerName);
                     put("category", finalDetectionCategory);
                     put("date", issueDate);
+                    put("conclusionDate", conclusionDate);
                     put("maker", defaultReportText(insOrder.getProduction(), "/"));
                     put("env", finalEnvironment);
                     put("qty", defaultReportText(insOrder.getTestQuantity(), "/"));
@@ -3767,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()
@@ -3891,12 +4004,18 @@
         for (Map<String, String> device : deviceList) {
             String name = getMapText(device, "deviceName", "device_name");
             String managementNumber = getMapText(device, "managementNumber", "management_number");
+            String factoryNo = getMapText(device, "factoryNo", "factory_no");
             if (StringUtils.isBlank(name) || "/".equals(name.trim())) {
                 continue;
             }
-            String number = StringUtils.isBlank(managementNumber) || "/".equals(managementNumber.trim())
-                    ? "鍑哄巶缂栧彿" : managementNumber.trim();
-            String item = name.trim() + "锛�" + number;
+            String number = managementNumber;
+            if (StringUtils.isBlank(number) || "/".equals(number.trim())) {
+                number = factoryNo;
+            }
+            if (StringUtils.isBlank(number) || "/".equals(number.trim())) {
+                continue;
+            }
+            String item = name.trim() + "锛�" + number.trim();
             equipmentItems.add(item);
         }
         if (equipmentItems.isEmpty()) {
@@ -3915,22 +4034,12 @@
         List<InsOrderStandardTemplate> snapshots = insOrderStandardTemplateService.list(
                 Wrappers.<InsOrderStandardTemplate>lambdaQuery()
                         .eq(InsOrderStandardTemplate::getInsOrderId, orderId)
-                        .select(InsOrderStandardTemplate::getTemperature,
-                                InsOrderStandardTemplate::getHumidity));
+                        .select(InsOrderStandardTemplate::getTemperature));
         List<String> temperatures = snapshots.stream()
                 .map(InsOrderStandardTemplate::getTemperature)
                 .filter(StringUtils::isNotBlank)
                 .collect(Collectors.toList());
-        List<String> humidities = snapshots.stream()
-                .map(InsOrderStandardTemplate::getHumidity)
-                .filter(StringUtils::isNotBlank)
-                .collect(Collectors.toList());
-
-        String temperature = formatConditionRange(temperatures, order.getTemperature(), "鈩�");
-        String humidity = formatConditionRange(humidities, order.getHumidity(), "%RH");
-        return Stream.of(temperature, humidity)
-                .filter(StringUtils::isNotBlank)
-                .collect(Collectors.joining(" "));
+        return formatConditionRange(temperatures, order.getTemperature(), "鈩�");
     }
 
     private static String formatConditionRange(List<String> values, String fallback, String unit) {

--
Gitblit v1.9.3