Commit 291db7430e54561f2e41ae2a52181295a4f8964e

Authored by wangqian
1 parent 9ce96e13

险情管理导出

Showing 33 changed files with 1966 additions and 176 deletions
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/ImageListConverter.java
@@ -7,27 +7,17 @@ import cn.idev.excel.metadata.data.ImageData; @@ -7,27 +7,17 @@ import cn.idev.excel.metadata.data.ImageData;
7 import cn.idev.excel.metadata.data.ReadCellData; 7 import cn.idev.excel.metadata.data.ReadCellData;
8 import cn.idev.excel.metadata.data.WriteCellData; 8 import cn.idev.excel.metadata.data.WriteCellData;
9 import cn.idev.excel.metadata.property.ExcelContentProperty; 9 import cn.idev.excel.metadata.property.ExcelContentProperty;
10 -import cn.idev.excel.util.FileUtils;  
11 import cn.idev.excel.util.ListUtils; 10 import cn.idev.excel.util.ListUtils;
12 import lombok.extern.slf4j.Slf4j; 11 import lombok.extern.slf4j.Slf4j;
13 -import org.springframework.util.StringUtils;  
14 12
15 -import java.io.ByteArrayOutputStream;  
16 -import java.io.File;  
17 -import java.io.InputStream;  
18 -import java.net.HttpURLConnection;  
19 -import java.net.URL;  
20 import java.util.List; 13 import java.util.List;
21 14
22 @Slf4j 15 @Slf4j
23 -public class ImageListConverter implements Converter<List<String>> { 16 +public class ImageListConverter implements Converter<List<byte[]>> {
24 17
25 // 横向排列:每张图片宽度 + 间距 18 // 横向排列:每张图片宽度 + 间距
26 private static final int IMAGE_WIDTH = 120; 19 private static final int IMAGE_WIDTH = 120;
27 private static final int IMAGE_GAP = 15; 20 private static final int IMAGE_GAP = 15;
28 - private static final int MAX_IMAGE_SIZE = 10 * 1024 * 1024;  
29 - private static final int CONNECT_TIMEOUT = 5000;  
30 - private static final int READ_TIMEOUT = 10000;  
31 21
32 @Override 22 @Override
33 public Class<?> supportJavaTypeKey() { 23 public Class<?> supportJavaTypeKey() {
@@ -40,28 +30,27 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; { @@ -40,28 +30,27 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; {
40 } 30 }
41 31
42 @Override 32 @Override
43 - public List<String> convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty, 33 + public List<byte[]> convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
44 GlobalConfiguration globalConfiguration) { 34 GlobalConfiguration globalConfiguration) {
45 return null; 35 return null;
46 } 36 }
47 37
  38 + /**
  39 + * 这里改成接收 List<byte[]>
  40 + */
48 @Override 41 @Override
49 - public WriteCellData<?> convertToExcelData(List<String> imageList, ExcelContentProperty contentProperty, 42 + public WriteCellData<?> convertToExcelData(List<byte[]> imageByteList, ExcelContentProperty contentProperty,
50 GlobalConfiguration globalConfiguration) { 43 GlobalConfiguration globalConfiguration) {
51 try { 44 try {
52 - if (imageList == null || imageList.isEmpty()) { 45 + if (imageByteList == null || imageByteList.isEmpty()) {
53 return new WriteCellData<>("无图片"); 46 return new WriteCellData<>("无图片");
54 } 47 }
55 48
56 List<ImageData> imageDataList = ListUtils.newArrayList(); 49 List<ImageData> imageDataList = ListUtils.newArrayList();
57 int leftOffset = 0; // 横向偏移量 50 int leftOffset = 0; // 横向偏移量
58 51
59 - for (String path : imageList) {  
60 - if (!StringUtils.hasText(path)) continue;  
61 -  
62 - byte[] imageBytes = path.startsWith("http") ? loadNetworkImage(path) : loadLocalImage(path);  
63 - if (imageBytes == null || imageBytes.length == 0 || imageBytes.length > MAX_IMAGE_SIZE) {  
64 - log.warn("跳过无效图片:{}", path); 52 + for (byte[] imageBytes : imageByteList) {
  53 + if (imageBytes == null || imageBytes.length == 0) {
65 continue; 54 continue;
66 } 55 }
67 56
@@ -69,11 +58,11 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; { @@ -69,11 +58,11 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; {
69 imageData.setImage(imageBytes); 58 imageData.setImage(imageBytes);
70 imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG); 59 imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
71 60
72 - // ====================== 核心:横向偏移 ====================== 61 + // 横向排列
73 imageData.setLeft(leftOffset); 62 imageData.setLeft(leftOffset);
74 - // =============================================================  
75 -  
76 imageDataList.add(imageData); 63 imageDataList.add(imageData);
  64 +
  65 + // 下一张图片偏移
77 leftOffset += IMAGE_WIDTH + IMAGE_GAP; 66 leftOffset += IMAGE_WIDTH + IMAGE_GAP;
78 } 67 }
79 68
@@ -88,35 +77,8 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; { @@ -88,35 +77,8 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; {
88 return writeCellData; 77 return writeCellData;
89 78
90 } catch (Exception e) { 79 } catch (Exception e) {
91 - log.error("图片导出失败", e); 80 + log.error("图片导出异常", e);
92 return new WriteCellData<>("图片加载失败"); 81 return new WriteCellData<>("图片加载失败");
93 } 82 }
94 } 83 }
95 -  
96 - private byte[] loadLocalImage(String imagePath) throws Exception {  
97 - File file = new File(imagePath);  
98 - if (!file.exists() || !file.isFile()) {  
99 - log.warn("本地图片不存在:{}", imagePath);  
100 - return null;  
101 - }  
102 - return FileUtils.readFileToByteArray(file);  
103 - }  
104 -  
105 - private byte[] loadNetworkImage(String imageUrl) throws Exception {  
106 - URL url = new URL(imageUrl);  
107 - HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
108 - conn.setConnectTimeout(CONNECT_TIMEOUT);  
109 - conn.setReadTimeout(READ_TIMEOUT);  
110 - try {  
111 - if (conn.getResponseCode() != 200) return null;  
112 - try (InputStream in = conn.getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream()) {  
113 - byte[] buffer = new byte[4096];  
114 - int len;  
115 - while ((len = in.read(buffer)) != -1) out.write(buffer, 0, len);  
116 - return out.toByteArray();  
117 - }  
118 - } finally {  
119 - conn.disconnect();  
120 - }  
121 - }  
122 } 84 }
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/convert/ImageStringConverter.java 0 → 100644
  1 +package com.zteits.urbanops.framework.excel.core.convert;
  2 +
  3 +import cn.idev.excel.converters.Converter;
  4 +import cn.idev.excel.enums.CellDataTypeEnum;
  5 +import cn.idev.excel.metadata.GlobalConfiguration;
  6 +import cn.idev.excel.metadata.data.ImageData;
  7 +import cn.idev.excel.metadata.data.ReadCellData;
  8 +import cn.idev.excel.metadata.data.WriteCellData;
  9 +import cn.idev.excel.metadata.property.ExcelContentProperty;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +
  12 +@Slf4j
  13 +public class ImageStringConverter implements Converter<byte[]> {
  14 +
  15 + @Override
  16 + public Class<byte[]> supportJavaTypeKey() {
  17 + return byte[].class;
  18 + }
  19 +
  20 + @Override
  21 + public CellDataTypeEnum supportExcelTypeKey() {
  22 + return CellDataTypeEnum.EMPTY;
  23 + }
  24 +
  25 + @Override
  26 + public byte[] convertToJavaData(ReadCellData cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
  27 + return null;
  28 + }
  29 +
  30 + @Override
  31 + public WriteCellData<?> convertToExcelData(byte[] imageBytes, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
  32 + try {
  33 + if (imageBytes == null || imageBytes.length == 0) {
  34 + return new WriteCellData<>("无图片");
  35 + }
  36 +
  37 + ImageData imageData = new ImageData();
  38 + imageData.setImage(imageBytes);
  39 + imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
  40 +
  41 + WriteCellData<byte[]> cellData = new WriteCellData<>();
  42 + cellData.setType(CellDataTypeEnum.EMPTY);
  43 + cellData.setImageDataList(java.util.Collections.singletonList(imageData));
  44 + return cellData;
  45 + } catch (Exception e) {
  46 + return new WriteCellData<>("图片异常");
  47 + }
  48 + }
  49 +}
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/handler/HeadMergeHandler.java 0 → 100644
  1 +package com.zteits.urbanops.framework.excel.core.handler;
  2 +
  3 +import cn.idev.excel.write.handler.SheetWriteHandler;
  4 +import cn.idev.excel.write.handler.context.SheetWriteHandlerContext;
  5 +import cn.idev.excel.write.metadata.holder.WriteSheetHolder;
  6 +import org.apache.poi.ss.usermodel.Sheet;
  7 +import org.apache.poi.ss.util.CellRangeAddress;
  8 +import java.util.Arrays;
  9 +import java.util.List;
  10 +
  11 +/**
  12 + * 表头合并处理器(适配低版本 idev.excel,无 getSheet 方法)
  13 + * 纯表头场景、空数据也能正常执行,无需额外空行
  14 + */
  15 +public class HeadMergeHandler implements SheetWriteHandler {
  16 +
  17 + private final List<CellRangeAddress> regions;
  18 +
  19 + public HeadMergeHandler(CellRangeAddress... regions) {
  20 + this.regions = Arrays.asList(regions);
  21 + }
  22 +
  23 + @Override
  24 + public void afterSheetCreate(SheetWriteHandlerContext context) {
  25 + // 核心修正:从 WriteSheetHolder 拿 Sheet
  26 + WriteSheetHolder writeSheetHolder = context.getWriteSheetHolder();
  27 + if (writeSheetHolder == null) {
  28 + return;
  29 + }
  30 + Sheet sheet = writeSheetHolder.getSheet();
  31 +
  32 + for (CellRangeAddress area : regions) {
  33 + if (isValidMergedRegion(area)) {
  34 + sheet.addMergedRegionUnsafe(area);
  35 + }
  36 + }
  37 + }
  38 +
  39 + /**
  40 + * 过滤单单元格无效合并
  41 + */
  42 + private boolean isValidMergedRegion(CellRangeAddress area) {
  43 + int rowCnt = area.getLastRow() - area.getFirstRow() + 1;
  44 + int colCnt = area.getLastColumn() - area.getFirstColumn() + 1;
  45 + return rowCnt >= 2 || colCnt >= 2;
  46 + }
  47 +}
urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/util/ExcelUtils.java
1 package com.zteits.urbanops.framework.excel.core.util; 1 package com.zteits.urbanops.framework.excel.core.util;
2 2
  3 +import cn.hutool.core.collection.CollectionUtil;
  4 +import cn.idev.excel.ExcelWriter;
3 import cn.idev.excel.FastExcelFactory; 5 import cn.idev.excel.FastExcelFactory;
4 import cn.idev.excel.converters.longconverter.LongStringConverter; 6 import cn.idev.excel.converters.longconverter.LongStringConverter;
  7 +import cn.idev.excel.write.builder.ExcelWriterBuilder;
5 import com.zteits.urbanops.framework.common.util.http.HttpUtils; 8 import com.zteits.urbanops.framework.common.util.http.HttpUtils;
6 import com.zteits.urbanops.framework.excel.core.handler.ColumnWidthMatchStyleStrategy; 9 import com.zteits.urbanops.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
  10 +import com.zteits.urbanops.framework.excel.core.handler.HeadMergeHandler;
7 import com.zteits.urbanops.framework.excel.core.handler.SelectSheetWriteHandler; 11 import com.zteits.urbanops.framework.excel.core.handler.SelectSheetWriteHandler;
8 import jakarta.servlet.http.HttpServletResponse; 12 import jakarta.servlet.http.HttpServletResponse;
  13 +import org.apache.poi.ss.util.CellRangeAddress;
9 import org.springframework.web.multipart.MultipartFile; 14 import org.springframework.web.multipart.MultipartFile;
10 15
  16 +import java.io.File;
  17 +import java.io.FileOutputStream;
11 import java.io.IOException; 18 import java.io.IOException;
12 import java.util.List; 19 import java.util.List;
13 20
@@ -39,7 +46,8 @@ public class ExcelUtils { @@ -39,7 +46,8 @@ public class ExcelUtils {
39 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度 46 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
40 .sheet(sheetName).doWrite(data); 47 .sheet(sheetName).doWrite(data);
41 // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了 48 // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
42 - response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename)); 49 + //response.addHeader("Content-Disposition", "attachment;filename=" + HttpUtils.encodeUtf8(filename));
  50 + response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + HttpUtils.encodeUtf8(filename));
43 response.setContentType("application/vnd.ms-excel;charset=UTF-8"); 51 response.setContentType("application/vnd.ms-excel;charset=UTF-8");
44 } 52 }
45 53
@@ -74,4 +82,82 @@ public class ExcelUtils { @@ -74,4 +82,82 @@ public class ExcelUtils {
74 .sheet(0) 82 .sheet(0)
75 .doReadSync(); 83 .doReadSync();
76 } 84 }
  85 +
  86 +
  87 + /**
  88 + * 导出多级表头,将列表以 Excel 响应给前端
  89 + *
  90 + * @param response 响应
  91 + * @param filename 文件名
  92 + * @param sheetName Excel sheet 名
  93 + * @param head Excel head 头
  94 + * @param data 数据列表哦
  95 + * @param <T> 泛型,保证 head 和 data 类型的一致性
  96 + * @throws IOException 写入失败的情况
  97 + */
  98 + public static <T> void writeEmergencyTask(HttpServletResponse response, String filename, String sheetName,
  99 + Class<T> head, List<T> data, List<List<String>> headList, List<CellRangeAddress> mergeList) throws IOException {
  100 + FastExcelFactory.write(response.getOutputStream())
  101 + .head(headList) //动态自定义3行表头
  102 + .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
  103 + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
  104 + .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
  105 + .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
  106 + // 注册表头合并
  107 + .registerWriteHandler(new HeadMergeHandler(mergeList.toArray(new CellRangeAddress[0])))
  108 + .sheet(sheetName).doWrite(data);
  109 + // 设置 header 和 contentType。写在最后的原因是,避免报错时,响应 contentType 已经被修改了
  110 + response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + HttpUtils.encodeUtf8(filename));
  111 + response.setContentType("application/vnd.ms-excel;charset=UTF-8");
  112 +
  113 + }
  114 +
  115 + /**
  116 + * 初始化Excel、写入表头,返回全局复用的 ExcelWriter
  117 + * 不再返回 OutputStream,彻底解决多次创建写入器导致文件损坏
  118 + */
  119 + public static <T> ExcelWriter initEmergencyTaskExcel(File targetFile,
  120 + String fileName,
  121 + String sheetName,
  122 + Class<T> head,
  123 + List<List<String>> headList,
  124 + List<CellRangeAddress> mergeList) throws IOException {
  125 + // 目录创建逻辑不变
  126 + File parentFile = targetFile.getParentFile();
  127 + if (!parentFile.exists()) {
  128 + boolean mkdirs = parentFile.mkdirs();
  129 + if (!mkdirs) {
  130 + throw new IOException("目录创建失败:" + parentFile.getAbsolutePath());
  131 + }
  132 + }
  133 +
  134 + // 输出流交给 ExcelWriter
  135 + FileOutputStream fos = new FileOutputStream(targetFile);
  136 + ExcelWriterBuilder writeBuilder = null;
  137 + if (CollectionUtil.isNotEmpty(headList)) {
  138 + writeBuilder = FastExcelFactory.write(fos)
  139 + .head(headList) //动态自定义3行表头
  140 + .autoCloseStream(true) // 自动关闭
  141 + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
  142 + .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
  143 + .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
  144 + .registerWriteHandler(new HeadMergeHandler(mergeList.toArray(new CellRangeAddress[0])));
  145 + } else {
  146 + writeBuilder = FastExcelFactory.write(fos, head)
  147 + .autoCloseStream(true) // 自动关闭
  148 + .registerWriteHandler(new ColumnWidthMatchStyleStrategy()) // 基于 column 长度,自动适配。最大 255 宽度
  149 + .registerWriteHandler(new SelectSheetWriteHandler(head)) // 基于固定 sheet 实现下拉框
  150 + .registerConverter(new LongStringConverter()); // 避免 Long 类型丢失精度
  151 + }
  152 + ExcelWriter excelWriter = writeBuilder.build();
  153 + return excelWriter;
  154 + }
  155 + /**
  156 + * 最终收尾:刷写文件尾、释放资源(替代手动close流)
  157 + */
  158 + public static void finishExcel(ExcelWriter excelWriter) {
  159 + if (excelWriter != null) {
  160 + excelWriter.finish();
  161 + }
  162 + }
