From f02d0df2f1dfc7a8b775cf50d45375e57628a5ef Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 10 十一月 2026 11:26:22 +0800
Subject: [PATCH] feat(ncr): 完善NCR处置联动逻辑与关闭校验
---
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractDocumentRenderer.java | 130 ++++++++++++++++++++++++++++++++++---------
1 files changed, 103 insertions(+), 27 deletions(-)
diff --git a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractDocumentRenderer.java b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractDocumentRenderer.java
index cd05bb6..c3a7102 100644
--- a/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractDocumentRenderer.java
+++ b/yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contract/CrmContractDocumentRenderer.java
@@ -4,6 +4,7 @@
import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO;
import org.springframework.core.io.ClassPathResource;
import org.w3c.dom.*;
+
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.TransformerFactory;
@@ -16,7 +17,7 @@
import java.util.*;
import java.util.zip.*;
-/** 淇濈暀鐢ㄦ埛鍚堝悓妯℃澘鐨� OOXML 鍖咃紝浠呭~鍐欎笟鍔℃Ы浣嶅苟鎵╁睍浜у搧鏄庣粏琛屻�� */
+/** 鍦ㄩ攢鍞悎鍚屾ā鏉夸腑濉啓鍚堝悓淇℃伅锛屼繚鐣欐ā鏉跨殑鍏朵粬鍐呭鍙婃牸寮忋�� */
public final class CrmContractDocumentRenderer {
private static final String W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
private CrmContractDocumentRenderer() { }
@@ -49,14 +50,38 @@
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, "");
var document = factory.newDocumentBuilder().parse(new ByteArrayInputStream(xml));
- Node rowTemplate = null;
- var rows = document.getElementsByTagNameNS(W, "tr");
- for (int i = 0; i < rows.getLength(); i++) {
- if (rows.item(i).getTextContent().contains("${itemName}")) { rowTemplate = rows.item(i); break; }
+ var paragraphs = document.getElementsByTagNameNS(W, "p");
+ boolean numberFilled = false, dateFilled = false, buyerFilled = false;
+ for (int i = 0; i < paragraphs.getLength(); i++) {
+ Node p = paragraphs.item(i);
+ String value = p.getTextContent();
+ if (value.contains("鍚堝悓缂栧彿锛歋HHT20260102902-F")) {
+ setParagraphText(p, "鍚堝悓缂栧彿锛�" + text(contract.getNo()));
+ alignRight(p);
+ numberFilled = true;
+ } else if (value.trim().equals("绛捐鏃ユ湡锛�")) {
+ setParagraphText(p, "绛捐鏃ユ湡锛�" + date(contract.getOrderDate()));
+ alignRight(p);
+ dateFilled = true;
+ } else if (value.contains("涔版柟锛堢敳鏂癸級锛�") && value.contains("绛捐鍦扮偣锛氭睙鑻忓崡閫�")) {
+ fillBuyer(p, text(contract.getCustomerName()));
+ buyerFilled = true;
+ } else if (value.equals("涔欐柟锛堢洊绔狅級锛�")) {
+ // 鍘熸ā鏉跨殑绌虹櫧涔版柟绛剧珷妗嗚鍐欎负涔欐柟锛涘悓鏃跺鐞� DrawingML 鍜� VML 涓や釜鍏煎鍒嗘敮銆�
+ setParagraphText(p, "鐢叉柟锛堢洊绔狅級锛�" + text(contract.getCustomerName()));
+ }
}
- if (rowTemplate == null) throw new IOException("鍚堝悓妯℃澘缂哄皯浜у搧鏄庣粏妲戒綅");
- int index = 0;
+ if (!numberFilled || !dateFilled || !buyerFilled) throw new IOException("閿�鍞悎鍚屾ā鏉胯〃澶翠笉瀹屾暣");
+
+ Node table = document.getElementsByTagNameNS(W, "tbl").item(0);
+ List<Node> rows = children(table, "tr");
+ if (rows.size() != 4 || children(rows.get(1), "tc").size() != 7
+ || !rows.get(3).getTextContent().contains("鍚堣锛堝惈13%澧炲�肩◣锛�")) {
+ throw new IOException("閿�鍞悎鍚屾ā鏉夸骇鍝佽〃鏍间笉瀹屾暣");
+ }
+ Node rowTemplate = rows.get(1);
BigDecimal productTotal = BigDecimal.ZERO;
+ int index = 0;
for (var product : contract.getProducts()) {
Node row = rowTemplate.cloneNode(true);
BigDecimal price = Objects.requireNonNull(product.getContractPrice(), "鍚堝悓鍗曚环涓嶈兘涓虹┖");
@@ -66,25 +91,32 @@
productTotal = productTotal.add(amount);
String name = text(product.getItemName());
if (name.isBlank()) name = text(product.getItemCode());
- String specification = text(product.getItemSpecification());
- if (!specification.isBlank()) name += "锛�" + specification + "锛�";
- replace(row, Map.of("index", String.valueOf(++index), "itemName", name,
- "unitName", text(product.getItemUnitName()), "count", count.stripTrailingZeros().toPlainString(),
- "price", money(price), "amount", money(amount)));
+ List<Node> cells = children(row, "tc");
+ String[] values = {String.valueOf(++index), name, text(product.getItemSpecification()),
+ text(product.getItemUnitName()), count.stripTrailingZeros().toPlainString(), money(price), money(amount)};
+ for (int i = 0; i < values.length; i++) {
+ Node cell = cells.get(i);
+ Node properties = firstChild(cell, "tcPr");
+ Node noWrap = firstChild(properties, "noWrap");
+ if (noWrap != null) properties.removeChild(noWrap);
+ setParagraphText(firstChild(cell, "p"), values[i]);
+ }
+ Node rowProperties = firstChild(row, "trPr");
+ if (firstChild(rowProperties, "cantSplit") == null) {
+ rowProperties.appendChild(document.createElementNS(W, "w:cantSplit"));
+ }
rowTemplate.getParentNode().insertBefore(row, rowTemplate);
}
rowTemplate.getParentNode().removeChild(rowTemplate);
BigDecimal total = Objects.requireNonNull(contract.getTotalPrice(), "鍚堝悓鎬婚噾棰濅笉鑳戒负绌�");
BigDecimal discount = productTotal.subtract(total);
- replace(document, Map.of("customerName", text(contract.getCustomerName()),
- "contractNo", text(contract.getNo()),
- "contractName", text(contract.getName()),
- "contractDate", contract.getOrderDate() == null ? "____________" : contract.getOrderDate().format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")),
- "startDate", date(contract.getStartTime()),
- "endDate", date(contract.getEndTime()),
- "totalPrice", money(total), "totalUpper", Convert.digitToChinese(total),
- "discountNote", discount.signum() == 0 ? "" : "锛堟槑缁嗗悎璁� " + money(productTotal) + " 鍏冿紝涓庡悎鍚岄噾棰濈殑宸 " + money(discount) + " 鍏冿級"));
- if (document.getDocumentElement().getTextContent().contains("${")) throw new IOException("鍚堝悓妯℃澘瀛樺湪鏈~鍐欐Ы浣�");
+ String summary = "鍚堣锛堝惈13%澧炲�肩◣锛� 浜烘皯甯侊細" + money(total) + "鍏� 澶у啓锛�" + Convert.digitToChinese(total);
+ if (discount.signum() != 0) summary += "锛堟槑缁嗗悎璁� " + money(productTotal) + " 鍏冿紝涓庡悎鍚岄噾棰濈殑宸 " + money(discount) + " 鍏冿級";
+ Node totalCell = children(rows.get(3), "tc").get(0);
+ Node totalProperties = firstChild(totalCell, "tcPr");
+ Node noWrap = firstChild(totalProperties, "noWrap");
+ if (noWrap != null) totalProperties.removeChild(noWrap);
+ setParagraphText(firstChild(totalCell, "p"), summary);
var transformerFactory = TransformerFactory.newInstance();
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, "");
transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, "");
@@ -95,16 +127,60 @@
} catch (Exception exception) { throw new IOException("鐢熸垚鍚堝悓 Word 鏂囦欢澶辫触", exception); }
}
- private static void replace(Node node, Map<String, String> values) {
- if (node.getNodeType() == Node.ELEMENT_NODE && "t".equals(node.getLocalName()) && W.equals(node.getNamespaceURI())) {
+ private static void fillBuyer(Node paragraph, String buyer) {
+ var nodes = ((Element) paragraph).getElementsByTagNameNS(W, "t");
+ boolean afterColon = false, inserted = false;
+ for (int i = 0; i < nodes.getLength(); i++) {
+ Node node = nodes.item(i);
String value = node.getTextContent();
- for (var entry : values.entrySet()) value = value.replace("${" + entry.getKey() + "}", entry.getValue());
- node.setTextContent(value);
- return;
+ if (value.contains("绛捐鍦扮偣锛�")) break;
+ if (afterColon) {
+ node.setTextContent(inserted ? "" : buyer + " ");
+ inserted = true;
+ } else if (value.equals("锛�")) afterColon = true;
}
- for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) replace(child, values);
+ if (!inserted) throw new IllegalStateException("鍚堝悓妯℃澘缂哄皯涔版柟濉啓浣嶇疆");
}
+ private static void alignRight(Node paragraph) {
+ Node properties = firstChild(paragraph, "pPr");
+ Node indent = firstChild(properties, "ind");
+ if (indent != null) properties.removeChild(indent);
+ Node alignment = firstChild(properties, "jc");
+ if (alignment == null) {
+ alignment = paragraph.getOwnerDocument().createElementNS(W, "w:jc");
+ properties.appendChild(alignment);
+ }
+ ((Element) alignment).setAttributeNS(W, "w:val", "right");
+ }
+
+ private static void setParagraphText(Node paragraph, String value) {
+ var nodes = ((Element) paragraph).getElementsByTagNameNS(W, "t");
+ if (nodes.getLength() == 0) {
+ var document = paragraph.getOwnerDocument();
+ Node run = document.createElementNS(W, "w:r");
+ Node text = document.createElementNS(W, "w:t");
+ run.appendChild(text);
+ paragraph.appendChild(run);
+ text.setTextContent(value);
+ } else {
+ nodes.item(0).setTextContent(value);
+ for (int i = 1; i < nodes.getLength(); i++) nodes.item(i).setTextContent("");
+ }
+ }
+
+ private static List<Node> children(Node parent, String localName) {
+ List<Node> result = new ArrayList<>();
+ if (parent == null) return result;
+ for (Node node = parent.getFirstChild(); node != null; node = node.getNextSibling()) {
+ if (W.equals(node.getNamespaceURI()) && localName.equals(node.getLocalName())) result.add(node);
+ }
+ return result;
+ }
+ private static Node firstChild(Node parent, String localName) {
+ List<Node> result = children(parent, localName);
+ return result.isEmpty() ? null : result.get(0);
+ }
private static String money(BigDecimal value) { return value.setScale(2, RoundingMode.HALF_UP).toPlainString(); }
private static String date(java.time.LocalDateTime value) { return value == null ? "____________" : value.format(DateTimeFormatter.ofPattern("yyyy骞碝M鏈坉d鏃�")); }
private static String text(String value) { return value == null ? "" : value; }
--
Gitblit v1.9.3