3 小时以前 ed01be06d17f1159d32e7fa2133a32f2986d3b9f
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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("判定结论:1型", 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);
            assertTrue(signLine.contains("复核:周亚洁"), "复核应带出复核人姓名:" + signLine);
            assertTrue(signLine.contains("批准:包翠萍"), "批准应带出审核人姓名:" + signLine);
        }
    }
 
    @Test
    public void testBuildWithoutSigners() throws IOException {
        MesQcQcReportRespVO report = randomReport();
        report.setInspectorName(null).setAuditName("").setApproveName(null);
        byte[] docx = MesQcProductReportWordBuilder.build(report);
        try (InputStream in = new ByteArrayInputStream(docx); XWPFDocument doc = new XWPFDocument(in)) {
            String signLine = doc.getParagraphs().stream()
                    .filter(p -> p.getText().contains("批准:"))
                    .findFirst().orElseThrow().getText();
            assertTrue(signLine.contains("检验:") && signLine.contains("复核:") && signLine.contains("批准:"),
                    "未指定人员时仍保留三个栏位供手写:" + signLine);
        }
    }
 
    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("包翠萍")
                .setCheckResult(1).setCheckResultText("1型");
 
        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() {
        }
    }
 
}