77 } 163 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/EmergencyTaskController.java
1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask; 1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask;
2 2
3 -import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO;  
4 -import org.springframework.util.CollectionUtils;  
5 -import org.springframework.web.bind.annotation.*; 3 +import cn.hutool.core.collection.CollectionUtil;
  4 +import com.zteits.urbanops.framework.apilog.core.annotation.ApiAccessLog;
  5 +import com.zteits.urbanops.framework.common.pojo.CommonResult;
  6 +import com.zteits.urbanops.framework.common.pojo.PageParam;
  7 +import com.zteits.urbanops.framework.common.pojo.PageResult;
  8 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskApprovalReqVO;
  9 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskPageReqVO;
  10 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskRespVO;
  11 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskSaveReqVO;
  12 +import com.zteits.urbanops.module.garden.service.emergencytask.*;
  13 +import com.zteits.urbanops.module.garden.service.filedownload.ExcelExportDispatchService;
  14 +import com.zteits.urbanops.module.infra.service.file.FileConfigService;
  15 +import com.zteits.urbanops.module.infra.service.file.FileService;
  16 +import io.swagger.v3.oas.annotations.Operation;
  17 +import io.swagger.v3.oas.annotations.Parameter;
  18 +import io.swagger.v3.oas.annotations.tags.Tag;
6 import jakarta.annotation.Resource; 19 import jakarta.annotation.Resource;
7 -import org.springframework.validation.annotation.Validated; 20 +import jakarta.validation.Valid;
  21 +import lombok.extern.slf4j.Slf4j;
8 import org.springframework.security.access.prepost.PreAuthorize; 22 import org.springframework.security.access.prepost.PreAuthorize;
9 -import io.swagger.v3.oas.annotations.tags.Tag;  
10 -import io.swagger.v3.oas.annotations.Parameter;  
11 -import io.swagger.v3.oas.annotations.Operation;  
12 -  
13 -import jakarta.validation.*;  
14 -import jakarta.servlet.http.*;  
15 -import java.util.*;  
16 -import java.io.IOException;  
17 -import java.util.stream.Collectors; 23 +import org.springframework.validation.annotation.Validated;
  24 +import org.springframework.web.bind.annotation.*;
18 25
19 -import com.zteits.urbanops.framework.common.pojo.PageParam;  
20 -import com.zteits.urbanops.framework.common.pojo.PageResult;  
21 -import com.zteits.urbanops.framework.common.pojo.CommonResult; 26 +import java.util.List;
22 27
  28 +import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
23 import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; 29 import static com.zteits.urbanops.framework.common.pojo.CommonResult.success;
24 30
25 -import com.zteits.urbanops.framework.excel.core.util.ExcelUtils;  
26 -  
27 -import com.zteits.urbanops.framework.apilog.core.annotation.ApiAccessLog;  
28 -import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.*;  
29 -  
30 -import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.*;  
31 -import com.zteits.urbanops.module.garden.service.emergencytask.EmergencyTaskService;  
32 -  
33 @Tag(name = "管理后台 - 抢险任务主") 31 @Tag(name = "管理后台 - 抢险任务主")
34 @RestController 32 @RestController
35 @RequestMapping("/garden/emergency-task") 33 @RequestMapping("/garden/emergency-task")
36 @Validated 34 @Validated
  35 +@Slf4j
