From 31ccc9cd47f96cea6e4e13b078187fd3201c4666 Mon Sep 17 00:00:00 2001
From: liding <756868258@qq.com>
Date: 星期五, 04 九月 2026 16:35:24 +0800
Subject: [PATCH] feat(report): 更新检测报告模板并优化报告生成功能 - 优化设备信息展示格式,支持多设备列表显示 - 统一检测报告和检验报告使用新版模板格式

---
 inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java |  203 ++++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 186 insertions(+), 17 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 161afd1..2c9bf30 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
@@ -45,6 +45,7 @@
 import com.ruoyi.inspect.pojo.*;
 import com.ruoyi.inspect.service.*;
 import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
+import com.ruoyi.inspect.util.PreserveReportPicturePlaceholderHandler;
 import com.ruoyi.inspect.vo.InsOrderPlanTaskSwitchVo;
 import com.ruoyi.inspect.vo.InsOrderPlanVO;
 import com.ruoyi.inspect.vo.InsSampleUserVO;
@@ -781,12 +782,6 @@
         InsOrder order = insOrderMapper.selectOne(Wrappers.<InsOrder>lambdaQuery()
                 .eq(InsOrder::getId, orderId)
                 .last("FOR UPDATE"));
-        InsReport existingReport = insReportMapper.selectOne(Wrappers.<InsReport>lambdaQuery()
-                .eq(InsReport::getInsOrderId, orderId));
-        if (existingReport != null && existingReport.getReportType() != null
-                && !Objects.equals(existingReport.getReportType(), reportType)) {
-            throw new ErrorException("鍚屼竴妫�楠岃鍗曞彧鑳戒娇鐢ㄤ竴绉嶆姤鍛婄被鍨�");
-        }
         boolean isInspectionReport = Objects.equals(INSPECTION_REPORT, reportType);
         registerInsResults = isInspectionReport && Boolean.TRUE.equals(registerInsResults);
         // 1. 鍒ゆ柇鏄惁鏈夐噸澶嶇紪鍙�, 鏈夐噸澶嶇紪鍙峰仛鎻愰啋
@@ -823,7 +818,8 @@
                 })
                 .ne(InsProduct::getIsBinding, 1));
         if (Objects.equals(reportType, INSPECTION_REPORT)) {
-            insProducts.addAll(insProductMapper.selectFiberInsProduct(InsSampleIds, laboratory));
+            List<InsProduct> insProducts1 = insProductMapper.selectFiberInsProduct(InsSampleIds, laboratory);
+            insProducts.addAll(insProducts1);
         }
         if (insProducts.size() > 0) {
             String str = "";
@@ -1954,7 +1950,8 @@
             // 淇敼鎹㈣鍜屽悎骞堕棶棰�
             updaeMerge(template.getXWPFDocument(), true);
             String name = insReport.getCode().replace("/", "") + (showResult ? "-J.docx" : "-C.docx");
-            template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl + "/" + name)));
+            Files.createDirectories(Paths.get(wordUrl));
+            template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl, name)));
             insReport.setUrl("/word/" + name);
             insReport.setIsPass(0);
             insReport.setWriteUserId(SecurityUtils.getUserId().intValue());//鎻愪氦浜�
@@ -2495,6 +2492,11 @@
                     }
                 }
             }
+            // 鏂扮増妫�娴嬫姤鍛婄洿鎺ヤ娇鐢ㄥ浐瀹氬洓鍒楃粨鏋滆锛屼笉鍐嶆瀯閫犳棫鐗堝彲鍙樺垪 TableRenderData銆�
+            // 妫�楠屾姤鍛婁粛娌跨敤涓嬫柟鍘熸湁鐨勫ぇ琛ㄦ牸閫昏緫銆�
+            if (!showResult) {
+                return;
+            }
             // 鏀舵牱鏃ユ湡
             List<RowRenderData> rows = new ArrayList<>();
             List<TextRenderData> text = new ArrayList<>();
@@ -2686,7 +2688,9 @@
         for (String s : standardMethod) {
             standardMethod2.append("銆�").append(s);
         }
