11 小时以前 453fece707fc1e611554fbb10125383eba01449a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package cn.iocoder.yudao.module.mes.service.qc.indicatorresult;
 
import cn.iocoder.yudao.module.mes.controller.admin.qc.indicatorresult.vo.MesQcQcReportRespVO;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.junit.jupiter.api.Test;
 
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.List;
 
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
 
/**
 * {@link MesQcProductReportWordBuilder} 的单元测试(版式冒烟:生成可读回 + 关键内容断言)
 *
 * 生成的样例文件会输出到 target/MesQcProductReportWordBuilder-sample.docx,可用 Word 人工核对版式。
 */
public class MesQcProductReportWordBuilderTest {
 
    @Test
    public void testBuild() throws IOException {
        byte[] docx = MesQcProductReportWordBuilder.build(randomReport());
        assertTrue(docx.length > 0);
 
        // 输出样例文件,便于人工用 Word 打开核对
        Path sample = Path.of("target", "MesQcProductReportWordBuilder-sample.docx");
        Files.createDirectories(sample.getParent());
        try (OutputStreamHolder ignored = new OutputStreamHolder(sample, docx)) {
            // no-op
        }
 
        try (InputStream in = new ByteArrayInputStream(docx); XWPFDocument doc = new XWPFDocument(in)) {
            List<XWPFTable> tables = doc.getTables();
            assertEquals(4, tables.size(), "表头/检验项目/判定结论/备注 共 4 张表");
 
            // 表头首行:生产单位 + 公司名
            assertTrue(tables.get(0).getRow(0).getCell(0).getText().contains("生产单位"));
            assertTrue(tables.get(0).getRow(0).getCell(1).getText().contains("宁夏九泓化工科技有限公司"));
 
            // 检验项目表:表头行 + 外观 + 分组展开的录入项(酸含量分组被隐藏)
            XWPFTable itemTable = tables.get(1);
            assertEquals(4, itemTable.getNumberOfRows());
            assertEquals("外观", itemTable.getRow(1).getCell(0).getText().trim());
            assertEquals("透明液体,无可见杂质", itemTable.getRow(1).getCell(1).getText().trim());
            // 平行测量顿号合并
            assertEquals("10.023、10.115", itemTable.getRow(2).getCell(1).getText().trim());
            assertEquals("/", itemTable.getRow(3).getCell(1).getText().trim());
 
            // 判定结论/质检专用章留空
            assertTrue(itemTable.getRow(2).getCell(0).getText().contains("试样质量 m /g"));
            assertEquals("判定结论:", tables.get(2).getRow(0).getCell(0).getText().trim());
            assertEquals("质检专用章:", tables.get(2).getRow(0).getCell(1).getText().trim());
 
            // 备注默认文案
            assertTrue(tables.get(3).getRow(0).getCell(1).getText().contains("检验结果仅对样品本身或本批次负责"));
 
            // 签字行留空(打印后手写),不得出现姓名
            String signLine = doc.getParagraphs().stream()
                    .filter(p -> p.getText().contains("批准:"))
                    .findFirst().orElseThrow().getText();
            assertTrue(signLine.contains("检验:") && signLine.contains("复核:"));
            assertTrue(!signLine.contains("李博") && !signLine.contains("周亚洁") && !signLine.contains("包翠萍"),
                    "签字行应留空,不输出检验/复核/批准姓名");
        }
    }
 
    private static MesQcQcReportRespVO randomReport() {
        MesQcQcReportRespVO report = new MesQcQcReportRespVO();
        report.setQcType(3).setDocCode("OQC20260106001").setItemName("工业用正丁醇")
                .setBatchNo("20260100001").setSamplingLocation("成品正丁醇储罐(V0008A)")
                .setExecuteStandard("GB/T 6027-2023")
                .setSamplingTime(LocalDateTime.of(2026, 1, 6, 9, 0))
                .setInspectDate(LocalDateTime.of(2026, 1, 6, 15, 0))
                .setDocCreateTime(LocalDateTime.of(2026, 1, 6, 8, 0))
                .setInspectorName("李博").setAuditName("周亚洁").setApproveName("包翠萍");
 
        MesQcQcReportRespVO.ReportItem appearance = new MesQcQcReportRespVO.ReportItem();
        appearance.setIndicatorName("外观").setItemType(1).setSortOrder(10);
        appearance.setMeasures(List.of(measure(1, "透明液体,无可见杂质")));
 
        MesQcQcReportRespVO.ReportItem acidGroup = new MesQcQcReportRespVO.ReportItem();
        acidGroup.setIndicatorName("酸含量").setItemType(2).setSortOrder(20);
        acidGroup.setFormulaText("w3 = c × V × 0.06005 / m × 100%");
        MesQcQcReportRespVO.ReportItem mass = new MesQcQcReportRespVO.ReportItem();
        mass.setIndicatorName("试样质量 m /g").setItemType(1).setSortOrder(1);
        mass.setMeasures(List.of(measure(1, "10.023"), measure(2, "10.115")));
        acidGroup.setChildren(List.of(mass));
 
        MesQcQcReportRespVO.ReportItem water = new MesQcQcReportRespVO.ReportItem();
        water.setIndicatorName("水分,w/%,≤").setItemType(1).setSortOrder(30);
        water.setMeasures(List.of());
 
        report.setItems(List.of(appearance, acidGroup, water));
        report.setBatchQuantity(null);
        return report;
    }
 
    private static MesQcQcReportRespVO.ReportMeasure measure(int measureNo, String value) {
        MesQcQcReportRespVO.ReportMeasure measure = new MesQcQcReportRespVO.ReportMeasure();
        measure.setMeasureNo(measureNo);
        measure.setValue(value);
        return measure;
    }
 
    /** try-with-resources 辅助:写出样例文件 */
    private static class OutputStreamHolder implements AutoCloseable {
        OutputStreamHolder(Path path, byte[] content) throws IOException {
            Files.write(path, content);
        }
 
        @Override
        public void close() {
        }
    }
 
}