cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessMethodVerifyServiceImpl.java
@@ -1,10 +1,7 @@
package com.ruoyi.process.service.impl;
import cn.hutool.core.bean.BeanUtil;
import  cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
@@ -32,11 +29,6 @@
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.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
@@ -49,9 +41,6 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.LinkedHashSet;
import java.util.Objects;
import java.util.Set;
/**
 * 标准方法验证
@@ -114,7 +103,6 @@
        if (methodVerifyDto.getOperationType() == null) {
            throw new ErrorException("请选择是变更还是验证");
        }
        validateWordVerification(methodVerifyDto);
        // 新增
        baseMapper.insert(methodVerifyDto);
@@ -160,37 +148,6 @@
        return processMethodVerifyDto;
    }
    @Override
    public List<ProcessMethodVerifyWorkFile> getCurrentWorkFiles(String userIds) {
        List<ProcessMethodVerifyWorkFile> files = new ArrayList<>();
        if (StringUtils.isBlank(userIds)) return files;
        Set<Integer> ids = new LinkedHashSet<>();
        for (String value : StrUtil.split(userIds, ",")) {
            try {
                ids.add(Integer.valueOf(value.trim()));
            } catch (NumberFormatException e) {
                throw new ErrorException("检测人员ID无效");
            }
        }
        if (ids.size() > 5) throw new ErrorException("最多选择5名检测人员");
        for (Integer userId : ids) {
            ProcessMethodVerifyWorkFile file = processMethodVerifyWorkFileMapper.selectLatestAuthorization(userId);
            if (file == null) continue;
            file.setUserId(userId);
            if (StringUtils.isBlank(file.getFileUrl())) {
                file.setFileName("未上传上岗证书");
                file.setType(0);
            } else {
                file.setFileName(file.getFileUrl());
                file.setType(file.getFileUrl().matches("(?i).*\\.(png|jpe?g|gif|bmp|webp)$") ? 1 : 2);
            }
            files.add(file);
        }
        return files;
    }
    /**
     * 修改标准方法验证
     *
@@ -199,7 +156,6 @@
     */
    @Override
    public boolean updateMethodVerify(ProcessMethodVerifyDto methodVerifyDto) {
        validateWordVerification(methodVerifyDto);
        baseMapper.updateById(methodVerifyDto);
        // 删除上岗证
@@ -332,14 +288,6 @@
    public void exportMethodVerify(Integer methodVerifyId, HttpServletResponse response) {
        // 获取标准方法更新验证数据
        ProcessMethodVerify processMethodVerify = baseMapper.selectById(methodVerifyId);
        if (processMethodVerify == null) {
            throw new ErrorException("方法验证记录不存在");
        }
        if (Objects.equals(processMethodVerify.getOperationType(), 1)
                && StringUtils.isNotBlank(processMethodVerify.getVerificationContent())) {
            exportWordVerification(processMethodVerify, response);
            return;
        }
        // 将内部字段转换成展示到word中的对象
        ProcessMethodVerifyExportWordDto exportWordTemplate = getExportWordTemplate(processMethodVerify);
@@ -404,91 +352,6 @@
        }
    }
    /** 新版方法验证主表导出;原始记录和验证结果仍作为附件保存。 */
    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("方法验证记录");
            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]);
                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 ? "是" : "否");
            }
            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");
            document.write(response.getOutputStream());
        } catch (Exception e) {
            throw new RuntimeException("导出方法验证记录失败", e);
        }
    }
    private JSONObject findCheck(JSONArray entries, String key) {
        if (entries == null) return null;
        for (Object value : entries) {
            JSONObject check = JSONUtil.parseObj(value);
            if (key.equals(check.getStr("key"))) return check;
        }
        return null;
    }
    private String field(JSONObject object, String key) {
        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 void validateWordVerification(ProcessMethodVerifyDto verify) {
        // 没有 verificationContent 的旧记录继续由旧表单维护。
        if (!Objects.equals(verify.getOperationType(), 1)
                || StringUtils.isBlank(verify.getVerificationContent())) return;
        if (StringUtils.isBlank(verify.getStandardName())
                || StringUtils.isBlank(verify.getStandardCode())
                || StringUtils.isBlank(verify.getVerifyItem())) {
            throw new ErrorException("请填写标准名称、标准编号和验证项目");
        }
        try {
            JSONUtil.parseObj(verify.getVerificationContent());
        } catch (Exception e) {
            throw new ErrorException("方法验证内容格式错误");
        }
        // 保留旧列供现有依赖 method_name 的查询使用。
        verify.setMethodName(verify.getStandardName());
    }
    private ProcessMethodVerifyExportWordDto getExportWordTemplate(ProcessMethodVerify processMethodVerify) {
        ProcessMethodVerifyExportWordDto processMethodVerifyExportWordDto = new ProcessMethodVerifyExportWordDto();
        BeanUtil.copyProperties(processMethodVerify, processMethodVerifyExportWordDto);
@@ -526,7 +389,22 @@
        }
        // 确认日期
        processMethodVerifyExportWordDto.setConfirmDateStr(processMethodVerify.getConfirmDate() != null ? processMethodVerify.getConfirmDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")): null);
        // 评审意见
        processMethodVerifyExportWordDto.setReviewResultStr(buildOpinionCheckBox(processMethodVerify.getReviewResult()));
        processMethodVerifyExportWordDto.setReviewDateStr(processMethodVerify.getReviewDate() != null ? processMethodVerify.getReviewDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")): null);
        // 审批意见
        processMethodVerifyExportWordDto.setApprovalResultStr(buildOpinionCheckBox(processMethodVerify.getApprovalResult()));
        processMethodVerifyExportWordDto.setApprovalDateStr(processMethodVerify.getApprovalDate() != null ? processMethodVerify.getApprovalDate().format(DateTimeFormatter.ofPattern("yyyy年MM月dd日")): null);
        return processMethodVerifyExportWordDto;
    }
    /**
     * 拼接"同意使用/不能使用/限制使用"勾选项展示串
     */
    private String buildOpinionCheckBox(String result) {
        return ("同意使用".equals(result) ? "☑" : "□") + "同意使用 "
                + ("不能使用".equals(result) ? "☑" : "□") + "不能使用 "
                + ("限制使用".equals(result) ? "☑" : "□") + "限制使用";
    }
@@ -546,7 +424,7 @@
                User user = userMapper.selectById(userId);
                ProcessMethodVerifyWorkFile workFile = new ProcessMethodVerifyWorkFile();
                workFile.setMethodVerifyId(methodVerifyDto.getMethodVerifyId());
                workFile.setType(workName.matches("(?i).*\\.(png|jpe?g|gif|bmp|webp)$") ? 1 : 2);
                workFile.setType(1);
                workFile.setFileUrl(workName);
                workFile.setFileName(workName);
                workFile.setUserId(user.getId());