-        standardMethod2.replace(0, 1, "");
+        if (standardMethod2.length() > 0) {
+            standardMethod2.deleteCharAt(0);
+        }
         tables.forEach(table -> {
             table.put("tableSize", tables.size() + 1);
         });
@@ -2790,10 +2794,13 @@
         // 鎶芥牱鏃ユ湡
         // 鎴愬搧鏄娊鏍锋椂闂�
         LocalDateTime sendTime = insOrder.getSendTime();
-        if (insOrder.getTypeSource() != null && insOrder.getTypeSource().equals(1)) {
+        IfsInventoryQuantity reportInventory = null;
+        if (insOrder.getIfsInventoryId() != null) {
+            reportInventory = ifsInventoryQuantityMapper.selectById(insOrder.getIfsInventoryId());
+        }
+        if (insOrder.getTypeSource() != null && insOrder.getTypeSource().equals(1) && reportInventory != null) {
             // 鍘熸潗鏂欐敹鏍锋棩鏈熸槸鎶ユ鏃ユ湡
-            IfsInventoryQuantity ifsInventoryQuantity = ifsInventoryQuantityMapper.selectById(insOrder.getIfsInventoryId());
-            sendTime = ifsInventoryQuantity.getDeclareDate();
+            sendTime = reportInventory.getDeclareDate();
         }
 
         // 妫�楠屾椂闂�  鎶芥牱鏃堕棿-鎻愪氦鏃堕棿
@@ -2822,12 +2829,60 @@
         List<Map<String, String>> finalDeviceList = deviceList;
         String finalModelStr = modelStr;
 
-        InputStream inputStream = this.getClass().getResourceAsStream(showResult ? "/static/report-template.docx" : "/static/detection-report-template.docx");
+        List<Map<String, String>> detectionResultRows = buildDetectionResultRows(samples);
+        String productName = defaultReportText(insOrder.getSampleView(), insOrder.getSample());
+        String customerName = defaultReportText(custom == null ? null : custom.getCompany(), insOrder.getCompany());
+        String detectionCategory = defaultReportText(finalOrderType, "濮旀墭妫�娴�");
+        String sampleCode = samples.stream()
+                .map(SampleProductDto::getSampleCode)
+                .filter(StringUtils::isNotBlank)
+                .distinct()
+                .collect(Collectors.joining("銆�"));
+        String productAndModel = productName;
+        if (StringUtils.isNotBlank(finalModelStr) && !Objects.equals(productName, finalModelStr)) {
+            productAndModel = productName + " " + finalModelStr;
+        }
+        String standardMethodText = "null".equals(standardMethod2.toString()) ? "" : standardMethod2.toString();
+        String productionDate = "/";
+        String batchNo = "/";
+        if (reportInventory != null) {
+            if (reportInventory.getProductDate() != null) {
+                productionDate = reportInventory.getProductDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
+            }
+            batchNo = defaultReportText(reportInventory.getUpdateBatchNo(), reportInventory.getLotBatchNo());
+        }
+        boolean containsOrganicChlorine = detectionResultRows.stream()
+                .map(row -> row.get("itemName"))
+                .filter(Objects::nonNull)
+                .anyMatch(name -> name.contains("鏈夋満姘�"));
+        String resultRemark = containsOrganicChlorine
+                ? "寰簱浠戞硶锛屾湁鏈烘隘鍚噺灏忎簬0.005%鏃讹紝浠ユ湭妫�鍑烘姤鍑恒��"
+                : defaultReportText(insOrder.getRemark(), "/");
+
+        // 妫�娴嬫姤鍛婂拰妫�楠屾姤鍛婄粺涓�浣跨敤鏂扮増鎶ュ憡妯℃澘
+        String templateName = "/static/detection-report-template-v2.docx";
+        InputStream inputStream = this.getClass().getResourceAsStream(templateName);
+        if (inputStream == null) {
+            throw new RuntimeException("鎵句笉鍒版姤鍛婃ā鏉�: " + templateName);
+        }
         Configure configure = Configure.builder()
                 .bind("deviceList", new HackLoopTableRenderPolicy())
+                .bind("r", new HackLoopTableRenderPolicy(true))
+                .setValidErrorHandler(new PreserveReportPicturePlaceholderHandler())
                 .useSpringEL(true)
                 .build();
 
+        String finalProductAndModel = productAndModel;
+        String finalProductName = productName;
+        String finalCustomerName = customerName;
+        String finalDetectionCategory = detectionCategory;
+        String finalSampleCode = defaultReportText(sampleCode, "/");
+        String finalStandardMethodText = defaultReportText(standardMethodText, "/");
+        String finalProductionDate = productionDate;
+        String finalBatchNo = batchNo;
+        String finalResultRemark = resultRemark;
+        String equipmentText = buildEquipmentText(finalDeviceList);
+        String issueDate = now.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�"));
         XWPFTemplate template = XWPFTemplate.compile(inputStream, configure).render(
                 new HashMap<String, Object>() {{
                     put("order", insOrder);
@@ -2837,7 +2892,7 @@
                     put("sampleSize", insOrder.getTestQuantity());
                     put("tables", tables);
                     put("tableSize", tables.size() + 1);
-                    put("standardMethod", (standardMethod2.toString().equals("null") ? "" : standardMethod2));
+                    put("standardMethod", finalStandardMethodText);
                     put("deviceList", finalDeviceList);
                     put("twoCode", null);
                     put("models", finalModelStr);
@@ -2861,12 +2916,38 @@
                     put("resultCh", finalResultCh);
                     put("resultEn", finalResultEn);
                     put("sampleStatus", sampleStatus);
+                    // 鏂扮増妫�娴嬫姤鍛婃ā鏉匡紙鍙傜収銆婃湁鏈烘隘妫�娴嬫姤鍛娿�嬶級浣跨敤鐨勬墎骞冲瓧娈点��
+                    put("no", insReport.getCode());
+                    put("cover", finalProductAndModel);
+                    put("product", finalProductName);
+                    put("model", defaultReportText(finalModelStr, "/"));
+                    put("customer", finalCustomerName);
+                    put("category", finalDetectionCategory);
+                    put("date", issueDate);
+                    put("maker", defaultReportText(insOrder.getProduction(), "/"));
+                    put("env", finalEnvironment);
+                    put("qty", defaultReportText(insOrder.getTestQuantity(), "/"));
+                    put("receive", finalSendTime.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")));
+                    put("sampleNo", finalSampleCode);
+                    put("appearance", defaultReportText(sampleStatus, "/"));
+                    put("sender", defaultReportText(insOrder.getPrepareUser(), "/"));
+                    put("produced", finalProductionDate);
+                    put("batch", finalBatchNo);
+                    put("phone", defaultReportText(insOrder.getPhone(), "/"));
+                    put("testDate", insTime);
+                    put("entrustNo", defaultReportText(insOrder.getEntrustCode(), "/"));
+                    put("sampling", "/");
+                    put("standard", finalStandardMethodText);
+                    put("equipment", equipmentText);
+                    put("conclusion", "鏍峰搧鎸夊鎵樻柟瑕佹眰杩涜妫�娴嬶紝鏁版嵁瑙佹娴嬬粨鏋溿��");
+                    put("remark", "鏈姤鍛婃娴嬬粨鏋滀粎瀵瑰綋鏃ユ墍鎺ユ敹鏍峰搧璐熻矗銆�");
+                    put("r", detectionResultRows);
+                    put("resultNote", finalResultRemark);
                 }});
         try {
-            // 淇敼鎹㈣鍜屽悎骞堕棶棰�
-            updaeMerge(template.getXWPFDocument(), false);
             String name = insReport.getCode().replace("/", "") + (showResult ? ".docx" : "-C.docx");
-            template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl + "/" + name)));
+            Files.createDirectories(Paths.get(wordUrl));
+            template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl, name)));
             insReport.setUrl("/word/" + name);
             insReport.setWriteUserId(SecurityUtils.getUserId().intValue());//鎻愪氦浜�
             insReport.setWriteTime(LocalDateTime.now());//鎻愪氦鏃堕棿
