ソースを参照

检验意见上报下载文件调整

xy 1 週間 前
コミット
2dec682e11

+ 3 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/BoilerTaskOrderPageReqVO.java

@@ -434,4 +434,7 @@ public class BoilerTaskOrderPageReqVO extends PageParam {
 
     @Schema(description = "是否已超期")
     private Boolean isOverdue;
+
+    private List<String> orderItemIds;
+
 }

+ 36 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/boilertaskorder/vo/InspectionOpinionFirstReportExcelVO.java

@@ -0,0 +1,36 @@
+package cn.start.tz.module.pressure2.controller.admin.boilertaskorder.vo;
+
+import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.annotation.write.style.ColumnWidth;
+import com.alibaba.excel.annotation.write.style.ContentRowHeight;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.time.LocalDate;
+
+@Schema(description = "管理后台 - 检验意见记录导出 Request VO")
+@Data
+@ExcelIgnoreUnannotated
+@ContentRowHeight(27)
+public class InspectionOpinionFirstReportExcelVO {
+
+    @ExcelProperty("使用单位")
+    private String useUnitName;
+
+    @ExcelProperty("意见通知书编号")
+    private String reportNo;
+
+    @ExcelProperty("意见书类型")
+    private String opinionType;
+
+    @ExcelProperty(value = "日期")
+    @ColumnWidth(25)
+    private LocalDate date;
+
+    @ExcelProperty("区域")
+    private String deDistrictName;
+
+    @ExcelProperty("设备类别")
+    private String equipmentType;
+}

+ 2 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/controller/admin/pipetaskorder/vo/PipeTaskOrderPageReqVO.java

@@ -437,4 +437,6 @@ public class PipeTaskOrderPageReqVO extends PageParam {
 
     @Schema(description = "是否已超期")
     private Boolean isOverdue;
+
+    private List<String> orderItemIds;
 }

+ 4 - 0
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/dal/mysql/pipetaskorderitem/PipeTaskOrderItemMapper.java

@@ -225,6 +225,10 @@ public interface PipeTaskOrderItemMapper extends BaseMapperX<PipeTaskOrderItemDO
             recordDOMPJLambdaWrapper.in(PipeTaskOrderItemDO::getId, reqVO.getIdList());
         }
 
+        if (!CollUtil.isEmpty(reqVO.getTaskOrderIds())) {
+            recordDOMPJLambdaWrapper.in(PipeTaskOrderDO::getId, reqVO.getTaskOrderIds());
+        }
+
         if (reqVO.getNotInReportType()!= null){
             recordDOMPJLambdaWrapper.ne(PipeTaskOrderItemReportDO::getReportType, reqVO.getNotInReportType());
         }

+ 205 - 2
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/boilertaskorder/BoilerTaskOrderInspectionServiceImpl.java

@@ -7,8 +7,12 @@ import cn.start.tz.framework.common.exception.ErrorCode;
 import cn.start.tz.framework.common.exception.ServiceException;
 import cn.start.tz.framework.common.pojo.PageResult;
 import cn.start.tz.framework.common.util.object.BeanUtils;
+import cn.start.tz.framework.common.util.http.HttpUtils;
 import cn.start.tz.framework.env.core.enums.EnvEnum;
+import cn.start.tz.framework.excel.core.handler.SelectSheetWriteHandler;
+import cn.start.tz.framework.ip.core.utils.AreaUtils;
 import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.infra.api.file.FileApi;
 import cn.start.tz.module.member.api.user.MemberUserApi;
 import cn.start.tz.module.member.api.user.dto.MemberUserReqVo;
 import cn.start.tz.module.member.api.user.dto.MemberUserRespDTO;
@@ -38,6 +42,9 @@ import cn.start.tz.module.system.api.dict.dto.DictDataRespDTO;
 import cn.start.tz.module.system.api.user.AdminUserApi;
 import cn.start.tz.module.system.api.user.dto.AdminUserRespDTO;
 import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
 import io.swagger.v3.oas.annotations.media.Schema;
@@ -54,6 +61,14 @@ import org.springframework.validation.annotation.Validated;
 import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 
 import static cn.start.tz.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.start.tz.framework.common.util.json.JsonUtils.getHandleDateValue;
