package cn.iocoder.yudao.module.crm.service.contract; import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; import org.junit.jupiter.api.Test; import org.springframework.core.io.ClassPathResource; import java.io.*; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.nio.file.*; import java.time.LocalDateTime; import java.util.*; import java.util.zip.*; import static org.junit.jupiter.api.Assertions.*; class CrmContractDocumentRendererTest { private CrmContractRespVO contract() { var contract = new CrmContractRespVO(); contract.setNo("HT20261015001"); contract.setCustomerName("中国科学院西北高原生物研究所"); contract.setOrderDate(LocalDateTime.of(2026,10,15,0,0)); var product = new CrmContractRespVO.Product(); product.setItemName("冻干冬虫夏草"); product.setItemSpecification("2500级一等"); product.setItemUnitName("g"); product.setCount(new BigDecimal("200")); product.setContractPrice(new BigDecimal("50")); product.setTotalPrice(new BigDecimal("10000")); contract.setProducts(List.of(product)); contract.setTotalPrice(new BigDecimal("10000")); return contract; } private Map unzip(byte[] bytes) throws IOException { Map parts = new HashMap<>(); try (var input = new ZipInputStream(new ByteArrayInputStream(bytes))) { ZipEntry entry; while ((entry=input.getNextEntry())!=null) parts.put(entry.getName(),input.readAllBytes()); } return parts; } @Test void fillsContractAndPreservesTemplateParts() throws Exception { byte[] bytes = CrmContractDocumentRenderer.render(contract()); var parts = unzip(bytes); String xml = new String(parts.get("word/document.xml"), StandardCharsets.UTF_8); assertTrue(xml.contains("HT20261015001")); assertTrue(xml.contains("2026年10月15日")); assertTrue(xml.contains("10000.00")); assertTrue(xml.contains("壹万元整")); assertTrue(xml.contains("冻干冬虫夏草(2500级一等)")); assertTrue(xml.contains("诚信合规")); assertFalse(xml.contains("${")); try(var template = new ClassPathResource("templates/contract/sale-contract.docx").getInputStream()) { var original = unzip(template.readAllBytes()); assertEquals(original.keySet(),parts.keySet()); for (var name : original.keySet()) if (!name.equals("word/document.xml")) assertArrayEquals(original.get(name), parts.get(name), name); } String output = System.getProperty("contract.document.sample"); if (output != null) Files.write(Path.of(output),bytes); } @Test void supportsManyProductsDiscountAndXmlCharacters() throws Exception { var contract = contract(); contract.setCustomerName("客户<&>公司"); contract.setProducts(Collections.nCopies(25,contract.getProducts().getFirst())); contract.setTotalPrice(new BigDecimal("249000")); String xml = new String(unzip(CrmContractDocumentRenderer.render(contract)).get("word/document.xml"),StandardCharsets.UTF_8); assertTrue(xml.contains("客户<&>公司")); assertTrue(xml.contains("整单优惠 1000.00 元")); assertEquals(25,xml.split("冻干冬虫夏草(2500级一等)",-1).length-1); assertEquals(25,xml.split("CrmContractDocumentRenderer.render(contract)); } }