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 7 import cn.idev.excel.metadata.data.ReadCellData;
8 8 import cn.idev.excel.metadata.data.WriteCellData;
9 9 import cn.idev.excel.metadata.property.ExcelContentProperty;
10   -import cn.idev.excel.util.FileUtils;
11 10 import cn.idev.excel.util.ListUtils;
12 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 13 import java.util.List;
21 14  
22 15 @Slf4j
23   -public class ImageListConverter implements Converter<List<String>> {
  16 +public class ImageListConverter implements Converter<List<byte[]>> {
24 17  
25 18 // 横向排列:每张图片宽度 + 间距
26 19 private static final int IMAGE_WIDTH = 120;
27 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 22 @Override
33 23 public Class<?> supportJavaTypeKey() {
... ... @@ -40,28 +30,27 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; {
40 30 }
41 31  
42 32 @Override
43   - public List<String> convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
  33 + public List<byte[]> convertToJavaData(ReadCellData readCellData, ExcelContentProperty contentProperty,
44 34 GlobalConfiguration globalConfiguration) {
45 35 return null;
46 36 }
47 37  
  38 + /**
  39 + * 这里改成接收 List<byte[]>
  40 + */
48 41 @Override
49   - public WriteCellData<?> convertToExcelData(List<String> imageList, ExcelContentProperty contentProperty,
  42 + public WriteCellData<?> convertToExcelData(List<byte[]> imageByteList, ExcelContentProperty contentProperty,
50 43 GlobalConfiguration globalConfiguration) {
51 44 try {
52   - if (imageList == null || imageList.isEmpty()) {
  45 + if (imageByteList == null || imageByteList.isEmpty()) {
53 46 return new WriteCellData<>("无图片");
54 47 }
55 48  
56 49 List<ImageData> imageDataList = ListUtils.newArrayList();
57 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 54 continue;
66 55 }
67 56  
... ... @@ -69,11 +58,11 @@ public class ImageListConverter implements Converter&lt;List&lt;String&gt;&gt; {
69 58 imageData.setImage(imageBytes);
70 59 imageData.setImageType(ImageData.ImageType.PICTURE_TYPE_PNG);
71 60  
72   - // ====================== 核心:横向偏移 ======================
  61 + // 横向排列
73 62 imageData.setLeft(leftOffset);
74   - // =============================================================
75   -
76 63 imageDataList.add(imageData);
  64 +
  65 + // 下一张图片偏移
77 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 77 return writeCellData;
89 78  
90 79 } catch (Exception e) {
91   - log.error("图片导出失败", e);
  80 + log.error("图片导出异常", e);
92 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 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 5 import cn.idev.excel.FastExcelFactory;
4 6 import cn.idev.excel.converters.longconverter.LongStringConverter;
  7 +import cn.idev.excel.write.builder.ExcelWriterBuilder;
5 8 import com.zteits.urbanops.framework.common.util.http.HttpUtils;
6 9 import com.zteits.urbanops.framework.excel.core.handler.ColumnWidthMatchStyleStrategy;
  10 +import com.zteits.urbanops.framework.excel.core.handler.HeadMergeHandler;
7 11 import com.zteits.urbanops.framework.excel.core.handler.SelectSheetWriteHandler;
8 12 import jakarta.servlet.http.HttpServletResponse;
  13 +import org.apache.poi.ss.util.CellRangeAddress;
9 14 import org.springframework.web.multipart.MultipartFile;
10 15  
  16 +import java.io.File;
  17 +import java.io.FileOutputStream;
11 18 import java.io.IOException;
12 19 import java.util.List;
13 20  
... ... @@ -39,7 +46,8 @@ public class ExcelUtils {
39 46 .registerConverter(new LongStringConverter()) // 避免 Long 类型丢失精度
40 47 .sheet(sheetName).doWrite(data);
41 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 51 response.setContentType("application/vnd.ms-excel;charset=UTF-8");
44 52 }
45 53  
... ... @@ -74,4 +82,82 @@ public class ExcelUtils {
74 82 .sheet(0)
75 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 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 19 import jakarta.annotation.Resource;
7   -import org.springframework.validation.annotation.Validated;
  20 +import jakarta.validation.Valid;
  21 +import lombok.extern.slf4j.Slf4j;
8 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 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 31 @Tag(name = "管理后台 - 抢险任务主")
34 32 @RestController
35 33 @RequestMapping("/garden/emergency-task")
36 34 @Validated
  35 +@Slf4j
37 36 public class EmergencyTaskController {
38 37  
39 38 @Resource
40 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 51 @PostMapping("/create")
43 52 @Operation(summary = "创建抢险任务主")
... ... @@ -100,81 +109,42 @@ public class EmergencyTaskController {
100 109 @Operation(summary = "导出抢险任务主 Excel")
101 110 @PreAuthorize("@ss.hasPermission('garden:emergency-task:export')")
102 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 113 pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
106 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 28 @ExcelProperty("险情类型")
29 29 private String emergencyTypeName;
30 30  
  31 + @Schema(description = "险情类型数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "5209")
  32 + @ExcelProperty("数量/株")
  33 + private Integer emergencyTypeNum;
  34 +
31 35 @Schema(description = "险情危害(如:无)", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
32 36 @ExcelProperty("险情危害")
33 37 private String emergencyHarmName;
34 38  
  39 + @Schema(description = "险情危害数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "16325")
  40 + @ExcelProperty("数量")
  41 + private Integer emergencyHarmNum;
  42 +
35 43 @Schema(description = "树木权属(如:专业权属)", requiredMode = Schema.RequiredMode.REQUIRED, example = "19498")
36 44 @ExcelProperty("树木权属")
37 45 private String treeOwnershipName;
... ... @@ -40,6 +48,10 @@ public class EmergencyTaskExcleRespVO {
40 48 @ExcelProperty("险情地点")
41 49 private String emergencyLocation;
42 50  
  51 + @Schema(description = "所属街道", requiredMode = Schema.RequiredMode.REQUIRED)
  52 + @ExcelProperty("所属街道")
  53 + private String streetName;
  54 +
43 55 @Schema(description = "上报人", requiredMode = Schema.RequiredMode.REQUIRED)
44 56 @ExcelProperty("上报人")
45 57 private String reporter;
... ... @@ -56,7 +68,7 @@ public class EmergencyTaskExcleRespVO {
56 68 //@ExcelProperty(value="抢险中照片", converter = ListToStringConverter.class)
57 69 @ExcelProperty(value = "抢险中照片", converter = ImageListConverter.class)
58 70 @ColumnWidth(70)
59   - private List<String> emergencyImg;
  71 + private List<byte[]> emergencyImg;
60 72  
61 73 @Schema(description = "树种(如:国槐)")
62 74 @ExcelProperty("树种")
... ... @@ -78,7 +90,7 @@ public class EmergencyTaskExcleRespVO {
78 90 //@ExcelProperty(value="抢险结束照片", converter = ListToStringConverter.class)
79 91 @ExcelProperty(value = "抢险结束照片", converter = ImageListConverter.class)
80 92 @ColumnWidth(70)
81   - private List<String> emergencyEndImg;
  93 + private List<byte[]> emergencyEndImg;
82 94  
83 95 @Schema(description = "备注")
84 96 @ExcelProperty("备注")
... ... @@ -96,7 +108,6 @@ public class EmergencyTaskExcleRespVO {
96 108 @ExcelProperty("审批备注")
97 109 private String approvalRemarks;
98 110  
99   -
100 111 @Schema(description = "创建者", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
101 112 @ExcelProperty("创建者")
102 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 46 @Schema(description = "归属(一级部门id)", example = "6353")
47 47 private Long companyId;
48 48  
  49 + @Schema(description = "所在街道ID", example = "1111111")
  50 + private String streetId;
49 51  
50 52 private List<Integer> busiStatusList;
51 53  
52   -}
53 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 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 5 import com.zteits.urbanops.framework.excel.core.convert.ListToStringConverter;
4 6 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO;
5 7 import io.swagger.v3.oas.annotations.media.Schema;
  8 +import jakarta.validation.constraints.NotEmpty;
6 9 import lombok.*;
7 10 import java.util.*;
8 11 import java.time.LocalDateTime;
... ... @@ -136,4 +139,15 @@ public class EmergencyTaskRespVO {
136 139 @ExcelProperty("部门")
137 140 private String deptName;
138 141  
139   -}
140 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 1 package com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo;
2 2  
  3 +import cn.idev.excel.annotation.ExcelProperty;
3 4 import io.swagger.v3.oas.annotations.media.Schema;
4 5 import lombok.*;
5 6 import java.util.*;
6 7 import jakarta.validation.constraints.*;
7 8 import org.springframework.format.annotation.DateTimeFormat;
8 9 import java.time.LocalDateTime;
  10 +import java.util.List;
9 11  
10 12 @Schema(description = "管理后台 - 抢险任务主新增/修改 Request VO")
11 13 @Data
... ... @@ -94,6 +96,16 @@ public class EmergencyTaskSaveReqVO {
94 96 private String approvalUserName;
95 97  
96 98 @Schema(description = "使用机械", requiredMode = Schema.RequiredMode.REQUIRED, example = "赵六")
97   - private List<EmergencyMachinerySaveReqVO> emergencyMachineryList;
  99 + private List<EmergencyMachinerySaveReqVO> emergencyMachineryList;
98 100  
99   -}
100 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 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 54 \ No newline at end of file
... ...
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/dataobject/emergencytask/EmergencyTaskDO.java
1 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 8 import com.zteits.urbanops.framework.mybatis.core.type.StringArrayToListTypeHandler;
4   -import com.zteits.urbanops.framework.mybatis.core.type.StringListTypeHandler;
5 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 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 15 * 抢险任务主 DO
... ... @@ -139,6 +138,16 @@ public class EmergencyTaskDO extends BaseDO {
139 138 * 更新者
140 139 */
141 140 private String updaterName;
142   -
143   -
144   -}
145 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 40 .likeIfPresent(EmergencyTaskDO::getEmergencyLocation, reqVO.getEmergencyLocation())
41 41 .eqIfPresent(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatus())
42 42 .eqIfPresent(EmergencyTaskDO::getCompanyId, reqVO.getCompanyId())
  43 + .eqIfPresent(EmergencyTaskDO::getStreetId, reqVO.getStreetId())
43 44 .betweenIfPresent(EmergencyTaskDO::getDiscoveryTime, startDateTime, endDateTime);
44 45 if(!CollectionUtils.isEmpty(reqVO.getBusiStatusList())){
45 46 sql.in(EmergencyTaskDO::getBusiStatus, reqVO.getBusiStatusList());
... ... @@ -50,4 +51,4 @@ public interface EmergencyTaskMapper extends BaseMapperX&lt;EmergencyTaskDO&gt; {
50 51  
51 52 int deleteByTaskNo(@Param("taskNo") String taskNo);
52 53  
53   -}
54 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 155 // ========== 一树一档案巡检与风险评估 1-100-008-000 ==========
156 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 2  
3 3 import cn.hutool.core.collection.CollUtil;
4 4 import cn.hutool.core.collection.CollectionUtil;
  5 +import cn.hutool.core.util.ObjectUtil;
5 6 import cn.hutool.core.util.RandomUtil;
6 7 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
7 8 import com.zteits.urbanops.framework.common.biz.system.dict.dto.DictDataRespDTO;
8 9 import com.zteits.urbanops.framework.datapermission.core.annotation.DataPermission;
9 10 import com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils;
10 11 import com.zteits.urbanops.module.garden.dal.dataobject.emergencytask.EmergencyMachineryDO;
  12 +import com.zteits.urbanops.module.garden.dal.dataobject.road.RoadStreetDO;
11 13 import com.zteits.urbanops.module.garden.dal.mysql.emergencytask.EmergencyMachineryMapper;
  14 +import com.zteits.urbanops.module.garden.dal.mysql.road.RoadStreetMapper;
12 15 import com.zteits.urbanops.module.system.api.dept.DeptApi;
13 16 import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO;
14 17 import com.zteits.urbanops.module.system.api.dict.DictDataApi;
15 18 import org.apache.commons.lang3.StringUtils;
  19 +import org.apache.poi.ss.formula.functions.T;
16 20 import org.springframework.stereotype.Service;
17 21 import jakarta.annotation.Resource;
18 22 import org.springframework.util.CollectionUtils;
... ... @@ -20,6 +24,7 @@ import org.springframework.validation.annotation.Validated;
20 24 import org.springframework.transaction.annotation.Transactional;
21 25  
22 26 import java.util.*;
  27 +import java.util.concurrent.atomic.AtomicInteger;
23 28 import java.util.stream.Collectors;
24 29  
25 30 import com.zteits.urbanops.module.garden.controller.admin.emergencytask.vo.*;
... ... @@ -56,6 +61,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
56 61 @Resource
57 62 private DictDataApi dictDataApi;
58 63  
  64 + @Resource
  65 + private RoadStreetMapper roadStreetMapper;
  66 +
59 67 @Transactional(rollbackFor = Exception.class)
60 68 @Override
61 69 public Long createEmergencyTask(EmergencyTaskSaveReqVO createReqVO, boolean isSave) {
... ... @@ -92,6 +100,9 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
92 100 emergencyTask.setUpdater(userId);
93 101 emergencyTask.setUpdaterName(userName);
94 102 emergencyTask.setApprovalRemarks("");
  103 + emergencyTask.setStreetId(createReqVO.getStreetId());
  104 + emergencyTask.setEmergencyHarmNum(createReqVO.getEmergencyHarmNum());
  105 + emergencyTask.setEmergencyTypeNum(createReqVO.getEmergencyTypeNum());
95 106 if(isSave){
96 107 emergencyTask.setCreator(userId);
97 108 emergencyTask.setCreatorName(userName);
... ... @@ -202,22 +213,87 @@ public class EmergencyTaskServiceImpl implements EmergencyTaskService {
202 213 if(companyId != null && companyId.equals(100L)){
203 214 pageReqVO.setBusiStatusList(Arrays.asList(2,3,4,5));
204 215 }
  216 + // 2. 分页查询主表数据
205 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 333 }
258 334  
259 335  
260   -}
261 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 2  
3 3 import org.springframework.boot.SpringApplication;
4 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 15 */
15 16 @SuppressWarnings("SpringComponentScan") // 忽略 IDEA 无法识别 ${urbanops.info.base-package}
16 17 @SpringBootApplication(scanBasePackages = {"${urbanops.info.base-package}.server", "${urbanops.info.base-package}.module"})
  18 +@EnableAsync // 开启异步线程池
17 19 public class UrbanopsServerApplication {
18 20  
19 21 public static void main(String[] args) {
... ...
urbanops-server/src/main/resources/application-dev.yaml
... ... @@ -222,6 +222,9 @@ pf4j:
222 222 #文件上传默认路径配置
223 223 file:
224 224 path: uploadPath
  225 + upload-local:
  226 + path: /home/user/upload/
  227 + expire-days: 30 # 文件有效天数
225 228 flow:
226 229 apps:
227 230 - appid: yl
... ...