@@ -71,6 +86,12 @@ public class BoilerTaskOrderInspectionServiceImpl implements BoilerTaskOrderInsp
     @Value("${tz.env.name}")
     private String env;
 
+    @Value("${tz.minio.host}")
+    private String minioHost;
+
+    @Resource
+    private FileApi fileApi;
+
     @Resource
     private RedissonClient redissonClient;
 
@@ -368,8 +389,190 @@ public class BoilerTaskOrderInspectionServiceImpl implements BoilerTaskOrderInsp
 
     @Override
     public String opinionReported(InspectionOpinionReportedVo inspectionOpinionReportedVo) {
-        //TODO
-        return "";
+        // 获取所有任务单设备报告
+        LambdaQueryWrapperX<BoilerTaskOrderItemReportDO> queryOrderItemReport = new LambdaQueryWrapperX<>();
+        queryOrderItemReport.in(BoilerTaskOrderItemReportDO::getOrderItemId, inspectionOpinionReportedVo.getOrderItemIds());
+        List<BoilerTaskOrderItemReportDO> taskOrderItemReportDOS = taskOrderItemReportMapper.selectList(queryOrderItemReport);
+
+        if (CollUtil.isEmpty(taskOrderItemReportDOS)) {
+            return "";
+        }
+
+        // 获取设备信息(包含equipCode和区域)
+        BoilerTaskOrderPageReqVO pageReqVO = new BoilerTaskOrderPageReqVO();
+        pageReqVO.setOrderItemIds(inspectionOpinionReportedVo.getOrderItemIds());
+        pageReqVO.setPageSize(1000);
+        PageResult<BoilerOrderItemPageRespVO> pageResult = boilerTaskOrderService.inspectionOpinionPage(pageReqVO);
+        List<BoilerOrderItemPageRespVO> taskOrderItemDOS = pageResult.getList();
+
+        Map<String, String> equipCodeMap = taskOrderItemDOS.stream()
+                .collect(Collectors.toMap(BoilerOrderItemPageRespVO::getId, BoilerOrderItemPageRespVO::getEquipCode, (v1, v2) -> v1));
+        Map<String, Integer> beDistrictMap = taskOrderItemDOS.stream()
+                .collect(Collectors.toMap(BoilerOrderItemPageRespVO::getId, BoilerOrderItemPageRespVO::getEquipDistrict, (v1, v2) -> v1));
+
+        // businessType=0: 仅获取意见通知书
+        if (Integer.valueOf(0).equals(inspectionOpinionReportedVo.getBusinessType())) {
+            List<BoilerTaskOrderItemReportDO> opinionReports = taskOrderItemReportDOS.stream()
+                    .filter(x -> Integer.valueOf(400).equals(x.getReportType()) && !Integer.valueOf(200).equals(x.getTaskStatus()))
+                    .toList();
+            return buildOpinionReportZip(opinionReports, equipCodeMap, beDistrictMap);
+        } else {
+            // businessType=2: 获取意见通知书 + 主报告结论
+            List<BoilerTaskOrderItemReportDO> opinionReports = taskOrderItemReportDOS.stream()
+                    .filter(x -> Integer.valueOf(400).equals(x.getReportType()) && !Integer.valueOf(200).equals(x.getTaskStatus()))
+                    .toList();
+            return buildOpinionReportZip(opinionReports, equipCodeMap, beDistrictMap);
+        }
+    }
+
+    private String buildOpinionReportZip(List<BoilerTaskOrderItemReportDO> reports,
+                                          Map<String, String> equipCodeMap,
+                                          Map<String, Integer> beDistrictMap) {
+        if (CollUtil.isEmpty(reports)) {
+            return "";
+        }
+
+        ByteArrayOutputStream zipBaos = new ByteArrayOutputStream();
+        try (ZipOutputStream zipOut = new ZipOutputStream(zipBaos)) {
+            List<InspectionOpinionFirstReportExcelVO> excelVOS = new ArrayList<>();
+
+            Map<String, List<BoilerTaskOrderItemReportDO>> groupedByOrderItem = reports.stream()
+                    .filter(r -> r.getOrderItemId() != null)
+                    .collect(Collectors.groupingBy(BoilerTaskOrderItemReportDO::getOrderItemId));
+
+            for (Map.Entry<String, List<BoilerTaskOrderItemReportDO>> entry : groupedByOrderItem.entrySet()) {
+                String orderItemId = entry.getKey();
+                List<BoilerTaskOrderItemReportDO> itemReports = entry.getValue();
+
+                String equipCode = equipCodeMap.getOrDefault(orderItemId, orderItemId);
+                Integer beDistrict = beDistrictMap.getOrDefault(orderItemId, -1);
+                String beDistrictName = AreaUtils.getAreaName(beDistrict);
+                if (beDistrictName == null || beDistrictName.isBlank()) {
+                    beDistrictName = "无区域";
+                }
+
+                for (BoilerTaskOrderItemReportDO report : itemReports) {
+                    if (report.getIssueUrl() != null && !report.getIssueUrl().isBlank()) {
+                        String reportUrl = minioHost + report.getIssueUrl();
+                        try {
+                            byte[] pdfBytes = HttpUtils.downloadPdfFromUrl(reportUrl);
+                            String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ").pdf";
+                            zipOut.putNextEntry(new ZipEntry(fileName));
+                            zipOut.write(pdfBytes);
+                            zipOut.closeEntry();
+                        } catch (Exception e) {
+                            log.error("下载PDF文件失败: {}", e.getMessage());
+                        }
+
+                        InspectionOpinionFirstReportExcelVO excelVO = new InspectionOpinionFirstReportExcelVO();
+                        excelVO.setDeDistrictName(beDistrictName);
+                        excelVO.setOpinionType(report.getReportName());
+                        excelVO.setEquipmentType("锅炉");
+                        if (report.getRatifyTime() != null) {
+                            excelVO.setDate(report.getRatifyTime().toLocalDate());
+                        }
+                        excelVO.setReportNo(report.getReportNo());
+                        excelVOS.add(excelVO);
+                    }
+
+                    if (report.getRectificationImage() != null && !report.getRectificationImage().isBlank()) {
+                        String[] imageUrls = report.getRectificationImage().split(",");
+                        for (String imageUrl : imageUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(imageUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + imageUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载图片文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                    if (report.getRectificationUrl() != null && !report.getRectificationUrl().isBlank()) {
+                        String[] attachmentUrls = report.getRectificationUrl().split(",");
+                        for (String attachmentUrl : attachmentUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(attachmentUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + attachmentUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载附件文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                    if (report.getRectificationVideo() != null && !report.getRectificationVideo().isBlank()) {
+                        String[] videoUrls = report.getRectificationVideo().split(",");
+                        for (String videoUrl : videoUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(videoUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + videoUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载视频文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                }
+            }
+
+            // 分区域生成资料清单Excel
+            Map<String, List<InspectionOpinionFirstReportExcelVO>> listMap = excelVOS.stream()
+                    .collect(Collectors.groupingBy(InspectionOpinionFirstReportExcelVO::getDeDistrictName));
+            for (Map.Entry<String, List<InspectionOpinionFirstReportExcelVO>> mapEntry : listMap.entrySet()) {
+                try {
+                    ByteArrayOutputStream excelBaos = new ByteArrayOutputStream();
+                    EasyExcel.write(excelBaos, InspectionOpinionFirstReportExcelVO.class)
+                            .autoCloseStream(false)
+                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                            .registerConverter(new LongStringConverter())
+                            .sheet("清单").doWrite(mapEntry.getValue());
+                    String fileName = "检验意见通知文件/" + mapEntry.getKey() + "/资料清单-" + mapEntry.getKey() + ".xlsx";
+                    zipOut.putNextEntry(new ZipEntry(fileName));
+                    zipOut.write(excelBaos.toByteArray());
+                    zipOut.closeEntry();
+                } catch (Exception e) {
+                    log.error("生成区域Excel失败: {}", e.getMessage());
+                }
+            }
+
+            // 总体清单Excel
+            try {
+                ByteArrayOutputStream excelBaos = new ByteArrayOutputStream();
+                EasyExcel.write(excelBaos, InspectionOpinionFirstReportExcelVO.class)
+                        .autoCloseStream(false)
+                        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                        .registerConverter(new LongStringConverter())
+                        .sheet("清单").doWrite(excelVOS);
+                zipOut.putNextEntry(new ZipEntry("检验意见通知清单.xlsx"));
+                zipOut.write(excelBaos.toByteArray());
+                zipOut.closeEntry();
+            } catch (Exception e) {
+                log.error("生成总体Excel失败: {}", e.getMessage());
+            }
+
+            zipOut.finish();
+
+            String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
+            String uuid = UUID.randomUUID() + ".zip";
+            String zipFilePath = fileApi.createFile("temp/" + datePath + "/" + uuid, zipBaos.toByteArray());
+            log.info("生成ZIP报告文件路径:{}", zipFilePath);
+            return zipFilePath;
+        } catch (IOException e) {
+            log.error("创建ZIP文件失败", e);
+            return "";
+        } finally {
+            try {
+                zipBaos.close();
+            } catch (IOException ignored) {}
+        }
     }
 
     @Override

+ 202 - 6
tz-module-pressure2/tz-module-pressure2-biz/src/main/java/cn/start/tz/module/pressure2/service/pipetaskorder/PipeTaskOrderInspectionServiceImpl.java

@@ -6,8 +6,12 @@ import cn.start.tz.framework.common.enums.TerminalEnum;
 import cn.start.tz.framework.common.exception.ErrorCode;
 import cn.start.tz.framework.common.exception.ServiceException;
 import cn.start.tz.framework.common.pojo.PageResult;
+import cn.start.tz.framework.common.util.http.HttpUtils;
 import cn.start.tz.framework.env.core.enums.EnvEnum;
+import cn.start.tz.framework.excel.core.handler.SelectSheetWriteHandler;
+import cn.start.tz.framework.ip.core.utils.AreaUtils;
 import cn.start.tz.framework.mybatis.core.query.LambdaQueryWrapperX;
+import cn.start.tz.module.infra.api.file.FileApi;
 import cn.start.tz.module.member.api.user.MemberUserApi;
 import cn.start.tz.module.member.api.user.dto.MemberUserReqVo;
 import cn.start.tz.module.member.api.user.dto.MemberUserRespDTO;
@@ -45,6 +49,9 @@ import cn.start.tz.module.system.api.dict.dto.DictDataRespDTO;
 import cn.start.tz.module.system.api.user.AdminUserApi;
 import cn.start.tz.module.system.api.user.dto.AdminUserRespDTO;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
 import jakarta.annotation.Resource;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -56,11 +63,16 @@ import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
 
 import java.time.LocalDateTime;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
+import java.util.*;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
 
 import static cn.start.tz.framework.common.exception.util.ServiceExceptionUtil.exception;
 import static cn.start.tz.framework.common.util.json.JsonUtils.getHandleDateValue;
@@ -77,6 +89,12 @@ public class PipeTaskOrderInspectionServiceImpl implements PipeTaskOrderInspecti
     @Value("${tz.env.name}")
     private String env;
 
+    @Value("${tz.minio.host}")
+    private String minioHost;
+
+    @Resource
+    private FileApi fileApi;
+
     @Resource
     private RedissonClient redissonClient;
 
@@ -378,8 +396,186 @@ public class PipeTaskOrderInspectionServiceImpl implements PipeTaskOrderInspecti
 
     @Override
     public String opinionReported(InspectionOpinionReportedVo inspectionOpinionReportedVo) {
-        //TODO
-        return "";
+
+        List<PipeTaskOrderItemDO> pipeTaskOrderItemDOS = taskOrderItemMapper.selectList(new LambdaQueryWrapperX<PipeTaskOrderItemDO>()
+                .in(PipeTaskOrderItemDO::getId, inspectionOpinionReportedVo.getOrderItemIds()));
+        List<String> orderIds = pipeTaskOrderItemDOS.stream().map(PipeTaskOrderItemDO::getOrderId).distinct().toList();
+
+        // 获取所有任务单设备报告
+        LambdaQueryWrapperX<PipeTaskOrderItemReportDO> queryOrderItemReport = new LambdaQueryWrapperX<>();
+        queryOrderItemReport.in(PipeTaskOrderItemReportDO::getOrderId, orderIds);
+        List<PipeTaskOrderItemReportDO> taskOrderItemReportDOS = taskOrderItemReportMapper.selectList(queryOrderItemReport);
+
+        if (CollUtil.isEmpty(taskOrderItemReportDOS)) {
+            return "";
+        }
+
+        // 获取设备信息(包含equipCode和区域)
+        PipeTaskOrderPageReqVO pageReqVO = new PipeTaskOrderPageReqVO();
+        pageReqVO.setTaskOrderIds(orderIds);
+        pageReqVO.setPageSize(1000);
+        PageResult<PipeOrderItemPageRespVO> pageResult = pipeTaskOrderService.inspectionOpinionPage(pageReqVO);
+        List<PipeOrderItemPageRespVO> taskOrderItemDOS = pageResult.getList();
+
+        Map<String, String> equipCodeMap = taskOrderItemDOS.stream()
+                .collect(Collectors.toMap(PipeOrderItemPageRespVO::getOrderId, PipeOrderItemPageRespVO::getProjectNo, (v1, v2) -> v1));
+        Map<String, Integer> beDistrictMap = taskOrderItemDOS.stream()
+                .collect(Collectors.toMap(PipeOrderItemPageRespVO::getOrderId, PipeOrderItemPageRespVO::getEquipDistrict, (v1, v2) -> v1));
+
+        List<PipeTaskOrderItemReportDO> opinionReports = taskOrderItemReportDOS.stream()
+                .filter(x -> Integer.valueOf(400).equals(x.getReportType()) && !Integer.valueOf(200).equals(x.getTaskStatus()))
+                .toList();
+        return buildOpinionReportZip(opinionReports, equipCodeMap, beDistrictMap);
+    }
+
+    private String buildOpinionReportZip(List<PipeTaskOrderItemReportDO> reports,
+                                          Map<String, String> equipCodeMap,
+                                          Map<String, Integer> beDistrictMap) {
+        if (CollUtil.isEmpty(reports)) {
+            return "";
+        }
+
+        ByteArrayOutputStream zipBaos = new ByteArrayOutputStream();
+        try (ZipOutputStream zipOut = new ZipOutputStream(zipBaos)) {
+            List<InspectionOpinionFirstReportExcelVO> excelVOS = new ArrayList<>();
+
+            Map<String, List<PipeTaskOrderItemReportDO>> groupedByOrder = reports.stream()
+                    .filter(r -> r.getOrderId() != null)
+                    .collect(Collectors.groupingBy(PipeTaskOrderItemReportDO::getOrderId));
+
+            for (Map.Entry<String, List<PipeTaskOrderItemReportDO>> entry : groupedByOrder.entrySet()) {
+                String orderId = entry.getKey();
+                List<PipeTaskOrderItemReportDO> itemReports = entry.getValue();
+
+                String equipCode = equipCodeMap.getOrDefault(orderId, orderId);
+                Integer beDistrict = beDistrictMap.getOrDefault(orderId, -1);
+                String beDistrictName = AreaUtils.getAreaName(beDistrict);
+                if (beDistrictName == null || beDistrictName.isBlank()) {
+                    beDistrictName = "无区域";
+                }
+
+                for (PipeTaskOrderItemReportDO report : itemReports) {
+                    if (report.getIssueUrl() != null && !report.getIssueUrl().isBlank()) {
+                        String reportUrl = minioHost + report.getIssueUrl();
+                        try {
+                            byte[] pdfBytes = HttpUtils.downloadPdfFromUrl(reportUrl);
+                            String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ").pdf";
+                            zipOut.putNextEntry(new ZipEntry(fileName));
+                            zipOut.write(pdfBytes);
+                            zipOut.closeEntry();
+                        } catch (Exception e) {
+                            log.error("下载PDF文件失败: {}", e.getMessage());
+                        }
+
+                        InspectionOpinionFirstReportExcelVO excelVO = new InspectionOpinionFirstReportExcelVO();
+                        excelVO.setDeDistrictName(beDistrictName);
+                        excelVO.setOpinionType(report.getReportName());
+                        excelVO.setEquipmentType("管道");
+                        if (report.getRatifyTime() != null) {
+                            excelVO.setDate(report.getRatifyTime().toLocalDate());
+                        }
+                        excelVO.setReportNo(report.getReportNo());
+                        excelVOS.add(excelVO);
+                    }
+
+                    if (report.getRectificationImage() != null && !report.getRectificationImage().isBlank()) {
+                        String[] imageUrls = report.getRectificationImage().split(",");
+                        for (String imageUrl : imageUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(imageUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + imageUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载图片文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                    if (report.getRectificationUrl() != null && !report.getRectificationUrl().isBlank()) {
+                        String[] attachmentUrls = report.getRectificationUrl().split(",");
+                        for (String attachmentUrl : attachmentUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(attachmentUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + attachmentUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载附件文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                    if (report.getRectificationVideo() != null && !report.getRectificationVideo().isBlank()) {
+                        String[] videoUrls = report.getRectificationVideo().split(",");
+                        for (String videoUrl : videoUrls) {
+                            try {
+                                byte[] data = fileApi.getFileByPath(videoUrl).getData();
+                                String fileName = "检验意见通知文件/" + beDistrictName + "/" + report.getReportNo() + "(" + equipCode + ")/" + videoUrl;
+                                zipOut.putNextEntry(new ZipEntry(fileName));
+                                zipOut.write(data);
+                                zipOut.closeEntry();
+                            } catch (Exception e) {
+                                log.error("下载视频文件失败: {}", e.getMessage());
+                            }
+                        }
+                    }
+
+                }
+            }
+
+            // 分区域生成资料清单Excel
+            Map<String, List<InspectionOpinionFirstReportExcelVO>> listMap = excelVOS.stream()
+                    .collect(Collectors.groupingBy(InspectionOpinionFirstReportExcelVO::getDeDistrictName));
+            for (Map.Entry<String, List<InspectionOpinionFirstReportExcelVO>> mapEntry : listMap.entrySet()) {
+                try {
+                    ByteArrayOutputStream excelBaos = new ByteArrayOutputStream();
+                    EasyExcel.write(excelBaos, InspectionOpinionFirstReportExcelVO.class)
+                            .autoCloseStream(false)
+                            .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                            .registerConverter(new LongStringConverter())
+                            .sheet("清单").doWrite(mapEntry.getValue());
+                    String fileName = "检验意见通知文件/" + mapEntry.getKey() + "/资料清单-" + mapEntry.getKey() + ".xlsx";
+                    zipOut.putNextEntry(new ZipEntry(fileName));
+                    zipOut.write(excelBaos.toByteArray());
+                    zipOut.closeEntry();
+                } catch (Exception e) {
+                    log.error("生成区域Excel失败: {}", e.getMessage());
+                }
+            }
+
+            // 总体清单Excel
+            try {
+                ByteArrayOutputStream excelBaos = new ByteArrayOutputStream();
+                EasyExcel.write(excelBaos, InspectionOpinionFirstReportExcelVO.class)
+                        .autoCloseStream(false)
+                        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
+                        .registerConverter(new LongStringConverter())
+                        .sheet("清单").doWrite(excelVOS);
+                zipOut.putNextEntry(new ZipEntry("检验意见通知清单.xlsx"));
+                zipOut.write(excelBaos.toByteArray());
+                zipOut.closeEntry();
+            } catch (Exception e) {
+                log.error("生成总体Excel失败: {}", e.getMessage());
+            }
+
+            zipOut.finish();
+
+            String datePath = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy/MM/dd"));
+            String uuid = UUID.randomUUID() + ".zip";
+            String zipFilePath = fileApi.createFile("temp/" + datePath + "/" + uuid, zipBaos.toByteArray());
+            log.info("生成ZIP报告文件路径:{}", zipFilePath);
+            return zipFilePath;
+        } catch (IOException e) {
+            log.error("创建ZIP文件失败", e);
+            return "";
+        } finally {
+            try {
+                zipBaos.close();
+            } catch (IOException ignored) {}
+        }
     }
 
     @Override