cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java
@@ -32,9 +32,9 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
@@ -404,44 +404,39 @@
        }
    }
    /** 新版方法验证主表导出;原始记录和验证结果仍作为附件保存。 */
    /** 按客户提供的横向方法验证表导出;原始记录和验证结果仍作为附件保存。 */
    private void exportWordVerification(ProcessMethodVerify verify, HttpServletResponse response) {
        JSONObject content = JSONUtil.parseObj(verify.getVerificationContent());
        String[][] checks = {
                {"personnel", "1. 人员能力"}, {"equipment", "2. 检测设备"},
                {"reagents", "3. 试剂要求"}, {"conditions", "4. 试验条件"},
                {"sample", "5. 样品处置"}, {"steps", "6. 试验步骤"},
                {"processing", "7. 数据处理"}, {"qualityControl", "质量控制方法"},
                {"result", "方法验证结果"}
        };
        try (XWPFDocument document = new XWPFDocument()) {
            XWPFParagraph title = document.createParagraph();
            title.setAlignment(ParagraphAlignment.CENTER);
            title.createRun().setText("方法验证记录");
        String[] checkKeys = {"personnel", "equipment", "reagents", "conditions", "sample", "steps",
                "processing", "qualityControl", "result"};
        InputStream templateStream = getClass().getResourceAsStream("/static/method-verification-customer.docx");
        if (templateStream == null) throw new IllegalStateException("方法验证导出模板不存在");
        try (InputStream inputStream = templateStream; XWPFDocument document = new XWPFDocument(inputStream)) {
            XWPFTable table = document.getTables().get(0);
            setTemplateCell(table, 0, 1, verify.getStandardName());
            setTemplateCell(table, 0, 3, verify.getStandardCode());
            setTemplateCell(table, 1, 1, verify.getVerifyItem());
            XWPFTable basic = document.createTable(2, 4);
            setCells(basic, 0, "标准名称", verify.getStandardName(), "标准编号", verify.getStandardCode());
            setCells(basic, 1, "验证项目", verify.getVerifyItem(), "", "");
            XWPFTable table = document.createTable(checks.length + 1, 4);
            setCells(table, 0, "验证内容", "方法要求", "实验室能力", "是否满足");
            JSONArray entries = content.getJSONArray("checks");
            for (int i = 0; i < checks.length; i++) {
                JSONObject entry = findCheck(entries, checks[i][0]);
            for (int i = 0; i < checkKeys.length; i++) {
                JSONObject entry = findCheck(entries, checkKeys[i]);
                Integer satisfied = entry == null ? null : entry.getInt("satisfied");
                setCells(table, i + 1, checks[i][1], field(entry, "requirement"),
                        field(entry, "capability"), satisfied == null ? "" : satisfied == 1 ? "是" : "否");
                int row = i + 3;
                if (row <= 8) {
                    setTemplateCell(table, row, 1, field(entry, "requirement"));
                    setTemplateCell(table, row, 2, field(entry, "capability"));
                    setTemplateCell(table, row, 3, satisfiedText(satisfied));
                } else {
                    setTemplateCell(table, row, 1, mergedCheckText(entry));
                    setTemplateCell(table, row, 2, satisfiedText(satisfied));
                }
            }
            setTemplateCell(table, 12, 1, field(content, "conclusion"));
            setTemplateCell(table, 13, 1, decisionText(field(content, "reviewDecision"),
                    "评审人员", field(content, "reviewer"), field(content, "reviewDate")));
            setTemplateCell(table, 14, 1, decisionText(field(content, "approvalDecision"),
                    "技术负责人", field(content, "approver"), field(content, "approvalDate")));
            XWPFTable conclusion = document.createTable(1, 2);
            setCells(conclusion, 0, "验证结论", field(content, "conclusion"));
            XWPFTable decisions = document.createTable(2, 4);
            setCells(decisions, 0, "评审意见", field(content, "reviewDecision"),
                    "评审人员:" + field(content, "reviewer"), field(content, "reviewDate"));
            setCells(decisions, 1, "审批意见", field(content, "approvalDecision"),
                    "技术负责人:" + field(content, "approver"), field(content, "approvalDate"));
            document.createParagraph().createRun().setText("附件1 原始记录;附件2 验证结果(附件在系统中查看)");
            response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
            String fileName = URLEncoder.encode("方法验证记录", "UTF-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".docx");
@@ -464,10 +459,42 @@
        return object == null ? "" : StrUtil.nullToEmpty(object.getStr(key));
    }
    private void setCells(XWPFTable table, int row, String... values) {
        for (int i = 0; i < values.length; i++) {
            XWPFTableCell cell = table.getRow(row).getCell(i);
            cell.setText(StrUtil.nullToEmpty(values[i]));
    private String satisfiedText(Integer satisfied) {
        return satisfied == null ? "" : satisfied == 1 ? "是" : "否";
    }
    private String mergedCheckText(JSONObject entry) {
        String requirement = field(entry, "requirement").trim();
        String capability = field(entry, "capability").trim();
        if (requirement.isEmpty()) return capability;
        if (capability.isEmpty() || requirement.equals(capability)) return requirement;
        return "方法要求:" + requirement + "\n实验室能力:" + capability;
    }
    private String decisionText(String decision, String personLabel, String person, String date) {
        StringBuilder value = new StringBuilder();
        for (String choice : new String[]{"同意使用", "不能使用", "限制使用"}) {
            if (value.length() > 0) value.append("  ");
            value.append(choice.equals(decision) ? "☑" : "□").append(choice);
        }
        value.append("\n").append(personLabel).append(":").append(person);
        if (StringUtils.isNotBlank(date)) {
            String[] parts = date.split("-");
            value.append("    日期:");
            value.append(parts.length == 3 ? parts[0] + "年" + parts[1] + "月" + parts[2] + "日" : date);
        }
        return value.toString();
    }
    private void setTemplateCell(XWPFTable table, int row, int cellIndex, String value) {
        XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
        XWPFParagraph paragraph = cell.getParagraphs().get(0);
        XWPFRun run = paragraph.getRuns().get(0);
        String[] lines = StrUtil.nullToEmpty(value).replace("\r\n", "\n").split("\n", -1);
        run.setText(lines[0], 0);
        for (int i = 1; i < lines.length; i++) {
            run.addBreak();
            run.setText(lines[i]);
        }
    }