Commit e7f5653cbd65c6cb10ea14b40072ca91c5fad58b
1 parent
f461431b
注释人才机
Showing
2 changed files
with
442 additions
and
442 deletions
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java
| 1 | -package com.zteits.urbanops.module.garden.controller.admin.costfee; | ||
| 2 | - | ||
| 3 | -import com.zteits.urbanops.framework.common.pojo.CommonResult; | ||
| 4 | -import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; | ||
| 5 | -import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport; | ||
| 6 | -import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 7 | -import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; | ||
| 8 | -import io.swagger.v3.oas.annotations.Operation; | ||
| 9 | -import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 10 | -import jakarta.servlet.http.HttpServletResponse; | ||
| 11 | -import lombok.AllArgsConstructor; | ||
| 12 | -import lombok.Data; | ||
| 13 | -import lombok.extern.slf4j.Slf4j; | ||
| 14 | -import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | -import org.springframework.beans.factory.annotation.Value; | ||
| 16 | -import org.springframework.core.io.ClassPathResource; | ||
| 17 | -import org.springframework.util.CollectionUtils; | ||
| 18 | -import org.springframework.web.bind.annotation.*; | ||
| 19 | -import org.springframework.web.multipart.MultipartFile; | ||
| 20 | - | ||
| 21 | -import java.io.IOException; | ||
| 22 | -import java.io.InputStream; | ||
| 23 | -import java.io.OutputStream; | ||
| 24 | -import java.lang.reflect.Method; | ||
| 25 | -import java.net.URLEncoder; | ||
| 26 | -import java.nio.charset.StandardCharsets; | ||
| 27 | -import java.util.*; | ||
| 28 | - | ||
| 29 | -import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||
| 30 | -import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | ||
| 31 | - | ||
| 32 | -/** | ||
| 33 | - * 人机材公共处理控制器 | ||
| 34 | - * 处理Excel导入预览、提交等功能 | ||
| 35 | - */ | ||
| 36 | -@Tag(name = "管理后台 - 人机材批量导入") | ||
| 37 | -@RestController | ||
| 38 | -@RequestMapping("/costfee") | ||
| 39 | -@Slf4j | ||
| 40 | -public class CostFeeController{ | ||
| 41 | - | ||
| 42 | - @Autowired | ||
| 43 | - private CostFeeService costFeeService; | ||
| 44 | - | ||
| 45 | - // 静态资源服务器基础URL(从配置文件读取) | ||
| 46 | - @Value("${static.resource.server.url}") | ||
| 47 | - private String staticResourceServerUrl; | ||
| 48 | - | ||
| 49 | - @Value("${static.resource.server.enable}") | ||
| 50 | - private Boolean staticResourceServerenable; | ||
| 51 | - | ||
| 52 | - // Excel文件校验相关 | ||
| 53 | - private static final List<String> ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm"); | ||
| 54 | - private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; | ||
| 55 | - private static final String EXCEL_MIME_TYPE_XLS = "application/vnd.ms-excel"; | ||
| 56 | - | ||
| 57 | - /** | ||
| 58 | - * 导入模板下载 | ||
| 59 | - */ | ||
| 60 | - @GetMapping("/get-import-template") | ||
| 61 | - @Operation(summary = "获得导入用户模板") | ||
| 62 | - public void importTemplate(HttpServletResponse response, | ||
| 63 | - @RequestParam("templateName") String templateName) throws IOException { | ||
| 64 | - // 参数校验 | ||
| 65 | - if (templateName == null || templateName.trim().isEmpty()) { | ||
| 66 | - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "模板名称不能为空"); | ||
| 67 | - return; | ||
| 68 | - } | ||
| 69 | - | ||
| 70 | - // 模板类型校验 | ||
| 71 | - TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 72 | - if (template == null) { | ||
| 73 | - response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未知的模板类型: " + templateName); | ||
| 74 | - return; | ||
| 75 | - } | ||
| 76 | - | ||
| 77 | - if (staticResourceServerenable) { | ||
| 78 | - // 构建完整的静态服务器URL(确保路径正确) | ||
| 79 | - String serverUrl = staticResourceServerUrl; | ||
| 80 | - if (serverUrl != null && !serverUrl.endsWith("/")) { | ||
| 81 | - serverUrl += "/"; // 确保URL以/结尾,避免拼接错误 | ||
| 82 | - } | ||
| 83 | - | ||
| 84 | - String encodedFileName = URLEncoder.encode(template.getName().split("\\.")[0], StandardCharsets.UTF_8.name()); | ||
| 85 | - | ||
| 86 | - String templateFileName = encodedFileName + "." + template.getName().split("\\.")[1]; | ||
| 87 | - | ||
| 88 | - String templateUrl = serverUrl + templateFileName; // 包含templates目录 | ||
| 89 | - | ||
| 90 | - // 方式1:重定向到静态资源服务器 | ||
| 91 | - response.sendRedirect(templateUrl); | ||
| 92 | - } else { | ||
| 93 | - String templateFileName = templateName + "." + template.getName().split("\\.")[1]; | ||
| 94 | - | ||
| 95 | - // 写入模板文件到响应 | ||
| 96 | - writeTemplateToResponse(response, template.getName(), templateFileName); | ||
| 97 | - } | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - | ||
| 101 | - /** | ||
| 102 | - * Excel文件上传预览 | ||
| 103 | - */ | ||
| 104 | - @PostMapping("/preview") | ||
| 105 | - @Operation(summary = "文件上传") | ||
| 106 | - public CommonResult previewExcel(@RequestParam("file") MultipartFile file, | ||
| 107 | - @RequestParam("templateName") String templateName) { | ||
| 108 | - // 基础参数校验 | ||
| 109 | - if (templateName == null || templateName.trim().isEmpty()) { | ||
| 110 | - return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 111 | - | ||
| 112 | - } | ||
| 113 | - // 模板类型校验 | ||
| 114 | - TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 115 | - if (template == null) { | ||
| 116 | - return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 117 | - } | ||
| 118 | - String suffix = template.getName().split("\\.")[1]; | ||
| 119 | - // 文件有效性校验 | ||
| 120 | - if (!isValidExcelFile(file)) { | ||
| 121 | - return CommonResult.error(BAD_REQUEST, "请上传有效的Excel文件"); | ||
| 122 | - } | ||
| 123 | - //部门信息缓存,校验正确性使用 | ||
| 124 | - | ||
| 125 | - | ||
| 126 | - // 解析Excel并获取数据 | ||
| 127 | - ImportResult<?> result = parseExcelFile(file, template); | ||
| 128 | - | ||
| 129 | - // 需预览的数据存入Redis并返回批次ID | ||
| 130 | - if (result.isNeedPreview()) { | ||
| 131 | - if (CollectionUtils.isEmpty(result.getDataList())) { | ||
| 132 | - return CommonResult.error(BAD_REQUEST, "导入的Excel文件不能为空"); | ||
| 133 | - | ||
| 134 | - } | ||
| 135 | - String batchId = costFeeService.saveDataToRedis(result.getDataList(), templateName); | ||
| 136 | - Map<String, Object> resultMap = new HashMap<>(2); | ||
| 137 | - resultMap.put("batchId", batchId); | ||
| 138 | - resultMap.put("totalCount", result.getDataList().size()); | ||
| 139 | - resultMap.put("templateName",templateName); | ||
| 140 | - return success(resultMap); | ||
| 141 | - } | ||
| 142 | - // 无需预览的直接返回数据 | ||
| 143 | - return success(result.getDataList()); | ||
| 144 | - } | ||
| 145 | - | ||
| 146 | - | ||
| 147 | - /** | ||
| 148 | - * 分页获取预览数据 | ||
| 149 | - */ | ||
| 150 | - @GetMapping("/preview-data") | ||
| 151 | - @Operation(summary = "批量上传分页预览") | ||
| 152 | - public CommonResult getPreviewData(@RequestParam("batchId") String batchId, | ||
| 153 | - @RequestParam("templateName") String templateName, | ||
| 154 | - @RequestParam(value = "page", defaultValue = "1") int page, | ||
| 155 | - @RequestParam(value = "size", defaultValue = "100000") int size) { | ||
| 156 | - | ||
| 157 | - | ||
| 158 | - // 参数校验 | ||
| 159 | - if (batchId == null || batchId.trim().isEmpty()) { | ||
| 160 | - return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); | ||
| 161 | - } | ||
| 162 | - if (templateName == null || templateName.trim().isEmpty()) { | ||
| 163 | - return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 164 | - | ||
| 165 | - } | ||
| 166 | - if (TemplateEnum.getByCode(templateName) == null) { | ||
| 167 | - return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 168 | - | ||
| 169 | - } | ||
| 170 | - try { | ||
| 171 | - //if (page < 1 || size < 1 || size > 100) { // 限制每页最大100条,避免过大 | ||
| 172 | - // return AjaxResult.error("分页参数无效(page≥1,1≤size≤100)"); | ||
| 173 | - //} | ||
| 174 | - | ||
| 175 | - // 模板类型校验 | ||
| 176 | - TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 177 | - Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 178 | - | ||
| 179 | - // 从Redis获取分页数据 | ||
| 180 | - PageResult<?> pageResult = costFeeService.getPagedDataFromRedis(batchId, templateName, page, size, importClass); | ||
| 181 | - return CommonResult.success(pageResult); | ||
| 182 | - } catch (Exception e) { | ||
| 183 | - log.error("获取预览数据失败", e); | ||
| 184 | - throw new RuntimeException(e); | ||
| 185 | - } | ||
| 186 | - } | ||
| 187 | - | ||
| 188 | - | ||
| 189 | - /** | ||
| 190 | - * 提交导入数据到数据库 | ||
| 191 | - */ | ||
| 192 | - @PostMapping("/submit") | ||
| 193 | - @Operation(summary = "批次保存") | ||
| 194 | - public CommonResult submitImport(@RequestParam("batchId") String batchId, | ||
| 195 | - @RequestParam("templateName") String templateName, | ||
| 196 | - @RequestParam("operator") String operator) { | ||
| 197 | - // 参数校验 | ||
| 198 | - if (batchId == null || batchId.trim().isEmpty()) { | ||
| 199 | - return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); | ||
| 200 | - } | ||
| 201 | - if (templateName == null || templateName.trim().isEmpty()) { | ||
| 202 | - return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 203 | - | ||
| 204 | - } | ||
| 205 | - if (TemplateEnum.getByCode(templateName) == null) { | ||
| 206 | - return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 207 | - | ||
| 208 | - } | ||
| 209 | - if (operator == null || operator.trim().isEmpty()) { | ||
| 210 | - return CommonResult.error(BAD_REQUEST, "操作人不能为空"); | ||
| 211 | - } | ||
| 212 | - int count = costFeeService.submitImport(batchId, templateName, operator); | ||
| 213 | - return CommonResult.success("导入成功,共" + count + "条数据"); | ||
| 214 | - } | ||
| 215 | - | ||
| 216 | - /** | ||
| 217 | - * 写入模板文件到响应流 | ||
| 218 | - */ | ||
| 219 | - private void writeTemplateToResponse(HttpServletResponse response, String filename, String templatePath) throws IOException { | ||
| 220 | - // 设置响应头 | ||
| 221 | - response.setContentType(EXCEL_MIME_TYPE); | ||
| 222 | - response.setCharacterEncoding("UTF-8"); | ||
| 223 | - String encodedFileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20"); | ||
| 224 | - response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); | ||
| 225 | - response.setHeader("Cache-Control", "no-store"); // 禁止缓存 | ||
| 226 | - | ||
| 227 | - // 读取模板文件并写入响应 | ||
| 228 | - try (InputStream in = new ClassPathResource("template/" + templatePath).getInputStream(); | ||
| 229 | - OutputStream out = response.getOutputStream()) { | ||
| 230 | - | ||
| 231 | - byte[] buffer = new byte[4096]; | ||
| 232 | - int bytesRead; | ||
| 233 | - while ((bytesRead = in.read(buffer)) != -1) { | ||
| 234 | - out.write(buffer, 0, bytesRead); | ||
| 235 | - } | ||
| 236 | - out.flush(); | ||
| 237 | - } | ||
| 238 | - } | ||
| 239 | - | ||
| 240 | - | ||
| 241 | - /** | ||
| 242 | - * 校验Excel文件有效性(后缀+MIME类型) | ||
| 243 | - */ | ||
| 244 | - private boolean isValidExcelFile(MultipartFile file) { | ||
| 245 | - if (file == null || file.isEmpty()) { | ||
| 246 | - return false; | ||
| 247 | - } | ||
| 248 | - String fileName = file.getOriginalFilename(); | ||
| 249 | - if (fileName == null) { | ||
| 250 | - return false; | ||
| 251 | - } | ||
| 252 | - | ||
| 253 | - // 校验文件后缀 | ||
| 254 | - boolean validSuffix = ALLOWED_EXCEL_SUFFIX.stream() | ||
| 255 | - .anyMatch(suffix -> fileName.toLowerCase().endsWith(suffix)); | ||
| 256 | - if (!validSuffix) { | ||
| 257 | - return false; | ||
| 258 | - } | ||
| 259 | - | ||
| 260 | - // 4. 校验MIME类型(兼容可能带参数的MIME类型,如charset) | ||
| 261 | - String contentType = file.getContentType(); | ||
| 262 | - if (contentType == null) { | ||
| 263 | - return false; | ||
| 264 | - } | ||
| 265 | - // 处理MIME类型可能带参数的情况(如"application/xxx;charset=UTF-8") | ||
| 266 | - String baseContentType = contentType.split(";")[0].trim(); | ||
| 267 | - // 3. 匹配预设的Excel MIME类型(.xlsx/.xlsm或.xls) | ||
| 268 | - return EXCEL_MIME_TYPE.equals(baseContentType) | ||
| 269 | - || baseContentType.startsWith(EXCEL_MIME_TYPE_XLS); | ||
| 270 | - } | ||
| 271 | - | ||
| 272 | - | ||
| 273 | - /** | ||
| 274 | - * 解析Excel文件并返回导入结果 | ||
| 275 | - */ | ||
| 276 | - @SuppressWarnings("unchecked") | ||
| 277 | - private <T> ImportResult<T> parseExcelFile(MultipartFile file, TemplateEnum template){ | ||
| 278 | - // 获取模板对应的导入类 | ||
| 279 | - Class<T> importClass = (Class<T>) template.getImportClass(); | ||
| 280 | - List<T> dataList = null; | ||
| 281 | - try { | ||
| 282 | - // 解析Excel(从指定行开始读取) | ||
| 283 | - dataList = ExcelUtils.read(file, importClass, template.getStartIndex()); | ||
| 284 | - if (dataList == null) { | ||
| 285 | - dataList = Collections.emptyList(); | ||
| 286 | - } | ||
| 287 | - // 关键:移除最后一行(以“说明”开头的行) | ||
| 288 | - removeLastLineIfStartWithExplain(dataList, importClass); | ||
| 289 | - // 基础数据校验(非空、格式等) | ||
| 290 | - costFeeService.validateImportData(dataList, template); | ||
| 291 | - | ||
| 292 | - } catch (IOException e) { | ||
| 293 | - log.error(e.getMessage()); | ||
| 294 | - } | ||
| 295 | - return new ImportResult<>(dataList, template.isNeedPreview()); | ||
| 296 | - } | ||
| 297 | - /** | ||
| 298 | - * 移除列表最后一行(若该行第一列内容以“说明”开头) | ||
| 299 | - * @param dataList Excel解析后的实体类列表 | ||
| 300 | - * @param clazz 实体类字节码(用于反射获取第一列字段值) | ||
| 301 | - * @param <T> 泛型:Excel对应的实体类 | ||
| 302 | - */ | ||
| 303 | - private <T> void removeLastLineIfStartWithExplain(List<T> dataList, Class<T> clazz) { | ||
| 304 | - // 1. 跳过空列表/元素数<1的情况,避免索引越界 | ||
| 305 | - if (CollectionUtils.isEmpty(dataList)) { | ||
| 306 | - return; | ||
| 307 | - } | ||
| 308 | - | ||
| 309 | - // 2. 获取最后一个元素(正确索引:size()-1) | ||
| 310 | - T lastItem = dataList.get(dataList.size() - 1); | ||
| 311 | - if (Objects.isNull(lastItem)) { | ||
| 312 | - return; | ||
| 313 | - } | ||
| 314 | - | ||
| 315 | - // 3. 反射获取「第一列字段的值」(核心:Excel列对应实体类的字段,假设第一列字段名是 firstColumn,需根据实际调整) | ||
| 316 | - String firstColumnValue = getFirstColumnValue(lastItem, clazz); | ||
| 317 | - if (Objects.isNull(firstColumnValue)) { | ||
| 318 | - return; | ||
| 319 | - } | ||
| 320 | - | ||
| 321 | - // 4. 匹配“说明”(trim() 避免空格干扰,如“ 说明:xxx”) | ||
| 322 | - if (firstColumnValue.trim().startsWith("说明")) { | ||
| 323 | - // 移除最后一个元素(正确索引:size()-1) | ||
| 324 | - dataList.remove(dataList.size() - 1); | ||
| 325 | - } | ||
| 326 | - } | ||
| 327 | - | ||
| 328 | - /** | ||
| 329 | - * 反射获取实体类的「第一列字段值」(需根据你的Excel列配置调整字段名) | ||
| 330 | - * @param item 实体类对象(Excel一行数据) | ||
| 331 | - * @param clazz 实体类字节码 | ||
| 332 | - * @return 第一列的字符串值(null 表示无值) | ||
| 333 | - */ | ||
| 334 | - private <T> String getFirstColumnValue(T item, Class<T> clazz) { | ||
| 335 | - try { | ||
| 336 | - String firstColumnFieldName = ""; | ||
| 337 | - if (WaterFeeImport.class.isAssignableFrom(clazz)) { | ||
| 338 | - firstColumnFieldName = "feeMonth"; | ||
| 339 | - } else { | ||
| 340 | - return null; | ||
| 341 | - } | ||
| 342 | - | ||
| 343 | - // 反射获取 getter 方法() | ||
| 344 | - String getterMethodName = "get" + firstColumnFieldName.substring(0, 1).toUpperCase() + firstColumnFieldName.substring(1); | ||
| 345 | - Method getterMethod = clazz.getDeclaredMethod(getterMethodName); | ||
| 346 | - | ||
| 347 | - // 执行 getter 方法获取字段值 | ||
| 348 | - Object value = getterMethod.invoke(item); | ||
| 349 | - return value == null ? null : value.toString(); | ||
| 350 | - } catch (Exception e) { | ||
| 351 | - // 反射失败(字段名错误、无getter方法),打印日志不抛异常,避免影响整体流程 | ||
| 352 | - log.warn("获取Excel第一列字段值失败,实体类:{}", clazz.getName(), e); | ||
| 353 | - return null; | ||
| 354 | - } | ||
| 355 | - } | ||
| 356 | - | ||
| 357 | - /** | ||
| 358 | - * 导入结果封装 | ||
| 359 | - */ | ||
| 360 | - @Data | ||
| 361 | - @AllArgsConstructor | ||
| 362 | - private static class ImportResult<T> { | ||
| 363 | - private List<T> dataList; | ||
| 364 | - private boolean needPreview; | ||
| 365 | - } | ||
| 366 | - | ||
| 367 | - /** | ||
| 368 | - * 分页结果封装 | ||
| 369 | - */ | ||
| 370 | - @Data | ||
| 371 | - @AllArgsConstructor | ||
| 372 | - public static class PageResult<T> { | ||
| 373 | - private int page; | ||
| 374 | - private int size; | ||
| 375 | - private int total; | ||
| 376 | - private List<T> data; | ||
| 377 | - } | ||
| 378 | -} | 1 | +//package com.zteits.urbanops.module.garden.controller.admin.costfee; |
| 2 | +// | ||
| 3 | +//import com.zteits.urbanops.framework.common.pojo.CommonResult; | ||
| 4 | +//import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; | ||
| 5 | +//import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport; | ||
| 6 | +//import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 7 | +//import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; | ||
| 8 | +//import io.swagger.v3.oas.annotations.Operation; | ||
| 9 | +//import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 10 | +//import jakarta.servlet.http.HttpServletResponse; | ||
| 11 | +//import lombok.AllArgsConstructor; | ||
| 12 | +//import lombok.Data; | ||
| 13 | +//import lombok.extern.slf4j.Slf4j; | ||
| 14 | +//import org.springframework.beans.factory.annotation.Autowired; | ||
| 15 | +//import org.springframework.beans.factory.annotation.Value; | ||
| 16 | +//import org.springframework.core.io.ClassPathResource; | ||
| 17 | +//import org.springframework.util.CollectionUtils; | ||
| 18 | +//import org.springframework.web.bind.annotation.*; | ||
| 19 | +//import org.springframework.web.multipart.MultipartFile; | ||
| 20 | +// | ||
| 21 | +//import java.io.IOException; | ||
| 22 | +//import java.io.InputStream; | ||
| 23 | +//import java.io.OutputStream; | ||
| 24 | +//import java.lang.reflect.Method; | ||
| 25 | +//import java.net.URLEncoder; | ||
| 26 | +//import java.nio.charset.StandardCharsets; | ||
| 27 | +//import java.util.*; | ||
| 28 | +// | ||
| 29 | +//import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||
| 30 | +//import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; | ||
| 31 | +// | ||
| 32 | +///** | ||
| 33 | +// * 人机材公共处理控制器 | ||
| 34 | +// * 处理Excel导入预览、提交等功能 | ||
| 35 | +// */ | ||
| 36 | +//@Tag(name = "管理后台 - 人机材批量导入") | ||
| 37 | +//@RestController | ||
| 38 | +//@RequestMapping("/costfee") | ||
| 39 | +//@Slf4j | ||
| 40 | +//public class CostFeeController{ | ||
| 41 | +// | ||
| 42 | +// @Autowired | ||
| 43 | +// private CostFeeService costFeeService; | ||
| 44 | +// | ||
| 45 | +// // 静态资源服务器基础URL(从配置文件读取) | ||
| 46 | +// @Value("${static.resource.server.url}") | ||
| 47 | +// private String staticResourceServerUrl; | ||
| 48 | +// | ||
| 49 | +// @Value("${static.resource.server.enable}") | ||
| 50 | +// private Boolean staticResourceServerenable; | ||
| 51 | +// | ||
| 52 | +// // Excel文件校验相关 | ||
| 53 | +// private static final List<String> ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm"); | ||
| 54 | +// private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; | ||
| 55 | +// private static final String EXCEL_MIME_TYPE_XLS = "application/vnd.ms-excel"; | ||
| 56 | +// | ||
| 57 | +// /** | ||
| 58 | +// * 导入模板下载 | ||
| 59 | +// */ | ||
| 60 | +// @GetMapping("/get-import-template") | ||
| 61 | +// @Operation(summary = "获得导入用户模板") | ||
| 62 | +// public void importTemplate(HttpServletResponse response, | ||
| 63 | +// @RequestParam("templateName") String templateName) throws IOException { | ||
| 64 | +// // 参数校验 | ||
| 65 | +// if (templateName == null || templateName.trim().isEmpty()) { | ||
| 66 | +// response.sendError(HttpServletResponse.SC_BAD_REQUEST, "模板名称不能为空"); | ||
| 67 | +// return; | ||
| 68 | +// } | ||
| 69 | +// | ||
| 70 | +// // 模板类型校验 | ||
| 71 | +// TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 72 | +// if (template == null) { | ||
| 73 | +// response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未知的模板类型: " + templateName); | ||
| 74 | +// return; | ||
| 75 | +// } | ||
| 76 | +// | ||
| 77 | +// if (staticResourceServerenable) { | ||
| 78 | +// // 构建完整的静态服务器URL(确保路径正确) | ||
| 79 | +// String serverUrl = staticResourceServerUrl; | ||
| 80 | +// if (serverUrl != null && !serverUrl.endsWith("/")) { | ||
| 81 | +// serverUrl += "/"; // 确保URL以/结尾,避免拼接错误 | ||
| 82 | +// } | ||
| 83 | +// | ||
| 84 | +// String encodedFileName = URLEncoder.encode(template.getName().split("\\.")[0], StandardCharsets.UTF_8.name()); | ||
| 85 | +// | ||
| 86 | +// String templateFileName = encodedFileName + "." + template.getName().split("\\.")[1]; | ||
| 87 | +// | ||
| 88 | +// String templateUrl = serverUrl + templateFileName; // 包含templates目录 | ||
| 89 | +// | ||
| 90 | +// // 方式1:重定向到静态资源服务器 | ||
| 91 | +// response.sendRedirect(templateUrl); | ||
| 92 | +// } else { | ||
| 93 | +// String templateFileName = templateName + "." + template.getName().split("\\.")[1]; | ||
| 94 | +// | ||
| 95 | +// // 写入模板文件到响应 | ||
| 96 | +// writeTemplateToResponse(response, template.getName(), templateFileName); | ||
| 97 | +// } | ||
| 98 | +// } | ||
| 99 | +// | ||
| 100 | +// | ||
| 101 | +// /** | ||
| 102 | +// * Excel文件上传预览 | ||
| 103 | +// */ | ||
| 104 | +// @PostMapping("/preview") | ||
| 105 | +// @Operation(summary = "文件上传") | ||
| 106 | +// public CommonResult previewExcel(@RequestParam("file") MultipartFile file, | ||
| 107 | +// @RequestParam("templateName") String templateName) { | ||
| 108 | +// // 基础参数校验 | ||
| 109 | +// if (templateName == null || templateName.trim().isEmpty()) { | ||
| 110 | +// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 111 | +// | ||
| 112 | +// } | ||
| 113 | +// // 模板类型校验 | ||
| 114 | +// TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 115 | +// if (template == null) { | ||
| 116 | +// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 117 | +// } | ||
| 118 | +// String suffix = template.getName().split("\\.")[1]; | ||
| 119 | +// // 文件有效性校验 | ||
| 120 | +// if (!isValidExcelFile(file)) { | ||
| 121 | +// return CommonResult.error(BAD_REQUEST, "请上传有效的Excel文件"); | ||
| 122 | +// } | ||
| 123 | +// //部门信息缓存,校验正确性使用 | ||
| 124 | +// | ||
| 125 | +// | ||
| 126 | +// // 解析Excel并获取数据 | ||
| 127 | +// ImportResult<?> result = parseExcelFile(file, template); | ||
| 128 | +// | ||
| 129 | +// // 需预览的数据存入Redis并返回批次ID | ||
| 130 | +// if (result.isNeedPreview()) { | ||
| 131 | +// if (CollectionUtils.isEmpty(result.getDataList())) { | ||
| 132 | +// return CommonResult.error(BAD_REQUEST, "导入的Excel文件不能为空"); | ||
| 133 | +// | ||
| 134 | +// } | ||
| 135 | +// String batchId = costFeeService.saveDataToRedis(result.getDataList(), templateName); | ||
| 136 | +// Map<String, Object> resultMap = new HashMap<>(2); | ||
| 137 | +// resultMap.put("batchId", batchId); | ||
| 138 | +// resultMap.put("totalCount", result.getDataList().size()); | ||
| 139 | +// resultMap.put("templateName",templateName); | ||
| 140 | +// return success(resultMap); | ||
| 141 | +// } | ||
| 142 | +// // 无需预览的直接返回数据 | ||
| 143 | +// return success(result.getDataList()); | ||
| 144 | +// } | ||
| 145 | +// | ||
| 146 | +// | ||
| 147 | +// /** | ||
| 148 | +// * 分页获取预览数据 | ||
| 149 | +// */ | ||
| 150 | +// @GetMapping("/preview-data") | ||
| 151 | +// @Operation(summary = "批量上传分页预览") | ||
| 152 | +// public CommonResult getPreviewData(@RequestParam("batchId") String batchId, | ||
| 153 | +// @RequestParam("templateName") String templateName, | ||
| 154 | +// @RequestParam(value = "page", defaultValue = "1") int page, | ||
| 155 | +// @RequestParam(value = "size", defaultValue = "100000") int size) { | ||
| 156 | +// | ||
| 157 | +// | ||
| 158 | +// // 参数校验 | ||
| 159 | +// if (batchId == null || batchId.trim().isEmpty()) { | ||
| 160 | +// return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); | ||
| 161 | +// } | ||
| 162 | +// if (templateName == null || templateName.trim().isEmpty()) { | ||
| 163 | +// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 164 | +// | ||
| 165 | +// } | ||
| 166 | +// if (TemplateEnum.getByCode(templateName) == null) { | ||
| 167 | +// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 168 | +// | ||
| 169 | +// } | ||
| 170 | +// try { | ||
| 171 | +// //if (page < 1 || size < 1 || size > 100) { // 限制每页最大100条,避免过大 | ||
| 172 | +// // return AjaxResult.error("分页参数无效(page≥1,1≤size≤100)"); | ||
| 173 | +// //} | ||
| 174 | +// | ||
| 175 | +// // 模板类型校验 | ||
| 176 | +// TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 177 | +// Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 178 | +// | ||
| 179 | +// // 从Redis获取分页数据 | ||
| 180 | +// PageResult<?> pageResult = costFeeService.getPagedDataFromRedis(batchId, templateName, page, size, importClass); | ||
| 181 | +// return CommonResult.success(pageResult); | ||
| 182 | +// } catch (Exception e) { | ||
| 183 | +// log.error("获取预览数据失败", e); | ||
| 184 | +// throw new RuntimeException(e); | ||
| 185 | +// } | ||
| 186 | +// } | ||
| 187 | +// | ||
| 188 | +// | ||
| 189 | +// /** | ||
| 190 | +// * 提交导入数据到数据库 | ||
| 191 | +// */ | ||
| 192 | +// @PostMapping("/submit") | ||
| 193 | +// @Operation(summary = "批次保存") | ||
| 194 | +// public CommonResult submitImport(@RequestParam("batchId") String batchId, | ||
| 195 | +// @RequestParam("templateName") String templateName, | ||
| 196 | +// @RequestParam("operator") String operator) { | ||
| 197 | +// // 参数校验 | ||
| 198 | +// if (batchId == null || batchId.trim().isEmpty()) { | ||
| 199 | +// return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); | ||
| 200 | +// } | ||
| 201 | +// if (templateName == null || templateName.trim().isEmpty()) { | ||
| 202 | +// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); | ||
| 203 | +// | ||
| 204 | +// } | ||
| 205 | +// if (TemplateEnum.getByCode(templateName) == null) { | ||
| 206 | +// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); | ||
| 207 | +// | ||
| 208 | +// } | ||
| 209 | +// if (operator == null || operator.trim().isEmpty()) { | ||
| 210 | +// return CommonResult.error(BAD_REQUEST, "操作人不能为空"); | ||
| 211 | +// } | ||
| 212 | +// int count = costFeeService.submitImport(batchId, templateName, operator); | ||
| 213 | +// return CommonResult.success("导入成功,共" + count + "条数据"); | ||
| 214 | +// } | ||
| 215 | +// | ||
| 216 | +// /** | ||
| 217 | +// * 写入模板文件到响应流 | ||
| 218 | +// */ | ||
| 219 | +// private void writeTemplateToResponse(HttpServletResponse response, String filename, String templatePath) throws IOException { | ||
| 220 | +// // 设置响应头 | ||
| 221 | +// response.setContentType(EXCEL_MIME_TYPE); | ||
| 222 | +// response.setCharacterEncoding("UTF-8"); | ||
| 223 | +// String encodedFileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20"); | ||
| 224 | +// response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); | ||
| 225 | +// response.setHeader("Cache-Control", "no-store"); // 禁止缓存 | ||
| 226 | +// | ||
| 227 | +// // 读取模板文件并写入响应 | ||
| 228 | +// try (InputStream in = new ClassPathResource("template/" + templatePath).getInputStream(); | ||
| 229 | +// OutputStream out = response.getOutputStream()) { | ||
| 230 | +// | ||
| 231 | +// byte[] buffer = new byte[4096]; | ||
| 232 | +// int bytesRead; | ||
| 233 | +// while ((bytesRead = in.read(buffer)) != -1) { | ||
| 234 | +// out.write(buffer, 0, bytesRead); | ||
| 235 | +// } | ||
| 236 | +// out.flush(); | ||
| 237 | +// } | ||
| 238 | +// } | ||
| 239 | +// | ||
| 240 | +// | ||
| 241 | +// /** | ||
| 242 | +// * 校验Excel文件有效性(后缀+MIME类型) | ||
| 243 | +// */ | ||
| 244 | +// private boolean isValidExcelFile(MultipartFile file) { | ||
| 245 | +// if (file == null || file.isEmpty()) { | ||
| 246 | +// return false; | ||
| 247 | +// } | ||
| 248 | +// String fileName = file.getOriginalFilename(); | ||
| 249 | +// if (fileName == null) { | ||
| 250 | +// return false; | ||
| 251 | +// } | ||
| 252 | +// | ||
| 253 | +// // 校验文件后缀 | ||
| 254 | +// boolean validSuffix = ALLOWED_EXCEL_SUFFIX.stream() | ||
| 255 | +// .anyMatch(suffix -> fileName.toLowerCase().endsWith(suffix)); | ||
| 256 | +// if (!validSuffix) { | ||
| 257 | +// return false; | ||
| 258 | +// } | ||
| 259 | +// | ||
| 260 | +// // 4. 校验MIME类型(兼容可能带参数的MIME类型,如charset) | ||
| 261 | +// String contentType = file.getContentType(); | ||
| 262 | +// if (contentType == null) { | ||
| 263 | +// return false; | ||
| 264 | +// } | ||
| 265 | +// // 处理MIME类型可能带参数的情况(如"application/xxx;charset=UTF-8") | ||
| 266 | +// String baseContentType = contentType.split(";")[0].trim(); | ||
| 267 | +// // 3. 匹配预设的Excel MIME类型(.xlsx/.xlsm或.xls) | ||
| 268 | +// return EXCEL_MIME_TYPE.equals(baseContentType) | ||
| 269 | +// || baseContentType.startsWith(EXCEL_MIME_TYPE_XLS); | ||
| 270 | +// } | ||
| 271 | +// | ||
| 272 | +// | ||
| 273 | +// /** | ||
| 274 | +// * 解析Excel文件并返回导入结果 | ||
| 275 | +// */ | ||
| 276 | +// @SuppressWarnings("unchecked") | ||
| 277 | +// private <T> ImportResult<T> parseExcelFile(MultipartFile file, TemplateEnum template){ | ||
| 278 | +// // 获取模板对应的导入类 | ||
| 279 | +// Class<T> importClass = (Class<T>) template.getImportClass(); | ||
| 280 | +// List<T> dataList = null; | ||
| 281 | +// try { | ||
| 282 | +// // 解析Excel(从指定行开始读取) | ||
| 283 | +// dataList = ExcelUtils.read(file, importClass, template.getStartIndex()); | ||
| 284 | +// if (dataList == null) { | ||
| 285 | +// dataList = Collections.emptyList(); | ||
| 286 | +// } | ||
| 287 | +// // 关键:移除最后一行(以“说明”开头的行) | ||
| 288 | +// removeLastLineIfStartWithExplain(dataList, importClass); | ||
| 289 | +// // 基础数据校验(非空、格式等) | ||
| 290 | +// costFeeService.validateImportData(dataList, template); | ||
| 291 | +// | ||
| 292 | +// } catch (IOException e) { | ||
| 293 | +// log.error(e.getMessage()); | ||
| 294 | +// } | ||
| 295 | +// return new ImportResult<>(dataList, template.isNeedPreview()); | ||
| 296 | +// } | ||
| 297 | +// /** | ||
| 298 | +// * 移除列表最后一行(若该行第一列内容以“说明”开头) | ||
| 299 | +// * @param dataList Excel解析后的实体类列表 | ||
| 300 | +// * @param clazz 实体类字节码(用于反射获取第一列字段值) | ||
| 301 | +// * @param <T> 泛型:Excel对应的实体类 | ||
| 302 | +// */ | ||
| 303 | +// private <T> void removeLastLineIfStartWithExplain(List<T> dataList, Class<T> clazz) { | ||
| 304 | +// // 1. 跳过空列表/元素数<1的情况,避免索引越界 | ||
| 305 | +// if (CollectionUtils.isEmpty(dataList)) { | ||
| 306 | +// return; | ||
| 307 | +// } | ||
| 308 | +// | ||
| 309 | +// // 2. 获取最后一个元素(正确索引:size()-1) | ||
| 310 | +// T lastItem = dataList.get(dataList.size() - 1); | ||
| 311 | +// if (Objects.isNull(lastItem)) { | ||
| 312 | +// return; | ||
| 313 | +// } | ||
| 314 | +// | ||
| 315 | +// // 3. 反射获取「第一列字段的值」(核心:Excel列对应实体类的字段,假设第一列字段名是 firstColumn,需根据实际调整) | ||
| 316 | +// String firstColumnValue = getFirstColumnValue(lastItem, clazz); | ||
| 317 | +// if (Objects.isNull(firstColumnValue)) { | ||
| 318 | +// return; | ||
| 319 | +// } | ||
| 320 | +// | ||
| 321 | +// // 4. 匹配“说明”(trim() 避免空格干扰,如“ 说明:xxx”) | ||
| 322 | +// if (firstColumnValue.trim().startsWith("说明")) { | ||
| 323 | +// // 移除最后一个元素(正确索引:size()-1) | ||
| 324 | +// dataList.remove(dataList.size() - 1); | ||
| 325 | +// } | ||
| 326 | +// } | ||
| 327 | +// | ||
| 328 | +// /** | ||
| 329 | +// * 反射获取实体类的「第一列字段值」(需根据你的Excel列配置调整字段名) | ||
| 330 | +// * @param item 实体类对象(Excel一行数据) | ||
| 331 | +// * @param clazz 实体类字节码 | ||
| 332 | +// * @return 第一列的字符串值(null 表示无值) | ||
| 333 | +// */ | ||
| 334 | +// private <T> String getFirstColumnValue(T item, Class<T> clazz) { | ||
| 335 | +// try { | ||
| 336 | +// String firstColumnFieldName = ""; | ||
| 337 | +// if (WaterFeeImport.class.isAssignableFrom(clazz)) { | ||
| 338 | +// firstColumnFieldName = "feeMonth"; | ||
| 339 | +// } else { | ||
| 340 | +// return null; | ||
| 341 | +// } | ||
| 342 | +// | ||
| 343 | +// // 反射获取 getter 方法() | ||
| 344 | +// String getterMethodName = "get" + firstColumnFieldName.substring(0, 1).toUpperCase() + firstColumnFieldName.substring(1); | ||
| 345 | +// Method getterMethod = clazz.getDeclaredMethod(getterMethodName); | ||
| 346 | +// | ||
| 347 | +// // 执行 getter 方法获取字段值 | ||
| 348 | +// Object value = getterMethod.invoke(item); | ||
| 349 | +// return value == null ? null : value.toString(); | ||
| 350 | +// } catch (Exception e) { | ||
| 351 | +// // 反射失败(字段名错误、无getter方法),打印日志不抛异常,避免影响整体流程 | ||
| 352 | +// log.warn("获取Excel第一列字段值失败,实体类:{}", clazz.getName(), e); | ||
| 353 | +// return null; | ||
| 354 | +// } | ||
| 355 | +// } | ||
| 356 | +// | ||
| 357 | +// /** | ||
| 358 | +// * 导入结果封装 | ||
| 359 | +// */ | ||
| 360 | +// @Data | ||
| 361 | +// @AllArgsConstructor | ||
| 362 | +// private static class ImportResult<T> { | ||
| 363 | +// private List<T> dataList; | ||
| 364 | +// private boolean needPreview; | ||
| 365 | +// } | ||
| 366 | +// | ||
| 367 | +// /** | ||
| 368 | +// * 分页结果封装 | ||
| 369 | +// */ | ||
| 370 | +// @Data | ||
| 371 | +// @AllArgsConstructor | ||
| 372 | +// public static class PageResult<T> { | ||
| 373 | +// private int page; | ||
| 374 | +// private int size; | ||
| 375 | +// private int total; | ||
| 376 | +// private List<T> data; | ||
| 377 | +// } | ||
| 378 | +//} |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeService.java
| 1 | -package com.zteits.urbanops.module.garden.service.costfee; | ||
| 2 | - | ||
| 3 | -import com.fasterxml.jackson.core.JsonProcessingException; | ||
| 4 | -import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; | ||
| 5 | -import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 6 | - | ||
| 7 | -import java.io.IOException; | ||
| 8 | -import java.util.List; | ||
| 9 | -import java.util.Map; | ||
| 10 | - | ||
| 11 | -/** | ||
| 12 | - * @Classname ICostFeeService | ||
| 13 | - * @Description 模板导入公共接口 | ||
| 14 | - * @Date 2025/7/20 15:10 | ||
| 15 | - * @Created by wangqian | ||
| 16 | - */ | ||
| 17 | -public interface CostFeeService { | ||
| 18 | - | ||
| 19 | - /** | ||
| 20 | - * @Author wangqian | ||
| 21 | - * @Description 导入数据保存redis | ||
| 22 | - * @Date 2025/7/20 15:36 | ||
| 23 | - * @Param dataList | ||
| 24 | - * @param templateName | ||
| 25 | - * @Return java.lang.String | ||
| 26 | - */ | ||
| 27 | - public String saveDataToRedis(List<?> dataList, String templateName); | ||
| 28 | - | ||
| 29 | - /** | ||
| 30 | - * @Author wangqian | ||
| 31 | - * @Description 导入数据保存到数据库 | ||
| 32 | - * @Date 2025/7/20 15:36 | ||
| 33 | - * @Param batchId | ||
| 34 | - * @param templateName | ||
| 35 | - * @param operator | ||
| 36 | - * @Return int | ||
| 37 | - */ | ||
| 38 | - public int submitImport(String batchId, String templateName, String operator); | ||
| 39 | - | ||
| 40 | - /** | ||
| 41 | - * @Author wangqian | ||
| 42 | - * @Description 预览分页查询 | ||
| 43 | - * @Date 2025/7/20 15:36 | ||
| 44 | - * @Param batchId | ||
| 45 | - * @param templateName | ||
| 46 | - * @param page | ||
| 47 | - * @param size | ||
| 48 | - * @param clazz | ||
| 49 | - * @Return com.servicemanager.project.costfee.controller.CostFeeController.PageResult<T> | ||
| 50 | - */ | ||
| 51 | - public <T> CostFeeController.PageResult<T> getPagedDataFromRedis(String batchId, String templateName, | ||
| 52 | - int page, int size, Class<T> clazz) throws IOException; | ||
| 53 | - | ||
| 54 | - /** | ||
| 55 | - * @Author wangqian | ||
| 56 | - * @Description 导入内容校验 | ||
| 57 | - * @Date 2025/11/24 8:40 | ||
| 58 | - * @Param dataList | ||
| 59 | - * @param template | ||
| 60 | - * @Return void | ||
| 61 | - */ | ||
| 62 | - public void validateImportData(List dataList, TemplateEnum template); | ||
| 63 | - | ||
| 64 | -} | 1 | +//package com.zteits.urbanops.module.garden.service.costfee; |
| 2 | +// | ||
| 3 | +//import com.fasterxml.jackson.core.JsonProcessingException; | ||
| 4 | +//import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; | ||
| 5 | +//import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 6 | +// | ||
| 7 | +//import java.io.IOException; | ||
| 8 | +//import java.util.List; | ||
| 9 | +//import java.util.Map; | ||
| 10 | +// | ||
| 11 | +///** | ||
| 12 | +// * @Classname ICostFeeService | ||
| 13 | +// * @Description 模板导入公共接口 | ||
| 14 | +// * @Date 2025/7/20 15:10 | ||
| 15 | +// * @Created by wangqian | ||
| 16 | +// */ | ||
| 17 | +//public interface CostFeeService { | ||
| 18 | +// | ||
| 19 | +// /** | ||
| 20 | +// * @Author wangqian | ||
| 21 | +// * @Description 导入数据保存redis | ||
| 22 | +// * @Date 2025/7/20 15:36 | ||
| 23 | +// * @Param dataList | ||
| 24 | +// * @param templateName | ||
| 25 | +// * @Return java.lang.String | ||
| 26 | +// */ | ||
| 27 | +// public String saveDataToRedis(List<?> dataList, String templateName); | ||
| 28 | +// | ||
| 29 | +// /** | ||
| 30 | +// * @Author wangqian | ||
| 31 | +// * @Description 导入数据保存到数据库 | ||
| 32 | +// * @Date 2025/7/20 15:36 | ||
| 33 | +// * @Param batchId | ||
| 34 | +// * @param templateName | ||
| 35 | +// * @param operator | ||
| 36 | +// * @Return int | ||
| 37 | +// */ | ||
| 38 | +// public int submitImport(String batchId, String templateName, String operator); | ||
| 39 | +// | ||
| 40 | +// /** | ||
| 41 | +// * @Author wangqian | ||
| 42 | +// * @Description 预览分页查询 | ||
| 43 | +// * @Date 2025/7/20 15:36 | ||
| 44 | +// * @Param batchId | ||
| 45 | +// * @param templateName | ||
| 46 | +// * @param page | ||
| 47 | +// * @param size | ||
| 48 | +// * @param clazz | ||
| 49 | +// * @Return com.servicemanager.project.costfee.controller.CostFeeController.PageResult<T> | ||
| 50 | +// */ | ||
| 51 | +// public <T> CostFeeController.PageResult<T> getPagedDataFromRedis(String batchId, String templateName, | ||
| 52 | +// int page, int size, Class<T> clazz) throws IOException; | ||
| 53 | +// | ||
| 54 | +// /** | ||
| 55 | +// * @Author wangqian | ||
| 56 | +// * @Description 导入内容校验 | ||
| 57 | +// * @Date 2025/11/24 8:40 | ||
| 58 | +// * @Param dataList | ||
| 59 | +// * @param template | ||
| 60 | +// * @Return void | ||
| 61 | +// */ | ||
| 62 | +// public void validateImportData(List dataList, TemplateEnum template); | ||
| 63 | +// | ||
| 64 | +//} |