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/resources/mapper/InsSampleMapper.xml | 11 +
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java | 7 +
inspect-server/src/main/resources/static/detection-report-template-v2.docx | 0
inspect-server/src/main/java/com/ruoyi/inspect/util/PreserveReportPicturePlaceholderHandler.java | 41 ++++++++
inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsOrderPlanServiceImpl.java | 189 +++++++++++++++++++++++++++++++++++--
5 files changed, 230 insertions(+), 18 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 f41d3d0..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. 鍒ゆ柇鏄惁鏈夐噸澶嶇紪鍙�, 鏈夐噸澶嶇紪鍙峰仛鎻愰啋
@@ -2497,6 +2492,11 @@
}
}
}
+ // 鏂扮増妫�娴嬫姤鍛婄洿鎺ヤ娇鐢ㄥ浐瀹氬洓鍒楃粨鏋滆锛屼笉鍐嶆瀯閫犳棫鐗堝彲鍙樺垪 TableRenderData銆�
+ // 妫�楠屾姤鍛婁粛娌跨敤涓嬫柟鍘熸湁鐨勫ぇ琛ㄦ牸閫昏緫銆�
+ if (!showResult) {
+ return;
+ }
// 鏀舵牱鏃ユ湡
List<RowRenderData> rows = new ArrayList<>();
List<TextRenderData> text = new ArrayList<>();
@@ -2688,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);
});
@@ -2792,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();
}
// 妫�楠屾椂闂� 鎶芥牱鏃堕棿-鎻愪氦鏃堕棿
@@ -2824,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);
@@ -2839,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);
@@ -2863,10 +2916,35 @@
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");
Files.createDirectories(Paths.get(wordUrl));
template.writeAndClose(Files.newOutputStream(Paths.get(wordUrl, name)));
@@ -2884,6 +2962,89 @@
}
/**
+ * 鏂扮増妫�娴嬫姤鍛婄殑缁撴灉琛ㄥ浐瀹氫负鍥涘垪锛氬簭鍙枫�佹娴嬮」鐩�佸崟浣嶃�佹娴嬬粨鏋溿��
+ */
+ 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 : "/";
+ }
+
+ /**
* 璁$畻琛ㄦ牸鍒楀搴�
* @param max 鏈�澶ф楠屼釜鏁�
* @return
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
index 2354d4d..e27d5ff 100644
--- a/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/service/impl/InsReportServiceImpl.java
@@ -17,6 +17,7 @@
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.deepoove.poi.XWPFTemplate;
+import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.Pictures;
import com.itextpdf.text.BadElementException;
import com.itextpdf.text.DocumentException;
@@ -44,6 +45,7 @@
import com.ruoyi.inspect.service.InsOrderService;
import com.ruoyi.inspect.service.InsReportService;
import com.ruoyi.inspect.service.RawMaterialOrderService;
+import com.ruoyi.inspect.util.PreserveReportPicturePlaceholderHandler;
import com.ruoyi.system.mapper.UserMapper;
import com.ruoyi.system.service.InformationNotificationService;
import lombok.extern.slf4j.Slf4j;
@@ -523,7 +525,10 @@
@Override
public int wordInsertUrl(Map<String, Object> map, String url) {
- XWPFTemplate template = XWPFTemplate.compile(url).render(map);
+ Configure configure = Configure.builder()
+ .setValidErrorHandler(new PreserveReportPicturePlaceholderHandler())
+ .build();
+ XWPFTemplate template = XWPFTemplate.compile(url, configure).render(map);
try {
template.writeAndClose(Files.newOutputStream(Paths.get(url)));
} catch (IOException e) {
diff --git a/inspect-server/src/main/java/com/ruoyi/inspect/util/PreserveReportPicturePlaceholderHandler.java b/inspect-server/src/main/java/com/ruoyi/inspect/util/PreserveReportPicturePlaceholderHandler.java
new file mode 100644
index 0000000..9fb72f7
--- /dev/null
+++ b/inspect-server/src/main/java/com/ruoyi/inspect/util/PreserveReportPicturePlaceholderHandler.java
@@ -0,0 +1,41 @@
+package com.ruoyi.inspect.util;
+
+import com.deepoove.poi.config.Configure;
+import com.deepoove.poi.render.RenderContext;
+import com.deepoove.poi.template.ElementTemplate;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * 鎶ュ憡涓殑绛惧悕鍜屽嵃绔犱細鍦ㄧ紪鍒躲�佸鏍搞�佹壒鍑嗙瓑涓嶅悓闃舵鍒嗗埆鍐欏叆銆�
+ * 淇濈暀鍚庣画闃舵闇�瑕佷娇鐢ㄧ殑鍥剧墖鍗犱綅绗︼紝閬垮厤鍦ㄥ墠闈㈢殑娓叉煋闃舵琚彁鍓嶆竻闄ゃ��
+ */
+public class PreserveReportPicturePlaceholderHandler implements Configure.ValidErrorHandler {
+
+ private static final Set<String> DEFERRED_PICTURE_TAGS = new HashSet<>(Arrays.asList(
+ "writeUrl",
+ "writeDateUrl",
+ "insUrl",
+ "examineUrl",
+ "examineDateUrl",
+ "ratifyUrl",
+ "ratifyDateUrl",
+ "seal1",
+ "seal2"
+ ));
+
+ private final Configure.ClearHandler clearHandler = new Configure.ClearHandler();
+
+ @Override
+ public void handler(RenderContext<?> context) {
+ ElementTemplate template = context.getEleTemplate();
+ if (template != null
+ && Character.valueOf('@').equals(template.getSign())
+ && DEFERRED_PICTURE_TAGS.contains(template.getTagName())) {
+ return;
+ }
+ clearHandler.handler(context);
+ }
+}
diff --git a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
index 297090f..59e0cfd 100644
--- a/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
+++ b/inspect-server/src/main/resources/mapper/InsSampleMapper.xml
@@ -61,6 +61,7 @@
<result property="tell" column="tell" jdbcType="VARCHAR"/>
<result property="lastValue" column="last_value" jdbcType="VARCHAR"/>
<result property="insResult" column="ip_ins_result" jdbcType="INTEGER"/>
+ <result property="isBinding" column="ip_is_binding" jdbcType="INTEGER"/>
<result property="state" column="state" jdbcType="INTEGER"/>
<result property="insBushId" column="ins_bush_id" jdbcType="INTEGER"/>
<result property="insSampleId" column="ins_sample_id" jdbcType="INTEGER"/>
@@ -141,9 +142,10 @@
man_day,
bsm,
ask,
- `last_value`,
- ip.ins_result ip_ins_result,
- state,
+ `last_value`,
+ ip.ins_result ip_ins_result,
+ ip.is_binding ip_is_binding,
+ state,
ins_sample_id,
ip.create_user ip_create_user,
ip.update_user ip_update_user,
@@ -357,6 +359,7 @@
tell,
`last_value`,
ip.ins_result ip_ins_result,
+ ip.is_binding ip_is_binding,
state,
ins_sample_id,
ip.create_user ip_create_user,
@@ -416,6 +419,7 @@
ask,
`last_value`,
ip.ins_result ip_ins_result,
+ ip.is_binding ip_is_binding,
state,
ins_sample_id,
ip.create_user ip_create_user,
@@ -506,6 +510,7 @@
tell,
`last_value`,
ip.ins_result ip_ins_result,
+ ip.is_binding ip_is_binding,
state,
ins_sample_id,
ip.create_user ip_create_user,
diff --git a/inspect-server/src/main/resources/static/detection-report-template-v2.docx b/inspect-server/src/main/resources/static/detection-report-template-v2.docx
new file mode 100644
index 0000000..f385f8a
--- /dev/null
+++ b/inspect-server/src/main/resources/static/detection-report-template-v2.docx
Binary files differ
--
Gitblit v1.9.3