cnas-process/src/main/java/com/ruoyi/process/service/impl/ProcessReportServiceImpl.java
@@ -15,7 +15,12 @@
import com.ruoyi.common.utils.QueryWrappers;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.SignatureImageUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.framework.exception.ErrorException;
import com.ruoyi.inspect.mapper.InsOrderMapper;
import com.ruoyi.inspect.mapper.InsReportMapper;
import com.ruoyi.inspect.pojo.InsOrder;
import com.ruoyi.inspect.pojo.InsReport;
import com.ruoyi.inspect.util.HackLoopTableRenderPolicy;
import com.ruoyi.process.dto.ProcessReportDto;
import com.ruoyi.process.mapper.ProcessReportMapper;
@@ -33,11 +38,14 @@
import java.io.*;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -56,17 +64,162 @@
    private ProcessReportMapper processReportMapper;
    @Value("${file.path}")
    private String imgUrl;
    @Value("${wordUrl}")
    private String wordUrl;
    @Resource
    private UserMapper userMapper;
    @Resource
    private InsReportMapper insReportMapper;
    @Resource
    private InsOrderMapper insOrderMapper;
    @Override
    public IPage<ProcessReportDto> pageProcessReport(Page page, ProcessReportDto processReport) {
        // #21 打开列表时,自动带出已通过审批(批准状态=批准)但尚未登记的检验单
        autoBringOutApproved();
        IPage<ProcessReportDto> processReportIPage = processReportMapper.pageProcessReport(page, QueryWrappers.queryWrappers(processReport));
        return processReportIPage;
    }
    /**
     * 扫描已批准但尚未进入报告结果登记表的检验报告,自动生成待登记记录(以报告编号去重,幂等)。
     */
    private void autoBringOutApproved() {
        List<InsReport> approved = insReportMapper.selectList(Wrappers.<InsReport>lambdaQuery()
                .eq(InsReport::getIsRatify, 1)
                .eq(InsReport::getIsPass, 1));
        if (approved.isEmpty()) {
            return;
        }
        Set<String> existing = this.list(Wrappers.<ProcessReport>lambdaQuery()
                .select(ProcessReport::getInsReportCode)).stream()
                .map(ProcessReport::getInsReportCode)
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toCollection(HashSet::new));
        for (InsReport report : approved) {
            String code = report.getCode();
            if (StringUtils.isBlank(code) || existing.contains(code)) {
                continue;
            }
            ProcessReport pr = new ProcessReport();
            pr.setInsReportCode(code);
            pr.setInspectionOrderId(report.getInsOrderId());
            if (report.getRatifyTime() != null) {
                pr.setSendTime(report.getRatifyTime().toLocalDate());
            }
            if (report.getInsOrderId() != null) {
                InsOrder order = insOrderMapper.selectById(report.getInsOrderId());
                if (order != null) {
                    pr.setSend(order.getCompany());
                    pr.setMethod(getReportDeliveryName(order));
                }
            }
            this.save(pr);
            existing.add(code);
        }
    }
    private String getReportDeliveryName(InsOrder order) {
        if (order.getReportDelivery() != null) {
            switch (order.getReportDelivery()) {
                case 1:
                    return "自取";
                case 2:
                    return "快递";
                case 3:
                    return "电子传送";
                default:
                    break;
            }
        }
        // 兼容尚未维护 report_delivery 的历史委托单。
        return order.getSend() != null && order.getSend() == 1 ? "自取" : "其他";
    }
    @Override
    public void downloadStampedReport(Integer id, String format, HttpServletResponse response) {
        if (id == null) {
            throw new ErrorException("报告结果不能为空");
        }
        ProcessReport processReport = getById(id);
        if (processReport == null) {
            throw new ErrorException("报告结果不存在");
        }
        if (StringUtils.isBlank(processReport.getInsReportCode())
                && processReport.getInspectionOrderId() == null) {
            throw new ErrorException("该记录未关联检验报告,无法下载");
        }
        String downloadFormat = StringUtils.isBlank(format) ? "pdf" : format.trim().toLowerCase();
        if (!"pdf".equals(downloadFormat) && !"word".equals(downloadFormat)) {
            throw new ErrorException("下载格式不正确");
        }
        com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<InsReport> query =
                Wrappers.<InsReport>lambdaQuery()
                        .eq(InsReport::getIsRatify, 1)
                        .eq(StringUtils.isNotBlank(processReport.getInsReportCode()),
                                InsReport::getCode, processReport.getInsReportCode())
                        .eq(processReport.getInspectionOrderId() != null,
                                InsReport::getInsOrderId, processReport.getInspectionOrderId())
                        .orderByDesc(InsReport::getId)
                        .last("limit 1");
        InsReport report = insReportMapper.selectOne(query);
        if (report == null) {
            throw new ErrorException("未找到已批准的报告,无法下载有章报告");
        }
        String fileUrl;
        String extension;
        String contentType;
        if ("word".equals(downloadFormat)) {
            fileUrl = StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl();
            extension = ".docx";
            contentType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
        } else {
            fileUrl = report.getTempUrlPdf();
            extension = ".pdf";
            contentType = "application/pdf";
        }
        if ("pdf".equals(downloadFormat) && StringUtils.isBlank(fileUrl)) {
            String reportUrl = StringUtils.isNotBlank(report.getUrlS()) ? report.getUrlS() : report.getUrl();
            if (StringUtils.isNotBlank(reportUrl) && reportUrl.toLowerCase().endsWith(".docx")) {
                fileUrl = reportUrl.substring(0, reportUrl.length() - 5) + ".pdf";
            }
        }
        if (StringUtils.isBlank(fileUrl) || !fileUrl.replace('\\', '/').startsWith("/word/")) {
            throw new ErrorException("有章报告尚未生成,请先完成报告批准");
        }
        Path root = Paths.get(wordUrl).toAbsolutePath().normalize();
        Path file = root.resolve(fileUrl.replace('\\', '/').substring("/word/".length())).normalize();
        if (!file.startsWith(root) || !Files.isRegularFile(file)) {
            throw new ErrorException("有章报告文件不存在,请重新批准生成");
        }
        try {
            String filename = StringUtils.isNotBlank(processReport.getInsReportCode())
                    ? processReport.getInsReportCode() + extension : file.getFileName().toString();
            String encodedName = URLEncoder.encode(filename, "UTF-8").replace("+", "%20");
            response.setContentType(contentType);
            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedName);
            response.setHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename");
            response.setHeader("download-filename", encodedName);
            response.setContentLengthLong(Files.size(file));
            try (InputStream inputStream = Files.newInputStream(file);
                 OutputStream outputStream = response.getOutputStream()) {
                IOUtils.copy(inputStream, outputStream);
                outputStream.flush();
            }
        } catch (IOException e) {
            throw new ErrorException("有章报告下载失败");
        }
    }
    @Override
    public void exportProcessReport(ProcessReportDto dto, HttpServletResponse response) {
        List<ProcessReportDto> processReports = processReportMapper.exportProcessReport();