@@ -2878,6 +2959,89 @@
         } catch (IOException e) {
             throw new RuntimeException(e);
         }
+    }
+
+    /**
+     * 鏂扮増妫�娴嬫姤鍛婄殑缁撴灉琛ㄥ浐瀹氫负鍥涘垪锛氬簭鍙枫�佹娴嬮」鐩�佸崟浣嶃�佹娴嬬粨鏋溿��
+     */
+    private static List<Map<String, String>> buildDetectionResultRows(List<SampleProductDto> samples) {
+        List<Map<String, String>> rows = new ArrayList<>();
+        if (CollectionUtils.isEmpty(samples)) {
+            return rows;
+        }
+
+        boolean multipleSamples = samples.size() > 1;
+        int sequence = 1;
+        for (SampleProductDto sample : samples) {
+            if (CollectionUtils.isEmpty(sample.getInsProduct())) {
+                continue;
+            }
+            for (InsProduct product : sample.getInsProduct()) {
+                // 缁戝畾椤圭洰浠呭弬涓庤绠楋紝涓嶅簲鍑虹幇鍦ㄦ姤鍛婁腑銆�
+                if (product == null || Objects.equals(product.getIsBinding(), 1)) {
+                    continue;
+                }
+
+                String itemName = defaultReportText(
+                        product.getInspectionItemSubclass(),
+                        defaultReportText(product.getInspectionItem(), product.getInspectionItemClass()));
+                if (multipleSamples && StringUtils.isNotBlank(sample.getSampleCode())) {
+                    itemName = sample.getSampleCode() + " - " + itemName;
+                }
+
+                Map<String, String> row = new LinkedHashMap<>();
+                row.put("seq", String.valueOf(sequence++));
+                row.put("itemName", itemName);
+                row.put("unit", defaultReportText(product.getUnit(), "/"));
+                row.put("result", defaultReportText(product.getLastValue(), "/"));
+                rows.add(row);
+            }
+        }
+        return rows;
+    }
+
+    private static String buildEquipmentText(List<Map<String, String>> deviceList) {
+        if (CollectionUtils.isEmpty(deviceList)) {
+            return "/";
+        }
+        Set<String> lines = new LinkedHashSet<>();
+        int sequence = 1;
+        for (Map<String, String> device : deviceList) {
+            String name = getMapText(device, "deviceName", "device_name");
+            String managementNumber = getMapText(device, "managementNumber", "management_number");
+            String line = sequence++ + ". " + defaultReportText(name, "/");
+            if (StringUtils.isNotBlank(managementNumber)) {
+                line += "锛�" + managementNumber;
+            }
+            lines.add(line);
+        }
+        return String.join("\n", lines);
+    }
+
+    private static String getMapText(Map<String, String> map, String... keys) {
+        if (map == null) {
+            return null;
+        }
+        for (String key : keys) {
+            String value = map.get(key);
+            if (StringUtils.isNotBlank(value)) {
+                return value;
+            }
+            for (Map.Entry<String, String> entry : map.entrySet()) {
+                if (entry.getKey() != null && entry.getKey().equalsIgnoreCase(key)
+                        && StringUtils.isNotBlank(entry.getValue())) {
+                    return entry.getValue();
+                }
+            }
+        }
+        return null;
+    }
+
+    private static String defaultReportText(String value, String fallback) {
+        if (StringUtils.isNotBlank(value)) {
+            return value;
+        }
+        return StringUtils.isNotBlank(fallback) ? fallback : "/";
     }
 
     /**
@@ -3246,6 +3410,11 @@
             }
 
         }
+        // 妫�娴嬫姤鍛婁笉灞曠ず鈥滅粨璁衡�濆垪锛岄琛屼篃瑕佸悓姝ュ噺灏戜竴涓崟鍏冩牸锛�
+        // 鍚﹀垯棣栬涓� max + 6 鍒椼�佸叾浣欒涓� max + 5 鍒楋紝poi-tl 鏃犳硶娓叉煋琛ㄦ牸銆�
+        if (!showResult && !text.isEmpty()) {
+            text.remove(text.size() - 1);
+        }
         TextRenderData[] text2 = text.toArray(new TextRenderData[0]);
         rowRenderData = Rows.of(text2).center().rowAtleastHeight(1).create();
         rows.add(rowRenderData);

--
Gitblit v1.9.3