37 public class EmergencyTaskController { 36 public class EmergencyTaskController {
38 37
39 @Resource 38 @Resource
40 private EmergencyTaskService emergencyTaskService; 39 private EmergencyTaskService emergencyTaskService;
  40 + @Resource
  41 + private FileService fileService;
  42 + @Resource
  43 + private FileConfigService fileConfigService;
  44 + @Resource
  45 + private ExcelExportDispatchService dispatchService;
  46 + @Resource
  47 + private EmergencyExportHandler emergencyExportHandler;
  48 + @Resource
  49 + private EmergencyExportNewHandler emergencyExportNewHandler;
41 50
42 @PostMapping("/create") 51 @PostMapping("/create")
43 @Operation(summary = "创建抢险任务主") 52 @Operation(summary = "创建抢险任务主")
@@ -100,81 +109,42 @@ public class EmergencyTaskController { @@ -100,81 +109,42 @@ public class EmergencyTaskController {
100 @Operation(summary = "导出抢险任务主 Excel") 109 @Operation(summary = "导出抢险任务主 Excel")
101 @PreAuthorize("@ss.hasPermission('garden:emergency-task:export')") 110 @PreAuthorize("@ss.hasPermission('garden:emergency-task:export')")
102 @ApiAccessLog(operateType = EXPORT) 111 @ApiAccessLog(operateType = EXPORT)
103 - public void exportEmergencyTaskExcel(@Valid EmergencyTaskPageReqVO pageReqVO,  
104 - HttpServletResponse response) throws IOException { 112 + public CommonResult<String> exportEmergencyTaskExcel(@Valid EmergencyTaskPageReqVO pageReqVO) {
105 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE); 113 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
106 List<EmergencyTaskRespVO> list = emergencyTaskService.getEmergencyTaskPage(pageReqVO).getList(); 114 List<EmergencyTaskRespVO> list = emergencyTaskService.getEmergencyTaskPage(pageReqVO).getList();
107 - List<EmergencyTaskExcleRespVO> respList = new ArrayList<>();  
108 -  
109 - //险情类型 1:树木倒伏 2:树木折枝  
110 - List<DictDataRespDTO> dictDataList = emergencyTaskService.getDictDataList("hazard_type");  
111 -  
112 - //险情危害 1:砸车 2:砸房 3:砸人 4:无  
113 - List<DictDataRespDTO> hazardRiskList = emergencyTaskService.getDictDataList("hazard_risk");  
114 - //树木权属 1:专业权属 2:街道权属 3:其他  
115 - List<DictDataRespDTO> treePermissionsList = emergencyTaskService.getDictDataList("tree_permissions");  
116 - //机械类型 1:三轮车;2:货车;3:挖掘机  
117 - List<DictDataRespDTO> mechanicalTypeNameList = emergencyTaskService.getDictDataList("mechanical_type_name");  
118 -  
119 - //案件状态 1-待提交 2-待审核 3-驳回  
120 - List<DictDataRespDTO> rescueStatusList = emergencyTaskService.getDictDataList("rescue_status");  
121 115
122 -  
123 - list.forEach(emergencyTask -> {  
124 - EmergencyTaskExcleRespVO emergencyTaskExcleRespVO = new EmergencyTaskExcleRespVO();  
125 - emergencyTaskExcleRespVO.setTaskNo(emergencyTask.getTaskNo());  
126 - emergencyTaskExcleRespVO.setDiscoveryTime(emergencyTask.getDiscoveryTime());  
127 - emergencyTaskExcleRespVO.setEmergencyTypeName(getLabel(dictDataList, emergencyTask.getEmergencyTypeId()));  
128 - emergencyTaskExcleRespVO.setEmergencyHarmName(getLabel(hazardRiskList, emergencyTask.getEmergencyHarmId()));  
129 - emergencyTaskExcleRespVO.setTreeOwnershipName(getLabel(treePermissionsList, emergencyTask.getTreeOwnershipId()));  
130 - emergencyTaskExcleRespVO.setEmergencyLocation(emergencyTask.getEmergencyLocation());  
131 - emergencyTaskExcleRespVO.setReporter(emergencyTask.getReporter());  
132 - emergencyTaskExcleRespVO.setContactPhone(emergencyTask.getContactPhone());  
133 - emergencyTaskExcleRespVO.setEmergencyImg(emergencyTask.getEmergencyImg());  
134 - emergencyTaskExcleRespVO.setTreeSpecies(emergencyTask.getTreeSpecies());  
135 - emergencyTaskExcleRespVO.setTreeSpec(emergencyTask.getTreeSpec());  
136 - emergencyTaskExcleRespVO.setRescueStartTime(emergencyTask.getRescueStartTime());  
137 - emergencyTaskExcleRespVO.setRescueCompleteTime(emergencyTask.getRescueCompleteTime());  
138 - emergencyTaskExcleRespVO.setPersonnelCount(emergencyTask.getPersonnelCount());  
139 - emergencyTaskExcleRespVO.setEmergencyEndImg(emergencyTask.getEmergencyEndImg());  
140 - emergencyTaskExcleRespVO.setRemarks(emergencyTask.getRemarks());  
141 - emergencyTaskExcleRespVO.setBusiStatus(getLabel(rescueStatusList, emergencyTask.getBusiStatus()));  
142 - emergencyTaskExcleRespVO.setCompanyName(emergencyTask.getCompanyName());  
143 - emergencyTaskExcleRespVO.setApprovalUserName(emergencyTask.getApprovalUserName());  
144 - emergencyTaskExcleRespVO.setApprovalRemarks(emergencyTask.getApprovalRemarks());  
145 - emergencyTaskExcleRespVO.setCreatorName(emergencyTask.getCreatorName());  
146 - emergencyTaskExcleRespVO.setCreateTime(emergencyTask.getCreateTime());  
147 - emergencyTaskExcleRespVO.setUpdaterName(emergencyTask.getUpdaterName());  
148 - List<String> machineryList = new ArrayList<>();  
149 - if(CollectionUtils.isEmpty(emergencyTask.getEmergencyMachineryList())){  
150 - emergencyTaskExcleRespVO.setMachineryInfo(Collections.emptyList());  
151 - }else{  
152 - emergencyTask.getEmergencyMachineryList().forEach(emergencyMachinery -> {  
153 - String machineryName = getLabel(mechanicalTypeNameList, emergencyMachinery.getMachineryNameId());  
154 - Integer machineryCount = emergencyMachinery.getMachineryCount();  
155 - machineryList.add(machineryName + ":" + machineryCount);  
156 - });  
157 - emergencyTaskExcleRespVO.setMachineryInfo(machineryList);  
158 - }  
159 - respList.add(emergencyTaskExcleRespVO);  
160 - });  
161 -  
162 -  
163 - // 导出 Excel  
164 - ExcelUtils.write(response, "抢险任务主.xls", "数据", EmergencyTaskExcleRespVO.class, respList); 116 + // 无数据直接返回提示
  117 + if (CollectionUtil.isEmpty(list)) {
  118 + return CommonResult.error(1 ,"暂无数据,无法导出");
  119 + }
  120 + // 2. 调用调度服务创建任务
  121 + Long taskId = dispatchService.createExportTask(
  122 + "EmergencyTaskExcle",
  123 + list,
  124 + emergencyExportHandler
  125 + );
  126 + // 直接返回成功通知
  127 + return CommonResult.success("导出任务已提交,数据正在后台生成,请稍后下载");
165 } 128 }
166 129
167 - private String getLabel(List<DictDataRespDTO> dictDataList, Integer id){  
168 - if(CollectionUtils.isEmpty(dictDataList)){  
169 - return ""; 130 + @GetMapping("/export-excel-new")
  131 + @Operation(summary = "抢险案件表 Excel")
  132 + @PreAuthorize("@ss.hasPermission('garden:emergency-task:export')")
  133 + @ApiAccessLog(operateType = EXPORT)
  134 + public CommonResult<String> exportEmergencyTaskExcelNew(@Valid EmergencyTaskPageReqVO pageReqVO){
  135 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  136 + List<EmergencyTaskRespVO> list = emergencyTaskService.getEmergencyTaskPage(pageReqVO).getList();
  137 + if (CollectionUtil.isEmpty(list)) {
  138 + return null;
170 } 139 }
171 - Map<String, String> nameMap = dictDataList.stream()  
172 - .collect(Collectors.toMap(  
173 - DictDataRespDTO::getValue, // Key=id  
174 - DictDataRespDTO::getLabel // Value=name  
175 - ));  
176 -  
177 - return nameMap.get(String.valueOf( id)); 140 + // 创建异步任务
  141 + // 2. 调用调度服务创建任务
  142 + Long taskId = dispatchService.createExportTask(
  143 + "EmergencyTaskExcleNew",
  144 + list,
  145 + emergencyExportNewHandler
  146 + );
  147 + // 返回成功提示
  148 + return success("导出任务已提交,数据正在后台生成,请稍后下载");
178 } 149 }
179 -  
180 } 150 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskExcleNewRespVO.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo;
  2 +
  3 +import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
  4 +import cn.idev.excel.annotation.ExcelProperty;
  5 +import com.zteits.urbanops.framework.excel.core.convert.ImageStringConverter;
  6 +import io.swagger.v3.oas.annotations.media.Schema;
  7 +import lombok.Data;
  8 +
  9 +import java.time.LocalDateTime;
  10 +
  11 +@Schema(description = "管理后台 - 抢险任务主 Response VO")
  12 +@Data
  13 +@ExcelIgnoreUnannotated
  14 +public class EmergencyTaskExcleNewRespVO {
  15 +
  16 + @Schema(description = "任务编号(唯一标识,如生成规则:TASK+时间戳)", requiredMode = Schema.RequiredMode.REQUIRED)
  17 + @ExcelProperty("案件编号")
  18 + private String taskNo;
  19 +
  20 + @Schema(description = "归属(一级部门id)", requiredMode = Schema.RequiredMode.REQUIRED, example = "20800")
  21 + @ExcelProperty("归属公司")
  22 + private String companyName;
  23 +
  24 + @Schema(description = "险情地点", requiredMode = Schema.RequiredMode.REQUIRED)
  25 + @ExcelProperty("险情地点")
  26 + private String emergencyLocation;
  27 +
  28 + @Schema(description = "发现险情时间", requiredMode = Schema.RequiredMode.REQUIRED)
  29 + @ExcelProperty("发现险情时间")
  30 + private LocalDateTime discoveryTime;
  31 +
  32 + @Schema(description = "险情类型(如:树木倒伏)", requiredMode = Schema.RequiredMode.REQUIRED, example = "5209")
  33 + @ExcelProperty("险情类型")
  34 + private String emergencyTypeName;
  35 +
  36 + @Schema(description = "树木权属(如:专业权属)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19498")
  37 + @ExcelProperty("树木权属")
  38 + private String treeOwnershipName;
  39 +
  40 + @Schema(description = "险情危害(如:无)", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
  41 + @ExcelProperty("险情危害")
  42 + private String emergencyHarmName;
  43 +
  44 + @Schema(description = "险情危害(如:无)", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
  45 + @ExcelProperty("险情危害")
  46 + private Integer emergencyHarmId;
  47 +
  48 + @Schema(description = "数量/株", requiredMode = Schema.RequiredMode.REQUIRED, example = "3371")
  49 + @ExcelProperty("数量/株")
  50 + private Integer emergencyHarmNum;
  51 +
  52 + @Schema(description = "抢险开始时间", requiredMode = Schema.RequiredMode.REQUIRED)
  53 + @ExcelProperty("抢险开始时间")
  54 + private LocalDateTime rescueStartTime;
  55 +
  56 + @Schema(description = "树种(如:国槐)")
  57 + @ExcelProperty("树种")
  58 + private String treeSpecies;
  59 +
  60 + @Schema(description = "树木规格")
  61 + @ExcelProperty("树木规格")
  62 + private String treeSpec;
  63 +
  64 + @Schema(description = "抢险完成时间", requiredMode = Schema.RequiredMode.REQUIRED)
  65 + @ExcelProperty("抢险完成时间")
  66 + private LocalDateTime rescueCompleteTime;
  67 +
  68 + @Schema(description = "抢险中照片", requiredMode = Schema.RequiredMode.REQUIRED)
  69 + @ExcelProperty(value = "抢险中照片", converter = ImageStringConverter.class)
  70 + private byte[] emergencyImg1;
  71 +
  72 + @Schema(description = "抢险中照片", requiredMode = Schema.RequiredMode.REQUIRED)
  73 + @ExcelProperty(value = "抢险中照片", converter = ImageStringConverter.class)
  74 + private byte[] emergencyImg2;
  75 +
  76 + @Schema(description = "抢险中照片", requiredMode = Schema.RequiredMode.REQUIRED)
  77 + @ExcelProperty(value = "抢险中照片", converter = ImageStringConverter.class)
  78 + private byte[] emergencyImg3;
  79 +
  80 + @Schema(description = "抢险结束照片", requiredMode = Schema.RequiredMode.REQUIRED)
  81 + @ExcelProperty(value = "抢险结束照片", converter = ImageStringConverter.class)
  82 + private byte[] emergencyEndImg;
  83 +
  84 + @Schema(description = "预警信息")
  85 + @ExcelProperty("预警信息")
  86 + private String remarks;
  87 +
  88 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskExcleRespVO.java
@@ -28,10 +28,18 @@ public class EmergencyTaskExcleRespVO { @@ -28,10 +28,18 @@ public class EmergencyTaskExcleRespVO {
28 @ExcelProperty("险情类型") 28 @ExcelProperty("险情类型")
29 private String emergencyTypeName; 29 private String emergencyTypeName;
30 30
  31 + @Schema(description = "险情类型数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5209")
  32 + @ExcelProperty("数量/株")
  33 + private Integer emergencyTypeNum;
  34 +
31 @Schema(description = "险情危害(如:无)", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325") 35 @Schema(description = "险情危害(如:无)", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
32 @ExcelProperty("险情危害") 36 @ExcelProperty("险情危害")
33 private String emergencyHarmName; 37 private String emergencyHarmName;
34 38
  39 + @Schema(description = "险情危害数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
  40 + @ExcelProperty("数量")
  41 + private Integer emergencyHarmNum;
  42 +
35 @Schema(description = "树木权属(如:专业权属)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19498") 43 @Schema(description = "树木权属(如:专业权属)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19498")
36 @ExcelProperty("树木权属") 44 @ExcelProperty("树木权属")
37 private String treeOwnershipName; 45 private String treeOwnershipName;
@@ -40,6 +48,10 @@ public class EmergencyTaskExcleRespVO { @@ -40,6 +48,10 @@ public class EmergencyTaskExcleRespVO {
40 @ExcelProperty("险情地点") 48 @ExcelProperty("险情地点")
41 private String emergencyLocation; 49 private String emergencyLocation;
42 50
  51 + @Schema(description = "所属街道", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + @ExcelProperty("所属街道")
  53 + private String streetName;
  54 +
43 @Schema(description = "上报人", requiredMode = Schema.RequiredMode.REQUIRED) 55 @Schema(description = "上报人", requiredMode = Schema.RequiredMode.REQUIRED)
44 @ExcelProperty("上报人") 56 @ExcelProperty("上报人")
45 private String reporter; 57 private String reporter;
@@ -56,7 +68,7 @@ public class EmergencyTaskExcleRespVO { @@ -56,7 +68,7 @@ public class EmergencyTaskExcleRespVO {
56 //@ExcelProperty(value="抢险中照片", converter = ListToStringConverter.class) 68 //@ExcelProperty(value="抢险中照片", converter = ListToStringConverter.class)
57 @ExcelProperty(value = "抢险中照片", converter = ImageListConverter.class) 69 @ExcelProperty(value = "抢险中照片", converter = ImageListConverter.class)
58 @ColumnWidth(70) 70 @ColumnWidth(70)
59 - private List<String> emergencyImg; 71 + private List<byte[]> emergencyImg;
60 72
61 @Schema(description = "树种(如:国槐)") 73 @Schema(description = "树种(如:国槐)")
62 @ExcelProperty("树种") 74 @ExcelProperty("树种")
@@ -78,7 +90,7 @@ public class EmergencyTaskExcleRespVO { @@ -78,7 +90,7 @@ public class EmergencyTaskExcleRespVO {
78 //@ExcelProperty(value="抢险结束照片", converter = ListToStringConverter.class) 90 //@ExcelProperty(value="抢险结束照片", converter = ListToStringConverter.class)
79 @ExcelProperty(value = "抢险结束照片", converter = ImageListConverter.class) 91 @ExcelProperty(value = "抢险结束照片", converter = ImageListConverter.class)
80 @ColumnWidth(70) 92 @ColumnWidth(70)
81 - private List<String> emergencyEndImg; 93 + private List<byte[]> emergencyEndImg;
82 94
83 @Schema(description = "备注") 95 @Schema(description = "备注")
84 @ExcelProperty("备注") 96 @ExcelProperty("备注")
@@ -96,7 +108,6 @@ public class EmergencyTaskExcleRespVO { @@ -96,7 +108,6 @@ public class EmergencyTaskExcleRespVO {
96 @ExcelProperty("审批备注") 108 @ExcelProperty("审批备注")
97 private String approvalRemarks; 109 private String approvalRemarks;
98 110
99 -  
100 @Schema(description = "创建者", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") 111 @Schema(description = "创建者", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
101 @ExcelProperty("创建者") 112 @ExcelProperty("创建者")
102 private String creatorName; 113 private String creatorName;
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskPageReqVO.java
@@ -46,7 +46,9 @@ public class EmergencyTaskPageReqVO extends PageParam { @@ -46,7 +46,9 @@ public class EmergencyTaskPageReqVO extends PageParam {
46 @Schema(description = "归属(一级部门id)", example = "6353") 46 @Schema(description = "归属(一级部门id)", example = "6353")
47 private Long companyId; 47 private Long companyId;
48 48
  49 + @Schema(description = "所在街道ID", example = "1111111")
  50 + private String streetId;
49 51
50 private List<Integer> busiStatusList; 52 private List<Integer> busiStatusList;
51 53
52 -}  
53 \ No newline at end of file 54 \ No newline at end of file
  55 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskRespVO.java
1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo; 1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo;
2 2
  3 +import com.fhs.core.trans.anno.Trans;
  4 +import com.fhs.core.trans.constant.TransType;
3 import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter; 5 import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter;
4 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO; 6 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO;
5 import io.swagger.v3.oas.annotations.media.Schema; 7 import io.swagger.v3.oas.annotations.media.Schema;
  8 +import jakarta.validation.constraints.NotEmpty;
6 import lombok.*; 9 import lombok.*;
7 import java.util.*; 10 import java.util.*;
8 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
@@ -136,4 +139,15 @@ public class EmergencyTaskRespVO { @@ -136,4 +139,15 @@ public class EmergencyTaskRespVO {
136 @ExcelProperty("部门") 139 @ExcelProperty("部门")
137 private String deptName; 140 private String deptName;
138 141
139 -}  
140 \ No newline at end of file 142 \ No newline at end of file
  143 + @Schema(description = "险情类型数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  144 + private Integer emergencyTypeNum;
  145 +
  146 + @Schema(description = "险情危害数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  147 + private Integer emergencyHarmNum;
  148 +
  149 + @Schema(description = "所在街道", requiredMode = Schema.RequiredMode.REQUIRED, example = "255")
  150 + private String streetId;
  151 +
  152 + @Schema(description = "所在街道", requiredMode = Schema.RequiredMode.REQUIRED, example = "255")
  153 + private String streetName;
  154 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/emergencytask/vo/EmergencyTaskSaveReqVO.java
1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo; 1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo;
2 2
  3 +import cn.idev.excel.annotation.ExcelProperty;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.*; 5 import lombok.*;
5 import java.util.*; 6 import java.util.*;
6 import jakarta.validation.constraints.*; 7 import jakarta.validation.constraints.*;
7 import org.springframework.format.annotation.DateTimeFormat; 8 import org.springframework.format.annotation.DateTimeFormat;
8 import java.time.LocalDateTime; 9 import java.time.LocalDateTime;
  10 +import java.util.List;
9 11
10 @Schema(description = "管理后台 - 抢险任务主新增/修改 Request VO") 12 @Schema(description = "管理后台 - 抢险任务主新增/修改 Request VO")
11 @Data 13 @Data
@@ -94,6 +96,16 @@ public class EmergencyTaskSaveReqVO { @@ -94,6 +96,16 @@ public class EmergencyTaskSaveReqVO {
94 private String approvalUserName; 96 private String approvalUserName;
95 97
96 @Schema(description = "使用机械", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六") 98 @Schema(description = "使用机械", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
97 - private List<EmergencyMachinerySaveReqVO> emergencyMachineryList; 99 + private List<EmergencyMachinerySaveReqVO> emergencyMachineryList;
98 100
99 -}  
100 \ No newline at end of file 101 \ No newline at end of file
  102 + @Schema(description = "险情类型数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  103 + private Integer emergencyTypeNum;
  104 +
  105 + @Schema(description = "险情危害数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
  106 + private Integer emergencyHarmNum;
  107 +
  108 + @Schema(description = "所在街道", requiredMode = Schema.RequiredMode.REQUIRED, example = "255")
  109 + @NotEmpty(message = "所在街道")
  110 + private String streetId;
  111 +
  112 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/filedownload/FileDownloadController.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.controller.admin.filedownload;
  2 +
  3 +import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils;
  4 +import jakarta.annotation.security.PermitAll;
  5 +import org.springframework.web.bind.annotation.*;
  6 +import jakarta.annotation.Resource;
  7 +import org.springframework.validation.annotation.Validated;
  8 +import org.springframework.security.access.prepost.PreAuthorize;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
  10 +import io.swagger.v3.oas.annotations.Parameter;
  11 +import io.swagger.v3.oas.annotations.Operation;
  12 +
  13 +import jakarta.validation.constraints.*;
  14 +import jakarta.validation.*;
  15 +import jakarta.servlet.http.*;
  16 +
  17 +import java.io.FileInputStream;
  18 +import java.io.OutputStream;
  19 +import java.net.URLEncoder;
  20 +import java.nio.charset.StandardCharsets;
  21 +import java.util.*;
  22 +import java.io.IOException;
  23 +
  24 +import com.zteits.urbanops.framework.common.pojo.PageParam;
  25 +import com.zteits.urbanops.framework.common.pojo.PageResult;
  26 +import com.zteits.urbanops.framework.common.pojo.CommonResult;
  27 +import com.zteits.urbanops.framework.common.util.object.BeanUtils;
  28 +
  29 +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
  30 +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success;
  31 +
  32 +import com.zteits.urbanops.framework.excel.core.util.ExcelUtils;
  33 +
  34 +import com.zteits.urbanops.framework.apilog.core.annotation.ApiAccessLog;
  35 +import static com.zteits.urbanops.framework.apilog.core.enums.OperateTypeEnum.*;
  36 +import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.FILE_DOWNLOAD_NOT_EXISTS;
  37 +import static com.zteits.urbanops.module.infra.framework.file.core.utils.FileTypeUtils.writeAttachment;
  38 +
  39 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.*;
  40 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  41 +import com.zteits.urbanops.module.garden.service.filedownload.FileDownloadService;
  42 +
  43 +@Tag(name = "管理后台 - 文件异步下载任务")
  44 +@RestController
  45 +@RequestMapping("/garden/file-download")
  46 +@Validated
  47 +public class FileDownloadController {
  48 +
  49 + @Resource
  50 + private FileDownloadService fileDownloadService;
  51 +
  52 + @PostMapping("/create")
  53 + @Operation(summary = "创建文件异步下载任务")
  54 + @PreAuthorize("@ss.hasPermission('garden:file-download:create')")
  55 + public CommonResult<Long> createFileDownload(@Valid @RequestBody FileDownloadSaveReqVO createReqVO) {
  56 + return success(fileDownloadService.createFileDownload(createReqVO));
  57 + }
  58 +
  59 + @PutMapping("/update")
  60 + @Operation(summary = "更新文件异步下载任务")
  61 + @PreAuthorize("@ss.hasPermission('garden:file-download:update')")
  62 + public CommonResult<Boolean> updateFileDownload(@Valid @RequestBody FileDownloadSaveReqVO updateReqVO) {
  63 + fileDownloadService.updateFileDownload(updateReqVO);
  64 + return success(true);
  65 + }
  66 +
  67 + @DeleteMapping("/delete")
  68 + @Operation(summary = "删除文件异步下载任务")
  69 + @Parameter(name = "id", description = "编号", required = true)
  70 + @PreAuthorize("@ss.hasPermission('garden:file-download:delete')")
  71 + public CommonResult<Boolean> deleteFileDownload(@RequestParam("id") Long id) {
  72 + fileDownloadService.deleteFileDownload(id);
  73 + return success(true);
  74 + }
  75 +
  76 + @DeleteMapping("/delete-list")
  77 + @Parameter(name = "ids", description = "编号", required = true)
  78 + @Operation(summary = "批量删除文件异步下载任务")
  79 + @PreAuthorize("@ss.hasPermission('garden:file-download:delete')")
  80 + public CommonResult<Boolean> deleteFileDownloadList(@RequestParam("ids") List<Long> ids) {
  81 + fileDownloadService.deleteFileDownloadListByIds(ids);
  82 + return success(true);
  83 + }
  84 +
  85 + @GetMapping("/get")
  86 + @Operation(summary = "获得文件异步下载任务")
  87 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  88 + @PreAuthorize("@ss.hasPermission('garden:file-download:query')")
  89 + public CommonResult<FileDownloadRespVO> getFileDownload(@RequestParam("id") Long id) {
  90 + FileDownloadDO fileDownload = fileDownloadService.getFileDownload(id);
  91 + return success(BeanUtils.toBean(fileDownload, FileDownloadRespVO.class));
  92 + }
  93 +
  94 + @GetMapping("/page")
  95 + @Operation(summary = "获得文件异步下载任务分页")
  96 + //@PreAuthorize("@ss.hasPermission('garden:file-download:query')")
  97 + @PermitAll
  98 + public CommonResult<PageResult<FileDownloadRespVO>> getFileDownloadPage(@Valid FileDownloadPageReqVO pageReqVO) {
  99 + PageResult<FileDownloadDO> pageResult = fileDownloadService.getFileDownloadPage(pageReqVO);
  100 + return success(BeanUtils.toBean(pageResult, FileDownloadRespVO.class));
  101 + }
  102 +
  103 + @GetMapping("/export-excel")
  104 + @Operation(summary = "导出文件异步下载任务 Excel")
  105 + @PreAuthorize("@ss.hasPermission('garden:file-download:export')")
  106 + @ApiAccessLog(operateType = EXPORT)
  107 + public void exportFileDownloadExcel(@Valid FileDownloadPageReqVO pageReqVO,
  108 + HttpServletResponse response) throws IOException {
  109 + pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
  110 + List<FileDownloadDO> list = fileDownloadService.getFileDownloadPage(pageReqVO).getList();
  111 + // 导出 Excel
  112 + ExcelUtils.write(response, "文件异步下载任务.xls", "数据", FileDownloadRespVO.class,
  113 + BeanUtils.toBean(list, FileDownloadRespVO.class));
  114 + }
  115 +
  116 + @GetMapping("/download")
  117 + @Operation(summary = "下载文件异步下载任务")
  118 + @Parameter(name = "id", description = "编号", required = true, example = "1024")
  119 + @PreAuthorize("@ss.hasPermission('garden:file-download:query')")
  120 + public void downloadFileDownload(@RequestParam("id") Long id, HttpServletResponse response) throws IOException {
  121 +
  122 + fileDownloadService.writeAttachment(response, id);
  123 + }
  124 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/filedownload/vo/FileDownloadPageReqVO.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.controller.admin.filedownload.vo;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
  6 +import com.zteits.urbanops.framework.common.pojo.PageParam;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +import static com.zteits.urbanops.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
  11 +
  12 +@Schema(description = "管理后台 - 文件异步下载任务分页 Request VO")
  13 +@Data
  14 +public class FileDownloadPageReqVO extends PageParam {
  15 +
  16 + @Schema(description = "业务唯一编码::EmergencyTaskExcle")
  17 + private String busiCode;
  18 +
  19 + @Schema(description = "功能模块名称", example = "李四")
  20 + private String busiName;
  21 +
  22 + @Schema(description = "文件名称", example = "张三")
  23 + private String fileName;
  24 +
  25 + @Schema(description = "本地文件绝对路径")
  26 + private String filePath;
  27 +
  28 + @Schema(description = "状态:0待处理 1生成中 2成功 3失败", example = "2")
  29 + private Integer status;
  30 +
  31 + @Schema(description = "失败原因")
  32 + private String failMsg;
  33 +
  34 + @Schema(description = "文件过期时间")
  35 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  36 + private LocalDateTime[] expireTime;
  37 +
  38 + @Schema(description = "文件生成完成时间")
  39 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  40 + private LocalDateTime[] finishTime;
  41 +
  42 + @Schema(description = "文件大小(单位:字节)")
  43 + private Long fileSize;
  44 +
  45 + @Schema(description = "文件后缀:xlsx/zip/pdf")
  46 + private String fileSuffix;
  47 +
  48 + @Schema(description = "备注", example = "你猜")
  49 + private String remark;
  50 +
  51 + @Schema(description = "创建时间")
  52 + @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
  53 + private LocalDateTime[] createTime;
  54 +
  55 + @Schema(description = "创建人")
  56 + private String creator;
  57 +
  58 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/filedownload/vo/FileDownloadRespVO.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.controller.admin.filedownload.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import org.springframework.format.annotation.DateTimeFormat;
  7 +import java.time.LocalDateTime;
  8 +import cn.idev.excel.annotation.*;
  9 +
  10 +@Schema(description = "管理后台 - 文件异步下载任务 Response VO")
  11 +@Data
  12 +@ExcelIgnoreUnannotated
  13 +public class FileDownloadRespVO {
  14 +
  15 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27429")
  16 + @ExcelProperty("主键ID")
  17 + private Long id;
  18 +
  19 + @Schema(description = "业务唯一编码::EmergencyTaskExcle", requiredMode = Schema.RequiredMode.REQUIRED)
  20 + @ExcelProperty("业务唯一编码::EmergencyTaskExcle")
  21 + private String busiCode;
  22 +
  23 + @Schema(description = "功能模块名称", example = "李四")
  24 + @ExcelProperty("功能模块名称")
  25 + private String busiName;
  26 +
  27 + @Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
  28 + @ExcelProperty("文件名称")
  29 + private String fileName;
  30 +
  31 + @Schema(description = "本地文件绝对路径")
  32 + @ExcelProperty("本地文件绝对路径")
  33 + private String filePath;
  34 +
  35 + @Schema(description = "状态:0待处理 1生成中 2成功 3失败", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  36 + @ExcelProperty("状态:0待处理 1生成中 2成功 3失败")
  37 + private Integer status;
  38 +
  39 + @Schema(description = "失败原因")
  40 + @ExcelProperty("失败原因")
  41 + private String failMsg;
  42 +
  43 + @Schema(description = "文件过期时间")
  44 + @ExcelProperty("文件过期时间")
  45 + private LocalDateTime expireTime;
  46 +
  47 + @Schema(description = "文件生成完成时间")
  48 + @ExcelProperty("文件生成完成时间")
  49 + private LocalDateTime finishTime;
  50 +
  51 + @Schema(description = "文件大小(单位:字节)")
  52 + @ExcelProperty("文件大小(单位:字节)")
  53 + private Long fileSize;
  54 +
  55 + @Schema(description = "文件后缀:xlsx/zip/pdf")
  56 + @ExcelProperty("文件后缀:xlsx/zip/pdf")
  57 + private String fileSuffix;
  58 +
  59 + @Schema(description = "备注", example = "你猜")
  60 + @ExcelProperty("备注")
  61 + private String remark;
  62 +
  63 + @Schema(description = "创建时间")
  64 + @ExcelProperty("创建时间")
  65 + private LocalDateTime createTime;
  66 +
  67 +}
0 \ No newline at end of file 68 \ No newline at end of file
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/filedownload/vo/FileDownloadSaveReqVO.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.controller.admin.filedownload.vo;
  2 +
  3 +import io.swagger.v3.oas.annotations.media.Schema;
  4 +import lombok.*;
  5 +import java.util.*;
  6 +import jakarta.validation.constraints.*;
  7 +import org.springframework.format.annotation.DateTimeFormat;
  8 +import java.time.LocalDateTime;
  9 +
  10 +@Schema(description = "管理后台 - 文件异步下载任务新增/修改 Request VO")
  11 +@Data
  12 +public class FileDownloadSaveReqVO {
  13 +
  14 + @Schema(description = "主键ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "27429")
  15 + private Long id;
  16 +
  17 + @Schema(description = "业务唯一编码::EmergencyTaskExcle", requiredMode = Schema.RequiredMode.REQUIRED)
  18 + @NotEmpty(message = "业务唯一编码::EmergencyTaskExcle不能为空")
  19 + private String busiCode;
  20 +
  21 + @Schema(description = "功能模块名称", example = "李四")
  22 + private String busiName;
  23 +
  24 + @Schema(description = "文件名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
  25 + @NotEmpty(message = "文件名称不能为空")
  26 + private String fileName;
  27 +
  28 + @Schema(description = "本地文件绝对路径")
  29 + private String filePath;
  30 +
  31 + @Schema(description = "状态:0待处理 1生成中 2成功 3失败", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
  32 + @NotNull(message = "状态:0待处理 1生成中 2成功 3失败不能为空")
  33 + private Integer status;
  34 +
  35 + @Schema(description = "失败原因")
  36 + private String failMsg;
  37 +
  38 + @Schema(description = "文件过期时间")
  39 + private LocalDateTime expireTime;
  40 +
  41 + @Schema(description = "文件生成完成时间")
  42 + private LocalDateTime finishTime;
  43 +
  44 + @Schema(description = "文件大小(单位:字节)")
  45 + private Long fileSize;
  46 +
  47 + @Schema(description = "文件后缀:xlsx/zip/pdf")
  48 + private String fileSuffix;
  49 +
  50 + @Schema(description = "备注", example = "你猜")
  51 + private String remark;
  52 +
  53 +}
0 \ No newline at end of file 54 \ No newline at end of file
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/emergencytask/EmergencyTaskDO.java
1 package com.zteits.urbanops.module.garden.dal.dataobject.emergencytask; 1 package com.zteits.urbanops.module.garden.dal.dataobject.emergencytask;
2 2
  3 +import com.baomidou.mybatisplus.annotation.KeySequence;
  4 +import com.baomidou.mybatisplus.annotation.TableField;
  5 +import com.baomidou.mybatisplus.annotation.TableId;
  6 +import com.baomidou.mybatisplus.annotation.TableName;
  7 +import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO;
3 import com.zteits.urbanops.framework.mybatis.core.type.StringArrayToListTypeHandler; 8 import com.zteits.urbanops.framework.mybatis.core.type.StringArrayToListTypeHandler;
4 -import com.zteits.urbanops.framework.mybatis.core.type.StringListTypeHandler;  
5 import lombok.*; 9 import lombok.*;
6 -import java.util.*;  
7 -import java.time.LocalDateTime;  
8 -import java.time.LocalDateTime;  
9 -import java.time.LocalDateTime;  
10 -import java.time.LocalDateTime; 10 +
11 import java.time.LocalDateTime; 11 import java.time.LocalDateTime;
12 -import com.baomidou.mybatisplus.annotation.*;  
13 -import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO; 12 +import java.util.List;
14 13
15 /** 14 /**
16 * 抢险任务主 DO 15 * 抢险任务主 DO
@@ -139,6 +138,16 @@ public class EmergencyTaskDO extends BaseDO { @@ -139,6 +138,16 @@ public class EmergencyTaskDO extends BaseDO {
139 * 更新者 138 * 更新者
140 */ 139 */
141 private String updaterName; 140 private String updaterName;
142 -  
143 -  
144 -}  
145 \ No newline at end of file 141 \ No newline at end of file
  142 + /**
  143 + * 险情类型数量
  144 + */
  145 + private Integer emergencyTypeNum;
  146 + /**
  147 + * 险情危害数量
  148 + */
  149 + private Integer emergencyHarmNum;
  150 + /**
  151 + * 所在街道ID
  152 + */
  153 + private String streetId;
  154 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/filedownload/FileDownloadDO.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.dal.dataobject.filedownload;
  2 +
  3 +import lombok.*;
  4 +import java.util.*;
  5 +import java.time.LocalDateTime;
  6 +import java.time.LocalDateTime;
  7 +import java.time.LocalDateTime;
  8 +import java.time.LocalDateTime;
  9 +import com.baomidou.mybatisplus.annotation.*;
  10 +import com.zteits.urbanops.framework.mybatis.core.dataobject.BaseDO;
  11 +
  12 +/**
  13 + * 文件异步下载任务 DO
  14 + *
  15 + * @author 超级管理员
  16 + */
  17 +@TableName("garden_file_download")
  18 +@KeySequence("garden_file_download_seq") // 用于 Oracle、PostgreSQL、Kingbase、DB2、H2 数据库的主键自增。如果是 MySQL 等数据库,可不写。
  19 +@Data
  20 +@EqualsAndHashCode(callSuper = true)
  21 +@ToString(callSuper = true)
  22 +@Builder
  23 +@NoArgsConstructor
  24 +@AllArgsConstructor
  25 +public class FileDownloadDO extends BaseDO {
  26 +
  27 + /**
  28 + * 主键ID
  29 + */
  30 + @TableId
  31 + private Long id;
  32 + /**
  33 + * 业务唯一编码::EmergencyTaskExcle
  34 + */
  35 + private String busiCode;
  36 + /**
  37 + * 功能模块名称
  38 + */
  39 + private String busiName;
  40 + /**
  41 + * 文件名称
  42 + */
  43 + private String fileName;
  44 + /**
  45 + * 本地文件绝对路径
  46 + */
  47 + private String filePath;
  48 + /**
  49 + * 存储类型:存储类型:LOCAL/OSS
  50 + */
  51 + private String storageType;
  52 + /**
  53 + * 状态:0待处理 1生成中 2成功 3失败
  54 + */
  55 + private Integer status;
  56 + /**
  57 + * 失败原因
  58 + */
  59 + private String failMsg;
  60 + /**
  61 + * 文件过期时间
  62 + */
  63 + private LocalDateTime expireTime;
  64 + /**
  65 + * 文件生成完成时间
  66 + */
  67 + private LocalDateTime finishTime;
  68 + /**
  69 + * 文件大小(单位:字节)
  70 + */
  71 + private Long fileSize;
  72 + /**
  73 + * 文件后缀:xlsx/zip/pdf
  74 + */
  75 + private String fileSuffix;
  76 + /**
  77 + * 备注
  78 + */
  79 + private String remark;
  80 +
  81 +
  82 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/emergencytask/EmergencyTaskMapper.java
@@ -40,6 +40,7 @@ public interface EmergencyTaskMapper extends BaseMapperX&lt;EmergencyTaskDO&gt; { @@ -40,6 +40,7 @@ public interface EmergencyTaskMapper extends BaseMapperX&lt;EmergencyTaskDO&gt; {
40 .likeIfPresent(EmergencyTaskDO::getEmergencyLocation, reqVO.getEmergencyLocation()) 40 .likeIfPresent(EmergencyTaskDO::getEmergencyLocation, reqVO.getEmergencyLocation())
41 .eqIfPresent(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatus()) 41 .eqIfPresent(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatus())
42 .eqIfPresent(EmergencyTaskDO::getCompanyId, reqVO.getCompanyId()) 42 .eqIfPresent(EmergencyTaskDO::getCompanyId, reqVO.getCompanyId())
  43 + .eqIfPresent(EmergencyTaskDO::getStreetId, reqVO.getStreetId())
43 .betweenIfPresent(EmergencyTaskDO::getDiscoveryTime, startDateTime, endDateTime); 44 .betweenIfPresent(EmergencyTaskDO::getDiscoveryTime, startDateTime, endDateTime);
44 if(!CollectionUtils.isEmpty(reqVO.getBusiStatusList())){ 45 if(!CollectionUtils.isEmpty(reqVO.getBusiStatusList())){
45 sql.in(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatusList()); 46 sql.in(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatusList());
@@ -50,4 +51,4 @@ public interface EmergencyTaskMapper extends BaseMapperX&lt;EmergencyTaskDO&gt; { @@ -50,4 +51,4 @@ public interface EmergencyTaskMapper extends BaseMapperX&lt;EmergencyTaskDO&gt; {
50 51
51 int deleteByTaskNo(@Param("taskNo") String taskNo); 52 int deleteByTaskNo(@Param("taskNo") String taskNo);
52 53
53 -}  
54 \ No newline at end of file 54 \ No newline at end of file
  55 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/filedownload/FileDownloadMapper.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.dal.mysql.filedownload;
  2 +
  3 +import java.util.*;
  4 +
  5 +import com.zteits.urbanops.framework.common.pojo.PageResult;
  6 +import com.zteits.urbanops.framework.mybatis.core.query.LambdaQueryWrapperX;
  7 +import com.zteits.urbanops.framework.mybatis.core.mapper.BaseMapperX;
  8 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  9 +import org.apache.ibatis.annotations.Mapper;
  10 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.*;
  11 +
  12 +/**
  13 + * 文件异步下载任务 Mapper
  14 + *
  15 + * @author 超级管理员
  16 + */
  17 +@Mapper
  18 +public interface FileDownloadMapper extends BaseMapperX<FileDownloadDO> {
  19 +
  20 + default PageResult<FileDownloadDO> selectPage(FileDownloadPageReqVO reqVO) {
  21 + return selectPage(reqVO, new LambdaQueryWrapperX<FileDownloadDO>()
  22 + .eqIfPresent(FileDownloadDO::getBusiCode, reqVO.getBusiCode())
  23 + .likeIfPresent(FileDownloadDO::getBusiName, reqVO.getBusiName())
  24 + .likeIfPresent(FileDownloadDO::getFileName, reqVO.getFileName())
  25 + .eqIfPresent(FileDownloadDO::getFilePath, reqVO.getFilePath())
  26 + .eqIfPresent(FileDownloadDO::getStatus, reqVO.getStatus())
  27 + .eqIfPresent(FileDownloadDO::getFailMsg, reqVO.getFailMsg())
  28 + .betweenIfPresent(FileDownloadDO::getExpireTime, reqVO.getExpireTime())
  29 + .betweenIfPresent(FileDownloadDO::getFinishTime, reqVO.getFinishTime())
  30 + .eqIfPresent(FileDownloadDO::getFileSize, reqVO.getFileSize())
  31 + .eqIfPresent(FileDownloadDO::getFileSuffix, reqVO.getFileSuffix())
  32 + .eqIfPresent(FileDownloadDO::getRemark, reqVO.getRemark())
  33 + .betweenIfPresent(FileDownloadDO::getCreateTime, reqVO.getCreateTime())
  34 + .eqIfPresent(FileDownloadDO::getCreator, reqVO.getCreator())
  35 + .orderByDesc(FileDownloadDO::getId));
  36 + }
  37 +
  38 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/enums/ErrorCodeConstants.java
@@ -155,4 +155,7 @@ public interface ErrorCodeConstants { @@ -155,4 +155,7 @@ public interface ErrorCodeConstants {
155 // ========== 一树一档案巡检与风险评估 1-100-008-000 ========== 155 // ========== 一树一档案巡检与风险评估 1-100-008-000 ==========
156 ErrorCode TREE_INSPECTION_NOT_EXISTS = new ErrorCode(1100008001, "巡检评估记录不存在"); 156 ErrorCode TREE_INSPECTION_NOT_EXISTS = new ErrorCode(1100008001, "巡检评估记录不存在");
157 157
  158 + //文件异步下载
  159 + ErrorCode FILE_DOWNLOAD_NOT_EXISTS = new ErrorCode(1-100-000-001, "文件异步下载任务不存在");
  160 +
158 } 161 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/enums/ExportBusiCodeEnum.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.enums;
  2 +
  3 +import lombok.Getter;
  4 +
  5 +import java.time.LocalDateTime;
  6 +import java.time.format.DateTimeFormatter;
  7 +
  8 +/**
  9 + * 导出业务编码枚举
  10 + */
  11 +@Getter
  12 +public enum ExportBusiCodeEnum {
  13 +
  14 + /** 旧版抢险案件导出 */
  15 + EMERGENCY_TASK_OLD("EmergencyTaskExcle", "险情管理", "抢险案件表", "险情案件", ".xlsx"),
  16 + /** 新版抢险案件导出 */
  17 + EMERGENCY_TASK_NEW("EmergencyTaskExcleNew", "险情管理", "抢险案件表", "险情案件", ".xlsx");
  18 +
  19 + /** 业务编码 */
  20 + private final String busiCode;
  21 + /** 业务名称 */
  22 + private final String busiName;
  23 + /** 导出文件名(不含后缀) */
  24 + private final String fileName;
  25 + /** Excel Sheet 名称 */
  26 + private final String sheetName;
  27 + /** 文件后缀 */
  28 + private final String fileSuffix;
  29 +
  30 + // 时间格式化:yyyyMMddHHmmss
  31 + private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
  32 +
  33 + /**
  34 + * 全参构造
  35 + * @param busiCode 业务编码
  36 + * @param busiName 业务名称
  37 + * @param fileName 文件名
  38 + * @param sheetName sheet名称
  39 + * @param fileSuffix 文件后缀
  40 + */
  41 + ExportBusiCodeEnum(String busiCode, String busiName, String fileName, String sheetName, String fileSuffix) {
  42 + this.busiCode = busiCode;
  43 + this.busiName = busiName;
  44 + this.fileName = fileName;
  45 + this.sheetName = sheetName;
  46 + this.fileSuffix = fileSuffix;
  47 + }
  48 +
  49 + /**
  50 + * 根据业务编码获取枚举
  51 + * @param busiCode 业务编码
  52 + * @return 对应枚举,未匹配返回 null
  53 + */
  54 + public static ExportBusiCodeEnum getByCode(String busiCode) {
  55 + if (busiCode == null) {
  56 + return null;
  57 + }
  58 + for (ExportBusiCodeEnum enumItem : values()) {
  59 + if (enumItem.getBusiCode().equals(busiCode)) {
  60 + return enumItem;
  61 + }
  62 + }
  63 + return null;
  64 + }
  65 +
  66 + /**
  67 + * 拼接完整文件名(文件名 + 后缀)
  68 + * @return 完整文件名 例:抢险案件表.xls
  69 + */
  70 + public String getFullFileName() {
  71 + String nowTime = LocalDateTime.now().format(TIME_FORMATTER);
  72 + return String.format("%s_%s%s", this.fileName, nowTime, this.fileSuffix);
  73 + }
  74 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/enums/FileTaskStatus.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.enums;
  2 +
  3 +/**
  4 + * @Classname FileTaskStatus
  5 + * @Description * 文件异步任务状态
  6 + * * 0待处理 1生成中 2成功 3失败
  7 + * @Date 2026/6/12 15:49
  8 + * @Created by wangqian
  9 + */
  10 +public enum FileTaskStatus {
  11 + // FileTaskStatus 常见定义
  12 + PENDING(0, "待处理"), // 刚创建,还没开始
  13 + PROCESSING(1, "处理中"), // 正在分批导出
  14 + SUCCESS(2, "成功"), // 全部分页写完
  15 + FAIL(3, "失败"); // 中途出错
  16 +
  17 + private final Integer code;
  18 + private final String desc;
  19 +
  20 + FileTaskStatus(Integer code, String desc) {
  21 + this.code = code;
  22 + this.desc = desc;
  23 + }
  24 + public Integer getCode() {
  25 + return code;
  26 + }
  27 + public String getDesc() {
  28 + return desc;
  29 + }
  30 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/job/file/ClearExpiredFilesJob.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.job.file;
  2 +
  3 +import com.zteits.urbanops.framework.quartz.core.handler.JobHandler;
  4 +import com.zteits.urbanops.module.garden.service.filedownload.FileDownloadService;
  5 +import jakarta.annotation.Resource;
  6 +
  7 +/**
  8 + * @Classname ClearExpiredFilesJob
  9 + * @Description 清理过期文件
  10 + * 1、mysql事件调度器将过期文件记录更新为 4-失效,
  11 + * 2、定时任务每天执行一次,查询到数据库中状态为4的记录,将文件删除
  12 + * @Date 2026/6/15 12:25
  13 + * @Created by wangqian
  14 + */
  15 +public class ClearExpiredFilesJob implements JobHandler {
  16 + @Resource
  17 + private FileDownloadService fileDownloadService;
  18 +
  19 + @Override
  20 + public String execute(String param) throws Exception {
  21 + fileDownloadService.clearExpiredFiles();
  22 + return "Successfully";
  23 + }
  24 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/emergencytask/EmergencyExportHandler.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.emergencytask;
  2 +
  3 +import cn.idev.excel.ExcelWriter;
  4 +import cn.idev.excel.write.metadata.WriteSheet;
  5 +import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO;
  6 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskExcleRespVO;
  7 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskRespVO;
  8 +import com.zteits.urbanops.module.garden.service.filedownload.ExcelExportHandler;
  9 +import com.zteits.urbanops.module.garden.util.ImgDownUtils;
  10 +import com.zteits.urbanops.module.infra.framework.file.core.client.FileClient;
  11 +import com.zteits.urbanops.module.infra.service.file.FileConfigService;
  12 +import lombok.extern.slf4j.Slf4j;
  13 +import org.apache.poi.ss.util.CellRangeAddress;
  14 +import org.springframework.stereotype.Service;
  15 +import org.springframework.util.CollectionUtils;
  16 +
  17 +import jakarta.annotation.Resource;
  18 +import java.util.*;
  19 +import java.util.concurrent.ConcurrentHashMap;
  20 +import java.util.stream.Collectors;
  21 +
  22 +import static com.zteits.urbanops.module.garden.service.filedownload.ExcelExportAsyncService.BATCH_SIZE;
  23 +
  24 +/**
  25 + * 应急任务导出处理器
  26 + * 实现 ExcelExportHandler 接口,替代原抽象类继承
  27 + */
  28 +@Slf4j
  29 +@Service
  30 +public class EmergencyExportHandler implements ExcelExportHandler<EmergencyTaskRespVO, EmergencyTaskExcleRespVO> {
  31 +
  32 + // 字典本地缓存
  33 + private static final Map<String, Map<String, String>> DICT_CACHE = new ConcurrentHashMap<>();
  34 +
  35 + // 字典类型常量(避免魔法值)
  36 + private static final String DICT_HAZARD_TYPE = "hazard_type";
  37 + private static final String DICT_HAZARD_RISK = "hazard_risk";
  38 + private static final String DICT_TREE_PERMISSIONS = "tree_permissions";
  39 + private static final String DICT_MECHANICAL_TYPE = "mechanical_type";
  40 + private static final String DICT_RESCUE_STATUS = "rescue_status";
  41 +
  42 + @Resource
  43 + private EmergencyTaskService emergencyTaskService;
  44 + @Resource
  45 + private FileConfigService fileConfigService;
  46 +
  47 + // ===================== 接口方法实现 =====================
  48 + @Override
  49 + public List<EmergencyTaskExcleRespVO> convert(List<EmergencyTaskRespVO> batchList, int startIndex) {
  50 +
  51 + List<EmergencyTaskExcleRespVO> voList = new ArrayList<>(batchList.size());
  52 + // 从上下文获取字典,增加空兜底
  53 + Map<String, String> hazardTypeDict = getDictMap(DICT_HAZARD_TYPE);
  54 + Map<String, String> hazardRiskDict = getDictMap(DICT_HAZARD_RISK);
  55 + Map<String, String> treePermissionsDict = getDictMap(DICT_TREE_PERMISSIONS);
  56 + Map<String, String> mechanicalTypeDict = getDictMap(DICT_MECHANICAL_TYPE);
  57 + Map<String, String> rescueStatusDict = getDictMap(DICT_RESCUE_STATUS);
  58 + // 初始化文件客户端
  59 + FileClient fileClient = fileConfigService.getMasterFileClient();
  60 +
  61 + for (EmergencyTaskRespVO task : batchList) {
  62 + EmergencyTaskExcleRespVO vo = new EmergencyTaskExcleRespVO();
  63 + vo.setTaskNo(task.getTaskNo());
  64 + vo.setDiscoveryTime(task.getDiscoveryTime());
  65 + vo.setReporter(task.getReporter());
  66 + vo.setContactPhone(task.getContactPhone());
  67 + vo.setEmergencyLocation(task.getEmergencyLocation());
  68 + vo.setTreeSpecies(task.getTreeSpecies());
  69 + vo.setTreeSpec(task.getTreeSpec());
  70 + vo.setRescueStartTime(task.getRescueStartTime());
  71 + vo.setRescueCompleteTime(task.getRescueCompleteTime());
  72 + vo.setPersonnelCount(task.getPersonnelCount());
  73 + vo.setRemarks(task.getRemarks());
  74 + vo.setCompanyName(task.getCompanyName());
  75 + vo.setApprovalUserName(task.getApprovalUserName());
  76 + vo.setApprovalRemarks(task.getApprovalRemarks());
  77 + vo.setCreatorName(task.getCreatorName());
  78 + vo.setCreateTime(task.getCreateTime());
  79 + vo.setUpdaterName(task.getUpdaterName());
  80 + vo.setEmergencyHarmNum(task.getEmergencyHarmNum());
  81 + vo.setEmergencyTypeNum(task.getEmergencyTypeNum());
  82 + vo.setStreetName(task.getStreetName());
  83 +
  84 + // 字典翻译
  85 + vo.setEmergencyTypeName(getLabel(hazardTypeDict, task.getEmergencyTypeId()));
  86 + vo.setEmergencyHarmName(getLabel(hazardRiskDict, task.getEmergencyHarmId()));
  87 + vo.setTreeOwnershipName(getLabel(treePermissionsDict, task.getTreeOwnershipId()));
  88 + vo.setBusiStatus(getLabel(rescueStatusDict, task.getBusiStatus()));
  89 +
  90 + // 图片下载处理
  91 + vo.setEmergencyImg(ImgDownUtils.batchDownloadImages(task.getEmergencyImg(), fileClient));
  92 + vo.setEmergencyEndImg(ImgDownUtils.batchDownloadImages(task.getEmergencyEndImg(), fileClient));
  93 +
  94 + // 机械设备信息拼接
  95 + List<String> machineryInfoList = new ArrayList<>();
  96 + if (!CollectionUtils.isEmpty(task.getEmergencyMachineryList())) {
  97 + task.getEmergencyMachineryList().forEach(machinery ->
  98 + machineryInfoList.add(getLabel(mechanicalTypeDict, machinery.getMachineryNameId())
  99 + + ":" + machinery.getMachineryCount()));
  100 + }
  101 + vo.setMachineryInfo(machineryInfoList);
  102 + voList.add(vo);
  103 + }
  104 + return voList;
  105 + }
  106 +
  107 + @Override
  108 + public Class<EmergencyTaskExcleRespVO> getExcelVoClass() {
  109 + return EmergencyTaskExcleRespVO.class;
  110 + }
  111 +
  112 + @Override
  113 + public List<List<String>> buildHead() {
  114 + return new ArrayList<>();
  115 + }
  116 +
  117 + @Override
  118 + public List<CellRangeAddress> buildMergeRule() {
  119 + return new ArrayList<>();
  120 + }
  121 + // ===================== 字典工具方法 =====================
  122 + public Map<String, String> getDictMap(String dictType) {
  123 + // 先从缓存取
  124 + if (DICT_CACHE.containsKey(dictType)) {
  125 + return DICT_CACHE.get(dictType);
  126 + }
  127 + // 缓存不存在,查询数据库
  128 + List<DictDataRespDTO> dictList = emergencyTaskService.getDictDataList(dictType);
  129 + Map<String, String> map = buildDictMap(dictList);
  130 + DICT_CACHE.put(dictType, map);
  131 + return map;
  132 + }
  133 +
  134 + private Map<String, String> buildDictMap(List<DictDataRespDTO> dictDataList) {
  135 + if (CollectionUtils.isEmpty(dictDataList)) {
  136 + return Collections.emptyMap();
  137 + }
  138 + return dictDataList.stream()
  139 + .collect(Collectors.toMap(
  140 + DictDataRespDTO::getValue,
  141 + DictDataRespDTO::getLabel,
  142 + (v1, v2) -> v1
  143 + ));
  144 + }
  145 +
  146 + private String getLabel(Map<String, String> dictMap, Integer id) {
  147 + if (dictMap == null || dictMap.isEmpty() || id == null) {
  148 + return "";
  149 + }
  150 + return dictMap.getOrDefault(String.valueOf(id), "");
  151 + }
  152 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/emergencytask/EmergencyExportNewHandler.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.emergencytask;
  2 +
  3 +import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO;
  4 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskExcleNewRespVO;
  5 +import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.EmergencyTaskRespVO;
  6 +import com.zteits.urbanops.module.garden.service.filedownload.ExcelExportHandler;
  7 +import com.zteits.urbanops.module.garden.util.ImgDownUtils;
  8 +import com.zteits.urbanops.module.infra.framework.file.core.client.FileClient;
  9 +import com.zteits.urbanops.module.infra.service.file.FileConfigService;
  10 +import jakarta.annotation.Resource;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.apache.poi.ss.util.CellRangeAddress;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.util.CollectionUtils;
  15 +
  16 +import java.util.*;
  17 +import java.util.concurrent.ConcurrentHashMap;
  18 +import java.util.concurrent.atomic.AtomicInteger;
  19 +import java.util.stream.Collectors;
  20 +
  21 +import static com.zteits.urbanops.module.garden.service.filedownload.ExcelExportAsyncService.BATCH_SIZE;
  22 +
  23 +/**
  24 + * 新版应急任务导出处理器
  25 + * 实现 ExcelExportHandler 接口
  26 + */
  27 +@Slf4j
  28 +@Service
  29 +public class EmergencyExportNewHandler implements ExcelExportHandler<EmergencyTaskRespVO, EmergencyTaskExcleNewRespVO> {
  30 +
  31 + // ===================== 常量定义(消除魔法值) =====================
  32 + /** 字典类型编码 */
  33 + private static final String DICT_HAZARD_TYPE = "hazard_type";
  34 + private static final String DICT_HAZARD_RISK = "hazard_risk";
  35 + private static final String DICT_TREE_PERMISSIONS = "tree_permissions";
  36 +
  37 + /** 字典缓存:key=字典编码, value=字典映射 */
  38 + private static final Map<String, Map<String, String>> DICT_CACHE = new ConcurrentHashMap<>();
  39 +
  40 + @Resource
  41 + private EmergencyTaskService emergencyTaskService;
  42 + @Resource
  43 + private FileConfigService fileConfigService;
  44 +
  45 + // ===================== 接口方法实现 =====================
  46 + @Override
  47 + public List<EmergencyTaskExcleNewRespVO> convert(List<EmergencyTaskRespVO> sourceList, int startIndex) {
  48 + if (CollectionUtils.isEmpty(sourceList)) {
  49 + return Collections.emptyList();
  50 + }
  51 + Map<String, String> hazardTypeDict = getDictMap(DICT_HAZARD_TYPE);
  52 + Map<String, String> hazardRiskDict = getDictMap(DICT_HAZARD_RISK);
  53 + Map<String, String> treePermissionsDict = getDictMap(DICT_TREE_PERMISSIONS);
  54 + // 初始化文件客户端
  55 + FileClient fileClient = fileConfigService.getMasterFileClient();
  56 +
  57 + List<EmergencyTaskExcleNewRespVO> respList = new ArrayList<>(sourceList.size());
  58 +
  59 + // 合并图片收集+下载,减少一次全量遍历
  60 + for (EmergencyTaskRespVO task : sourceList) {
  61 + EmergencyTaskExcleNewRespVO vo = new EmergencyTaskExcleNewRespVO();
  62 + // 全局行号,跨分片连续不重复
  63 + vo.setTaskNo(String.valueOf(startIndex++));
  64 +
  65 + // 基础字段赋值
  66 + vo.setDiscoveryTime(task.getDiscoveryTime());
  67 + vo.setEmergencyHarmId(task.getEmergencyHarmId());
  68 + vo.setEmergencyLocation(task.getEmergencyLocation());
  69 + vo.setTreeSpecies(task.getTreeSpecies());
  70 + vo.setTreeSpec(task.getTreeSpec());
  71 + vo.setRescueStartTime(task.getRescueStartTime());
  72 + vo.setRescueCompleteTime(task.getRescueCompleteTime());
  73 + vo.setEmergencyHarmNum(task.getEmergencyTypeNum());
  74 + vo.setRemarks(task.getRemarks());
  75 + vo.setCompanyName(task.getCompanyName());
  76 +
  77 + // 字典翻译
  78 + vo.setEmergencyTypeName(getLabel(hazardTypeDict, task.getEmergencyTypeId()));
  79 + vo.setEmergencyHarmName(getLabel(hazardRiskDict, task.getEmergencyHarmId()));
  80 + vo.setTreeOwnershipName(getLabel(treePermissionsDict, task.getTreeOwnershipId()));
  81 +
  82 + // 图片处理(一次组装、一次下载,精简逻辑)
  83 + List<String> emergencyImgs = Optional.ofNullable(task.getEmergencyImg()).orElse(Collections.emptyList());
  84 + List<String> endImgs = Optional.ofNullable(task.getEmergencyEndImg()).orElse(Collections.emptyList());
  85 + List<String> imgUrls = new ArrayList<>(4);
  86 + imgUrls.add(ImgDownUtils.getImgSafe(emergencyImgs, 0));
  87 + imgUrls.add(ImgDownUtils.getImgSafe(emergencyImgs, 1));
  88 + imgUrls.add(ImgDownUtils.getImgSafe(emergencyImgs, 2));
  89 + imgUrls.add(ImgDownUtils.getImgSafe(endImgs, 0));
  90 +
  91 + List<byte[]> imgBytes = ImgDownUtils.batchDownloadImages(imgUrls, fileClient);
  92 + vo.setEmergencyImg1(ImgDownUtils.getByteSafe(imgBytes, 0));
  93 + vo.setEmergencyImg2(ImgDownUtils.getByteSafe(imgBytes, 1));
  94 + vo.setEmergencyImg3(ImgDownUtils.getByteSafe(imgBytes, 2));
  95 + vo.setEmergencyEndImg(ImgDownUtils.getByteSafe(imgBytes, 3));
  96 +
  97 + respList.add(vo);
  98 + }
  99 + return respList;
  100 + }
  101 +
  102 + @Override
  103 + public Class<EmergencyTaskExcleNewRespVO> getExcelVoClass() {
  104 + return EmergencyTaskExcleNewRespVO.class;
  105 + }
  106 +
  107 + @Override
  108 + public List<List<String>> buildHead() {
  109 + List<List<String>> head = new ArrayList<>();
  110 + head.add(Arrays.asList("抢险案件表", "", "序号"));
  111 + head.add(Arrays.asList("", "抢险前", "作业队"));
  112 + head.add(Arrays.asList("", "抢险前", "险情地点"));
  113 + head.add(Arrays.asList("", "抢险前", "发现险情时间"));
  114 + head.add(Arrays.asList("", "抢险前", "险情类型"));
  115 + head.add(Arrays.asList("", "抢险前", "树木权属"));
  116 + head.add(Arrays.asList("", "抢险前", "险情危害"));
  117 + head.add(Arrays.asList("", "抢险前", "险情危害"));
  118 + head.add(Arrays.asList("", "抢险前", "数量/株"));
  119 + head.add(Arrays.asList("", "抢险中", "抢险开始时间"));
  120 + head.add(Arrays.asList("", "抢险中", "树种"));
  121 + head.add(Arrays.asList("", "抢险中", "树木规格"));
  122 + head.add(Arrays.asList("", "抢险后", "抢险完成后时间"));
  123 + head.add(Arrays.asList("", "照片", "抢险过程照片"));
  124 + head.add(Arrays.asList("", "照片", "抢险过程照片"));
  125 + head.add(Arrays.asList("", "照片", "抢险过程照片"));
  126 + head.add(Arrays.asList("", "照片", "抢险后照片"));
  127 + head.add(Arrays.asList("", "备注", ""));
  128 + return head;
  129 + }
  130 +
  131 + @Override
  132 + public List<CellRangeAddress> buildMergeRule() {
  133 + List<CellRangeAddress> mergeList = new ArrayList<>();
  134 + mergeList.add(new CellRangeAddress(0, 0, 0, 17));
  135 + mergeList.add(new CellRangeAddress(1, 1, 1, 8));
  136 + mergeList.add(new CellRangeAddress(1, 1, 9, 11));
  137 + mergeList.add(new CellRangeAddress(1, 1, 12, 12));
  138 + mergeList.add(new CellRangeAddress(1, 1, 13, 16));
  139 + mergeList.add(new CellRangeAddress(1, 1, 17, 17));
  140 + mergeList.add(new CellRangeAddress(2, 2, 6, 7));
  141 + return mergeList;
  142 + }
  143 +
  144 + // ===================== 字典缓存工具 =====================
  145 + /**
  146 + * 获取字典映射(优先走本地缓存)
  147 + */
  148 + private Map<String, String> getDictMap(String dictType) {
  149 + if (DICT_CACHE.containsKey(dictType)) {
  150 + return DICT_CACHE.get(dictType);
  151 + }
  152 + List<DictDataRespDTO> dictList = emergencyTaskService.getDictDataList(dictType);
  153 + Map<String, String> map = buildDictMap(dictList);
  154 + DICT_CACHE.put(dictType, map);
  155 + log.info("加载字典[{}],数据量:{}", dictType, map.size());
  156 + return map;
  157 + }
  158 +
  159 + /**
  160 + * 构建字典 key-value 映射
  161 + */
  162 + private Map<String, String> buildDictMap(List<DictDataRespDTO> dictDataList) {
  163 + if (CollectionUtils.isEmpty(dictDataList)) {
  164 + return Collections.emptyMap();
  165 + }
  166 + return dictDataList.stream()
  167 + .collect(Collectors.toMap(
  168 + DictDataRespDTO::getValue,
  169 + DictDataRespDTO::getLabel,
  170 + (oldVal, newVal) -> oldVal
  171 + ));
  172 + }
  173 +
  174 + /**
  175 + * 字典翻译取值(防空、防NPE)
  176 + */
  177 + private String getLabel(Map<String, String> dictMap, Integer id) {
  178 + if (dictMap == null || dictMap.isEmpty() || id == null) {
  179 + return "";
  180 + }
  181 + return dictMap.getOrDefault(String.valueOf(id), "");
  182 + }
  183 +
  184 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/emergencytask/EmergencyTaskServiceImpl.java
@@ -2,17 +2,21 @@ package com.zteits.urbanops.module.garden.service.emergencytask; @@ -2,17 +2,21 @@ package com.zteits.urbanops.module.garden.service.emergencytask;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.collection.CollectionUtil; 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.util.ObjectUtil;
5 import cn.hutool.core.util.RandomUtil; 6 import cn.hutool.core.util.RandomUtil;
6 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; 7 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
7 import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO; 8 import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO;
8 import com.zteits.urbanops.framework.datapermission.core.annotation.DataPermission; 9 import com.zteits.urbanops.framework.datapermission.core.annotation.DataPermission;
9 import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils; 10 import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils;
10 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO; 11 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO;
  12 +import com.zteits.urbanops.module.garden.dal.dataobject.road.RoadStreetDO;
11 import com.zteits.urbanops.module.garden.dal.mysql.emergencytask.EmergencyMachineryMapper; 13 import com.zteits.urbanops.module.garden.dal.mysql.emergencytask.EmergencyMachineryMapper;
  14 +import com.zteits.urbanops.module.garden.dal.mysql.road.RoadStreetMapper;
12 import com.zteits.urbanops.module.system.api.dept.DeptApi; 15 import com.zteits.urbanops.module.system.api.dept.DeptApi;
13 import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; 16 import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO;
14 import com.zteits.urbanops.module.system.api.dict.DictDataApi; 17 import com.zteits.urbanops.module.system.api.dict.DictDataApi;
15 import org.apache.commons.lang3.StringUtils; 18 import org.apache.commons.lang3.StringUtils;
  19 +import org.apache.poi.ss.formula.functions.T;
16 import org.springframework.stereotype.Service; 20 import org.springframework.stereotype.Service;
17 import jakarta.annotation.Resource; 21 import jakarta.annotation.Resource;
18 import org.springframework.util.CollectionUtils; 22 import org.springframework.util.CollectionUtils;
@@ -20,6 +24,7 @@ import org.springframework.validation.annotation.Validated; @@ -20,6 +24,7 @@ import org.springframework.validation.annotation.Validated;
20 import org.springframework.transaction.annotation.Transactional; 24 import org.springframework.transaction.annotation.Transactional;
21 25
22 import java.util.*; 26 import java.util.*;
  27 +import java.util.concurrent.atomic.AtomicInteger;
23 import java.util.stream.Collectors; 28 import java.util.stream.Collectors;
24 29
25 import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.*; 30 import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.*;
@@ -56,6 +61,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService { @@ -56,6 +61,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
56 @Resource 61 @Resource
57 private DictDataApi dictDataApi; 62 private DictDataApi dictDataApi;
58 63
  64 + @Resource
  65 + private RoadStreetMapper roadStreetMapper;
  66 +
59 @Transactional(rollbackFor = Exception.class) 67 @Transactional(rollbackFor = Exception.class)
60 @Override 68 @Override
61 public Long createEmergencyTask(EmergencyTaskSaveReqVO createReqVO, boolean isSave) { 69 public Long createEmergencyTask(EmergencyTaskSaveReqVO createReqVO, boolean isSave) {
@@ -92,6 +100,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService { @@ -92,6 +100,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
92 emergencyTask.setUpdater(userId); 100 emergencyTask.setUpdater(userId);
93 emergencyTask.setUpdaterName(userName); 101 emergencyTask.setUpdaterName(userName);
94 emergencyTask.setApprovalRemarks(""); 102 emergencyTask.setApprovalRemarks("");
  103 + emergencyTask.setStreetId(createReqVO.getStreetId());
  104 + emergencyTask.setEmergencyHarmNum(createReqVO.getEmergencyHarmNum());
  105 + emergencyTask.setEmergencyTypeNum(createReqVO.getEmergencyTypeNum());
95 if(isSave){ 106 if(isSave){
96 emergencyTask.setCreator(userId); 107 emergencyTask.setCreator(userId);
97 emergencyTask.setCreatorName(userName); 108 emergencyTask.setCreatorName(userName);
@@ -202,22 +213,87 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService { @@ -202,22 +213,87 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
202 if(companyId != null && companyId.equals(100L)){ 213 if(companyId != null && companyId.equals(100L)){
203 pageReqVO.setBusiStatusList(Arrays.asList(2,3,4,5)); 214 pageReqVO.setBusiStatusList(Arrays.asList(2,3,4,5));
204 } 215 }
  216 + // 2. 分页查询主表数据
205 PageResult<EmergencyTaskDO> page = emergencyTaskMapper.selectPage(pageReqVO); 217 PageResult<EmergencyTaskDO> page = emergencyTaskMapper.selectPage(pageReqVO);
  218 + List<EmergencyTaskDO> doList = page.getList();
  219 + if (CollectionUtil.isEmpty(doList)) {
  220 + // 无数据直接返回,跳过后续逻辑
  221 + return new PageResult<>(Collections.emptyList(), page.getTotal());
  222 + }
  223 + // 预定义集合容量,避免 ArrayList 扩容
  224 + int dataSize = doList.size();
  225 + List<String> taskNoList = new ArrayList<>(dataSize);
  226 + Set<Long> orgIdSet = new HashSet<>(dataSize);
  227 +
  228 + for (EmergencyTaskDO taskDO : doList) {
  229 + String taskNo = taskDO.getTaskNo();
  230 + Long companyIdVal = taskDO.getCompanyId();
  231 + Long deptIdVal = taskDO.getDeptId();
  232 +
  233 + if (ObjectUtil.isNotEmpty(taskNo)) {
  234 + taskNoList.add(taskNo);
  235 + }
206 236
207 - List<EmergencyTaskDO> list = page.getList();  
208 - List<EmergencyTaskRespVO> listTwo = BeanUtils.toBean(list, EmergencyTaskRespVO.class); 237 + if (ObjectUtil.isNotNull(companyIdVal)) {
  238 + orgIdSet.add(companyIdVal);
  239 + }
  240 + if (ObjectUtil.isNotNull(deptIdVal)) {
  241 + orgIdSet.add(deptIdVal);
  242 + }
  243 + }
209 244
210 - listTwo.forEach(emergencyTaskRespVO -> {  
211 - List<EmergencyMachineryDO> detailList = emergencyMachineryMapper.selectList(EmergencyMachineryDO::getTaskNo, emergencyTaskRespVO.getTaskNo());  
212 - if(!CollectionUtil.isEmpty(detailList)){  
213 - emergencyTaskRespVO.setEmergencyMachineryList(detailList); 245 + // ===================== 批量查询关联数据(彻底解决 N+1) =====================
  246 + // 机械明细
  247 + List<EmergencyMachineryDO> allMachineryList = emergencyMachineryMapper.selectList(
  248 + EmergencyMachineryDO::getTaskNo, taskNoList
  249 + );
  250 + // 街道信息
  251 + List<DictDataRespDTO> streetList = getDictDataList("street_belong");
  252 +
  253 + // 部门/公司信息
  254 + Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(orgIdSet);
  255 +
  256 + // ===================== 内存分组 & 映射(指定Map初始容量,规避key重复) =====================
  257 + // taskNo -> 机械明细列表
  258 + Map<String, List<EmergencyMachineryDO>> machineryGroupMap = allMachineryList.stream()
  259 + .collect(Collectors.groupingBy(
  260 + EmergencyMachineryDO::getTaskNo,
  261 + () -> new HashMap<>(allMachineryList.size()),
  262 + Collectors.toList()
  263 + ));
  264 +
  265 + // streetId -> 街道对象,防key重复
  266 + Map<String, DictDataRespDTO> roadStreetMap = streetList.stream()
  267 + .collect(Collectors.toMap(
  268 + DictDataRespDTO::getValue,
  269 + item -> item,
  270 + (oldVal, newVal) -> newVal, // 重复key保留后者
  271 + () -> new HashMap<>(streetList.size())
  272 + ));
  273 +
  274 + // 5. DO 转 VO + 批量赋值关联数据
  275 + List<EmergencyTaskRespVO> voList = BeanUtils.toBean(doList, EmergencyTaskRespVO.class);
  276 + for (EmergencyTaskRespVO vo : voList) {
  277 + String taskNo = vo.getTaskNo();
  278 + List<EmergencyMachineryDO> detailList = machineryGroupMap.get(taskNo);
  279 + if (CollectionUtil.isNotEmpty(detailList)) {
  280 + vo.setEmergencyMachineryList(detailList);
214 } 281 }
215 - });  
216 - addDataInspectionPlan(listTwo); 282 + DictDataRespDTO roadStreet = roadStreetMap.get(vo.getStreetId());
  283 + if (roadStreet != null) {
  284 + vo.setStreetName(roadStreet.getLabel());
  285 + }
  286 + DeptRespDTO company = deptMap.get(vo.getCompanyId());
  287 + if(company != null){
  288 + vo.setCompanyName(company.getName());
  289 + }
  290 + }
217 291
218 - // 转换返回  
219 - return new PageResult<>(listTwo, page.getTotal()); 292 + // 6. 原有业务方法
  293 + //addDataInspectionPlan(voList);
220 294
  295 + // 转换返回
  296 + return new PageResult<>(voList, page.getTotal());
221 } 297 }
222 298
223 299
@@ -257,4 +333,4 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService { @@ -257,4 +333,4 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
257 } 333 }
258 334
259 335
260 -}  
261 \ No newline at end of file 336 \ No newline at end of file
  337 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/filedownload/ExcelExportAsyncService.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.filedownload;
  2 +
  3 +import cn.idev.excel.EasyExcel;
  4 +import cn.idev.excel.ExcelWriter;
  5 +import cn.idev.excel.write.metadata.WriteSheet;
  6 +import com.zteits.urbanops.framework.excel.core.util.ExcelUtils;
  7 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  8 +import com.zteits.urbanops.module.garden.dal.mysql.filedownload.FileDownloadMapper;
  9 +import com.zteits.urbanops.module.garden.enums.FileTaskStatus;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.beans.factory.annotation.Value;
  12 +import org.springframework.scheduling.annotation.Async;
  13 +import org.springframework.stereotype.Service;
  14 +import org.springframework.util.CollectionUtils;
  15 +
  16 +import java.io.File;
  17 +import java.io.IOException;
  18 +import java.time.LocalDateTime;
  19 +import java.util.ArrayList;
  20 +import java.util.List;
  21 +
  22 +@Slf4j
  23 +@Service
  24 +public class ExcelExportAsyncService {
  25 +
  26 + @Value("${file.upload-local.path}")
  27 + private String localExcelPath;
  28 +
  29 + @Value("${file.upload-local.expire-days}")
  30 + private Integer expireDay;
  31 +
  32 + public static final int BATCH_SIZE = 500;
  33 +
  34 + private final FileDownloadMapper fileDownloadMapper;
  35 +
  36 + public ExcelExportAsyncService(FileDownloadMapper fileDownloadMapper) {
  37 + this.fileDownloadMapper = fileDownloadMapper;
  38 + }
  39 +
  40 + /**
  41 + * 异步导出入口
  42 + */
  43 + @Async
  44 + public <S, E> void doAsyncExport(
  45 + FileDownloadDO downloadDO,
  46 + List<S> sourceList,
  47 + String sheetName,
  48 + ExcelExportHandler<S, E> exportHandler
  49 + ) {
  50 + String busiCode = downloadDO.getBusiCode();
  51 + ExcelWriter excelWriter = null;
  52 + File excelFile = null;
  53 +
  54 + try {
  55 + // 更新为处理中
  56 + downloadDO.setStatus(FileTaskStatus.PROCESSING.getCode());
  57 + fileDownloadMapper.updateById(downloadDO);
  58 +
  59 + String fullPath = buildFilePath(downloadDO);
  60 + excelFile = new File(fullPath);
  61 + File parentDir = excelFile.getParentFile();
  62 +
  63 + if (!parentDir.exists() && !parentDir.mkdirs()) {
  64 + throw new IOException("目录创建失败:" + parentDir.getAbsolutePath());
  65 + }
  66 + if (!parentDir.canRead() || !parentDir.canWrite()) {
  67 + throw new IOException("目录无读写权限:" + parentDir.getAbsolutePath());
  68 + }
  69 +
  70 + // 初始化Excel
  71 + excelWriter = ExcelUtils.initEmergencyTaskExcel(
  72 + excelFile,
  73 + downloadDO.getFileName(),
  74 + sheetName,
  75 + exportHandler.getExcelVoClass(),
  76 + exportHandler.buildHead(),
  77 + exportHandler.buildMergeRule()
  78 + );
  79 + downloadDO.setFilePath(fullPath);
  80 + fileDownloadMapper.updateById(downloadDO);
  81 + log.info("Excel初始化完成,业务编码:{},文件路径:{}", busiCode, fullPath);
  82 +
  83 + autoPageWrite(excelWriter, downloadDO, sheetName, sourceList, exportHandler);
  84 +
  85 + ExcelUtils.finishExcel(excelWriter);
  86 + excelWriter = null;
  87 +
  88 + if (excelFile.exists()) {
  89 + downloadDO.setFileSize(excelFile.length());
  90 + }
  91 + updateSuccess(downloadDO);
  92 + log.info("导出任务执行成功,业务编码:{}", busiCode);
  93 +
  94 + } catch (IOException ioEx) {
  95 + log.error("导出IO异常,业务编码:{}", busiCode, ioEx);
  96 + updateFail(downloadDO, "文件读写异常:" + ioEx.getMessage());
  97 + } catch (Exception e) {
  98 + log.error("导出任务异常,业务编码:{}", busiCode, e);
  99 + updateFail(downloadDO, e.getMessage());
  100 + } finally {
  101 + ExcelUtils.finishExcel(excelWriter);
  102 + }
  103 + }
  104 +
  105 + /** 自动分片写入 */
  106 + private <S, E> void autoPageWrite(ExcelWriter excelWriter, FileDownloadDO downloadDO,
  107 + String sheetName, List<S> sourceList, ExcelExportHandler<S, E> handler) {
  108 + if (CollectionUtils.isEmpty(sourceList)) {
  109 + log.info("导出数据为空,业务编码:{}", downloadDO.getBusiCode());
  110 + return;
  111 + }
  112 + WriteSheet writeSheet
  113 + = EasyExcel.writerSheet(sheetName).build();
  114 + int total = sourceList.size();
  115 + int page = 0;
  116 + while (page * BATCH_SIZE < total) {
  117 + int start = page * BATCH_SIZE;
  118 + int end = Math.min(start + BATCH_SIZE, total);
  119 + List<S> batchData = new ArrayList<>(sourceList.subList(start, end));
  120 + List<E> voList = handler.convert(batchData, start + 1);
  121 + excelWriter.write(voList, writeSheet);
  122 + log.info("自动分片写入完成,分片:{}, 条数:{}", page + 1, batchData.size());
  123 + voList.clear();
  124 + page++;
  125 + }
  126 + }
  127 +
  128 + private String buildFilePath(FileDownloadDO downloadDO) {
  129 + return new File(localExcelPath, downloadDO.getFileName()).getAbsolutePath();
  130 + }
  131 +
  132 + private void updateSuccess(FileDownloadDO downloadDO) {
  133 + downloadDO.setStatus(FileTaskStatus.SUCCESS.getCode());
  134 + LocalDateTime now = LocalDateTime.now();
  135 + downloadDO.setFinishTime(now);
  136 + int days = expireDay != null ? expireDay : 30;
  137 + downloadDO.setExpireTime(now.plusDays(days));
  138 + fileDownloadMapper.updateById(downloadDO);
  139 + }
  140 +
  141 + private void updateFail(FileDownloadDO downloadDO, String errorMsg) {
  142 + downloadDO.setStatus(FileTaskStatus.FAIL.getCode());
  143 + String msg = errorMsg == null ? "未知异常" : errorMsg;
  144 + downloadDO.setFailMsg("导出失败:" + msg);
  145 + downloadDO.setFinishTime(LocalDateTime.now());
  146 + fileDownloadMapper.updateById(downloadDO);
  147 + }
  148 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/filedownload/ExcelExportDispatchService.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.filedownload;
  2 +
  3 +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
  4 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  5 +import com.zteits.urbanops.module.garden.dal.mysql.filedownload.FileDownloadMapper;
  6 +import com.zteits.urbanops.module.garden.enums.ExportBusiCodeEnum;
  7 +import com.zteits.urbanops.module.garden.enums.FileTaskStatus;
  8 +import jakarta.annotation.Resource;
  9 +import lombok.RequiredArgsConstructor;
  10 +import lombok.extern.slf4j.Slf4j;
  11 +import org.springframework.stereotype.Service;
  12 +import org.springframework.util.CollectionUtils;
  13 +
  14 +import java.util.List;
  15 +
  16 +/**
  17 + * Excel导出统一调度服务
  18 + * 统一创建导出任务、并发控制、触发异步
  19 + */
  20 +@Slf4j
  21 +@Service
  22 +@RequiredArgsConstructor
  23 +public class ExcelExportDispatchService {
  24 +
  25 + private final FileDownloadMapper fileDownloadMapper;
  26 + private final ExcelExportAsyncService excelExportAsyncService;
  27 +
  28 + /**
  29 + * 创建导出任务(可指定是否自动分片)
  30 + */
  31 + public <S, E> Long createExportTask(String busiCode, List<S> sourceList, ExcelExportHandler<S, E> exportHandler) {
  32 + ExportBusiCodeEnum busiEnum = ExportBusiCodeEnum.getByCode(busiCode);
  33 + if (busiEnum == null) {
  34 + throw new IllegalArgumentException("不支持的导出业务编码:" + busiCode);
  35 + }
  36 +
  37 + // 防并发:同业务正在处理中的任务互斥
  38 + LambdaQueryWrapper<FileDownloadDO> queryWrapper = new LambdaQueryWrapper<FileDownloadDO>()
  39 + .eq(FileDownloadDO::getBusiCode, busiCode)
  40 + .eq(FileDownloadDO::getStatus, FileTaskStatus.PROCESSING.getCode());
  41 + List<FileDownloadDO> existTasks = fileDownloadMapper.selectList(queryWrapper);
  42 + if (!CollectionUtils.isEmpty(existTasks)) {
  43 + throw new IllegalArgumentException("当前业务导出任务正在处理中,请稍后再试");
  44 + }
  45 +
  46 + // 新增任务记录
  47 + FileDownloadDO downloadDO = new FileDownloadDO();
  48 + downloadDO.setBusiCode(busiCode);
  49 + downloadDO.setBusiName(busiEnum.getBusiName());
  50 + downloadDO.setFileName(busiEnum.getFullFileName());
  51 + downloadDO.setFileSuffix(busiEnum.getFileSuffix());
  52 + downloadDO.setStatus(FileTaskStatus.PENDING.getCode());
  53 + downloadDO.setStorageType("LOCAL");
  54 + fileDownloadMapper.insert(downloadDO);
  55 +
  56 + Long taskId = downloadDO.getId();
  57 +
  58 + // 调用异步导出
  59 + excelExportAsyncService.doAsyncExport(
  60 + downloadDO,
  61 + sourceList,
  62 + busiEnum.getSheetName(),
  63 + exportHandler
  64 + );
  65 + return taskId;
  66 + }
  67 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/filedownload/ExcelExportHandler.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.filedownload;
  2 +
  3 +import org.apache.poi.ss.util.CellRangeAddress;
  4 +
  5 +import java.util.List;
  6 +
  7 +/**
  8 + * Excel导出处理器接口
  9 + * 每个导出业务实现此接口,替代原抽象类
  10 + * @param <S> 源数据类型
  11 + * @param <E> Excel VO类型
  12 + */
  13 +public interface ExcelExportHandler<S, E> {
  14 +
  15 + /**
  16 + * 源数据转换为Excel VO
  17 + */
  18 + List<E> convert(List<S> batchList, int startIndex);
  19 +
  20 + /**
  21 + * 获取Excel VO实体Class
  22 + */
  23 + Class<E> getExcelVoClass();
  24 +
  25 + /**
  26 + * 构建表头
  27 + */
  28 + List<List<String>> buildHead();
  29 +
  30 + /**
  31 + * 单元格合并规则
  32 + */
  33 + List<CellRangeAddress> buildMergeRule();
  34 +
  35 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/filedownload/FileDownloadService.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.filedownload;
  2 +
  3 +import com.zteits.urbanops.framework.common.pojo.PageResult;
  4 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.FileDownloadPageReqVO;
  5 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.FileDownloadSaveReqVO;
  6 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  7 +import jakarta.servlet.http.HttpServletResponse;
  8 +import jakarta.validation.Valid;
  9 +
  10 +import java.util.List;
  11 +
  12 +/**
  13 + * 文件异步下载任务 Service 接口
  14 + *
  15 + * @author 超级管理员
  16 + */
  17 +public interface FileDownloadService {
  18 +
  19 + /**
  20 + * 创建文件异步下载任务
  21 + *
  22 + * @param createReqVO 创建信息
  23 + * @return 编号
  24 + */
  25 + Long createFileDownload(@Valid FileDownloadSaveReqVO createReqVO);
  26 +
  27 + /**
  28 + * 更新文件异步下载任务
  29 + *
  30 + * @param updateReqVO 更新信息
  31 + */
  32 + void updateFileDownload(@Valid FileDownloadSaveReqVO updateReqVO);
  33 +
  34 + /**
  35 + * 删除文件异步下载任务
  36 + *
  37 + * @param id 编号
  38 + */
  39 + void deleteFileDownload(Long id);
  40 +
  41 + /**
  42 + * 批量删除文件异步下载任务
  43 + *
  44 + * @param ids 编号
  45 + */
  46 + void deleteFileDownloadListByIds(List<Long> ids);
  47 +
  48 + /**
  49 + * 获得文件异步下载任务
  50 + *
  51 + * @param id 编号
  52 + * @return 文件异步下载任务
  53 + */
  54 + FileDownloadDO getFileDownload(Long id);
  55 +
  56 + /**
  57 + * 获得文件异步下载任务分页
  58 + *
  59 + * @param pageReqVO 分页查询
  60 + * @return 文件异步下载任务分页
  61 + */
  62 + PageResult<FileDownloadDO> getFileDownloadPage(FileDownloadPageReqVO pageReqVO);
  63 + /**
  64 + * @Author wangqian
  65 + * @Description 文件下载
  66 + * @Date 2026/6/15 10:19
  67 + * @Return void
  68 + */
  69 + void writeAttachment(HttpServletResponse response, Long id);
  70 + /**
  71 + * @Author wangqian
  72 + * @Description 清理过期文件
  73 + * @Date 2026/6/15 12:27
  74 + * @Return void
  75 + */
  76 + void clearExpiredFiles();
  77 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/filedownload/FileDownloadServiceImpl.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.service.filedownload;
  2 +
  3 +import com.zteits.urbanops.framework.common.pojo.PageResult;
  4 +import com.zteits.urbanops.framework.common.util.object.BeanUtils;
  5 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.FileDownloadPageReqVO;
  6 +import com.zteits.urbanops.module.garden.controller.admin.filedownload.vo.FileDownloadSaveReqVO;
  7 +import com.zteits.urbanops.module.garden.dal.dataobject.filedownload.FileDownloadDO;
  8 +import com.zteits.urbanops.module.garden.dal.mysql.filedownload.FileDownloadMapper;
  9 +import jakarta.annotation.Resource;
  10 +import jakarta.servlet.http.HttpServletResponse;
  11 +import lombok.extern.slf4j.Slf4j;
  12 +import org.springframework.stereotype.Service;
  13 +import org.springframework.validation.annotation.Validated;
  14 +
  15 +import java.io.File;
  16 +import java.io.FileInputStream;
  17 +import java.io.IOException;
  18 +import java.io.OutputStream;
  19 +import java.net.URLEncoder;
  20 +import java.nio.charset.StandardCharsets;
  21 +import java.util.List;
  22 +import java.util.Objects;
  23 +
  24 +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception;
  25 +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0;
  26 +import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.FILE_DOWNLOAD_NOT_EXISTS;
  27 +
  28 +/**
  29 + * 文件异步下载任务 Service 实现类
  30 + *
  31 + * @author 超级管理员
  32 + */
  33 +@Service
  34 +@Validated
  35 +@Slf4j
  36 +public class FileDownloadServiceImpl implements FileDownloadService {
  37 +
  38 + @Resource
  39 + private FileDownloadMapper fileDownloadMapper;
  40 +
  41 + @Override
  42 + public Long createFileDownload(FileDownloadSaveReqVO createReqVO) {
  43 + // 插入
  44 + FileDownloadDO fileDownload = BeanUtils.toBean(createReqVO, FileDownloadDO.class);
  45 + fileDownloadMapper.insert(fileDownload);
  46 +
  47 + // 返回
  48 + return fileDownload.getId();
  49 + }
  50 +
  51 + @Override
  52 + public void updateFileDownload(FileDownloadSaveReqVO updateReqVO) {
  53 + // 校验存在
  54 + validateFileDownloadExists(updateReqVO.getId());
  55 + // 更新
  56 + FileDownloadDO updateObj = BeanUtils.toBean(updateReqVO, FileDownloadDO.class);
  57 + fileDownloadMapper.updateById(updateObj);
  58 + }
  59 +
  60 + @Override
  61 + public void deleteFileDownload(Long id) {
  62 + // 校验存在
  63 + validateFileDownloadExists(id);
  64 + // 删除
  65 + fileDownloadMapper.deleteById(id);
  66 + }
  67 +
  68 + @Override
  69 + public void deleteFileDownloadListByIds(List<Long> ids) {
  70 + // 删除
  71 + fileDownloadMapper.deleteByIds(ids);
  72 + }
  73 +
  74 +
  75 + private void validateFileDownloadExists(Long id) {
  76 + if (fileDownloadMapper.selectById(id) == null) {
  77 + throw exception(FILE_DOWNLOAD_NOT_EXISTS);
  78 + }
  79 + }
  80 +
  81 + @Override
  82 + public FileDownloadDO getFileDownload(Long id) {
  83 + return fileDownloadMapper.selectById(id);
  84 + }
  85 +
  86 + @Override
  87 + public PageResult<FileDownloadDO> getFileDownloadPage(FileDownloadPageReqVO pageReqVO) {
  88 + // 当前登录用户不为空,创建人为空,则当前登录用户为创建人
  89 + if (Objects.isNull(pageReqVO.getCreator())) {
  90 + throw exception0(400 ,"创建人不能为空");
  91 + }
  92 + return fileDownloadMapper.selectPage(pageReqVO);
  93 + }
  94 +
  95 + @Override
  96 + public void writeAttachment(HttpServletResponse response, Long id) {
  97 + // 1. 查询下载任务记录
  98 + FileDownloadDO fileDownload = fileDownloadMapper.selectById(id);
  99 + if (fileDownload == null) {
  100 + try {
  101 + response.setContentType("text/plain;charset=UTF-8");
  102 + response.getWriter().write("下载任务不存在");
  103 + } catch (IOException e) {
  104 + throw exception0( 400, e.getMessage());
  105 + }
  106 + return;
  107 + }
  108 +
  109 + // 2. 组装文件对象(替换为你DO中实际的文件路径/文件名字段)
  110 + String filePath = fileDownload.getFilePath();
  111 + String fileName = fileDownload.getFileName();
  112 + File file = new File(filePath);
  113 + // 3. 校验文件合法性
  114 + if (!file.exists() || !file.isFile()) {
  115 + try {
  116 + response.setContentType("text/plain;charset=UTF-8");
  117 + response.getWriter().write("文件不存在或已被删除");
  118 + } catch (IOException e) {
  119 + throw exception0( 400, e.getMessage());
  120 + }
  121 + return;
  122 + }
  123 +
  124 + try {
  125 + // 4. 设置下载响应头,解决中文文件名乱码
  126 + response.setContentType("application/octet-stream");
  127 + String encodeName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
  128 + response.setHeader("Content-Disposition", "attachment;filename=" + encodeName);
  129 + response.setContentLength((int) file.length());
  130 +
  131 + // 5. 流拷贝下载,try-with-resources 自动关闭流,防止资源泄漏
  132 + byte[] buffer = new byte[8 * 1024];
  133 + try (FileInputStream fis = new FileInputStream(file); OutputStream os = response.getOutputStream()) {
  134 +
  135 + int len;
  136 + while ((len = fis.read(buffer)) != -1) {
  137 + os.write(buffer, 0, len);
  138 + }
  139 + os.flush();
  140 + }
  141 + } catch (IOException e) {
  142 + throw exception0( 400, e.getMessage());
  143 + }
  144 + }
  145 +
  146 + @Override
  147 + public void clearExpiredFiles() {
  148 + log.info("[clearExpiredFiles][开始清理过期的文件]");
  149 + List<FileDownloadDO> fileDownload = fileDownloadMapper.selectList(FileDownloadDO::getStatus, 4);
  150 + if (fileDownload.isEmpty()) {
  151 + log.info("[clearExpiredFiles][没有过期的文件]");
  152 + return;
  153 + }
  154 + for (FileDownloadDO fileDownloadDO : fileDownload) {
  155 + log.info("[clearExpiredFiles][清理过期文件 id={}] ", fileDownloadDO.getId());
  156 + //删除本地文件
  157 + File file = new File(fileDownloadDO.getFilePath());
  158 + if (file.exists()) {
  159 + file.delete();
  160 + //log.info("[clearExpiredFiles][删除本地文件 id={}] ", fileDownloadDO.getId());
  161 + fileDownloadMapper.deleteById(fileDownloadDO.getId());
  162 + }
  163 + }
  164 + }
  165 +
  166 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/util/ImgDownUtils.java 0 → 100644
  1 +package com.zteits.urbanops.module.garden.util;
  2 +
  3 +import com.zteits.urbanops.module.infra.framework.file.core.client.FileClient;
  4 +import lombok.extern.slf4j.Slf4j;
  5 +import org.springframework.util.StringUtils;
  6 +
  7 +import java.util.ArrayList;
  8 +import java.util.List;
  9 +
  10 +/**
  11 + * @Classname FileUtils
  12 + * @Description 文件处理
  13 + * @Date 2026/6/8 22:43
  14 + * @Created by wangqian
  15 + */
  16 +@Slf4j
  17 +public class ImgDownUtils {
  18 +
  19 + /**
  20 + * 获取图片
  21 + * @param imgList
  22 + * @param index
  23 + * @return
  24 + */
  25 + public static String getImgSafe(List<String> imgList, int index) {
  26 + if (imgList == null || imgList.size() <= index || imgList.get(index) == null) {
  27 + return ""; // 空图片返回空字符串,不报错
  28 + }
  29 + return imgList.get(index);
  30 + }
  31 + /**
  32 + * 获取图片字节
  33 + * @param list
  34 + * @param idx
  35 + * @return
  36 + */
  37 + public static byte[] getByteSafe(List<byte[]> list, int idx) {
  38 + return (list == null || idx >= list.size()) ? null : list.get(idx);
  39 + }
  40 + /**
  41 + * 获取图片真实路径
  42 + * @param fullUrl
  43 + * @return
  44 + */
  45 + public static String getRealPath(String fullUrl) {
  46 + if (fullUrl == null) return null;
  47 + int index = fullUrl.indexOf("/quanyu/");
  48 + return index >= 0 ? fullUrl.substring(index + 8) : fullUrl;
  49 + }
  50 +
  51 + /**
  52 + * 批量下载图片
  53 + * @param imageUrls
  54 + * @param client
  55 + * @return
  56 + */
  57 + public static List<byte[]> batchDownloadImages(List<String> imageUrls, FileClient client) {
  58 + List<byte[]> result = new ArrayList<>();
  59 + if (imageUrls == null) return result;
  60 + for (String url : imageUrls) {
  61 + if (StringUtils.isEmpty(url)) {
  62 + result.add(null);
  63 + continue;
  64 + }
  65 + try {
  66 + String realPath = ImgDownUtils.getRealPath(url);
  67 + byte[] bytes = client.getContent(realPath);
  68 + result.add(bytes);
  69 + } catch (Exception e) {
  70 + log.error("[图片下载] 异常:{},路径:{}", e.getMessage(), url);
  71 + result.add(null);
  72 + }
  73 + }
  74 + return result;
  75 + }
  76 +}
urbanops-server/src/main/java/com/zteits/urbanops/server/UrbanopsServerApplication.java
@@ -2,6 +2,7 @@ package com.zteits.urbanops.server; @@ -2,6 +2,7 @@ package com.zteits.urbanops.server;
2 2
3 import org.springframework.boot.SpringApplication; 3 import org.springframework.boot.SpringApplication;
4 import org.springframework.boot.autoconfigure.SpringBootApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
  5 +import org.springframework.scheduling.annotation.EnableAsync;
5 6
6 /** 7 /**
7 * 项目的启动类 8 * 项目的启动类
@@ -14,6 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -14,6 +15,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
14 */ 15 */
15 @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${urbanops.info.base-package} 16 @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${urbanops.info.base-package}
16 @SpringBootApplication(scanBasePackages = {"${urbanops.info.base-package}.server", "${urbanops.info.base-package}.module"}) 17 @SpringBootApplication(scanBasePackages = {"${urbanops.info.base-package}.server", "${urbanops.info.base-package}.module"})
  18 +@EnableAsync // 开启异步线程池
17 public class UrbanopsServerApplication { 19 public class UrbanopsServerApplication {
18 20
19 public static void main(String[] args) { 21 public static void main(String[] args) {
urbanops-server/src/main/resources/application-dev.yaml
@@ -222,6 +222,9 @@ pf4j: @@ -222,6 +222,9 @@ pf4j:
222 #文件上传默认路径配置 222 #文件上传默认路径配置
223 file: 223 file:
224 path: uploadPath 224 path: uploadPath
  225 + upload-local:
  226 + path: /home/user/upload/
  227 + expire-days: 30 # 文件有效天数
225 flow: 228 flow:
226 apps: 229 apps:
227 - appid: yl 230 - appid: yl