Commit 5dfcf12b219c29d59c2f1e9c8366148c35c7ccde
1 parent
5f5a817e
耗材修改
Showing
8 changed files
with
1477 additions
and
1432 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/controller/admin/wasterecord/vo/WasteRecordSaveReqVO.java
| @@ -43,11 +43,11 @@ public class WasteRecordSaveReqVO { | @@ -43,11 +43,11 @@ public class WasteRecordSaveReqVO { | ||
| 43 | private BigDecimal totalCost; | 43 | private BigDecimal totalCost; |
| 44 | 44 | ||
| 45 | @Schema(description = "归属公司", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") | 45 | @Schema(description = "归属公司", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") |
| 46 | - @NotEmpty(message = "归属公司不能为空") | 46 | + //@NotEmpty(message = "归属公司不能为空") |
| 47 | private String companyName; | 47 | private String companyName; |
| 48 | 48 | ||
| 49 | @Schema(description = "归属公司ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3314") | 49 | @Schema(description = "归属公司ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3314") |
| 50 | - @NotEmpty(message = "归属公司ID不能为空") | 50 | + //@NotEmpty(message = "归属公司ID不能为空") |
| 51 | private String companyId; | 51 | private String companyId; |
| 52 | 52 | ||
| 53 | @Schema(description = "部门id", example = "26656") | 53 | @Schema(description = "部门id", example = "26656") |
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 | +} |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java
| 1 | -//package com.zteits.urbanops.module.garden.service.costfee; | ||
| 2 | -// | ||
| 3 | -//import com.fasterxml.jackson.core.JsonProcessingException; | ||
| 4 | -//import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 5 | -//import com.zteits.urbanops.framework.security.core.LoginUser; | ||
| 6 | -//import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; | ||
| 7 | -//import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.*; | ||
| 8 | -//import com.zteits.urbanops.module.garden.dal.dataobject.departmentcost.DepartmentCostDO; | ||
| 9 | -//import com.zteits.urbanops.module.garden.dal.dataobject.mechanicalcost.MechanicalCostDO; | ||
| 10 | -//import com.zteits.urbanops.module.garden.dal.dataobject.personalcost.PersonalCostDO; | ||
| 11 | -//import com.zteits.urbanops.module.garden.dal.dataobject.rentalmechanicalcost.RentalMechanicalCostDO; | ||
| 12 | -//import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; | ||
| 13 | -//import com.zteits.urbanops.module.garden.dal.dataobject.waterfee.WaterFeeDO; | ||
| 14 | -//import com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper; | ||
| 15 | -//import com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper; | ||
| 16 | -//import com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper; | ||
| 17 | -//import com.zteits.urbanops.module.garden.dal.mysql.rentalmechanicalcost.RentalMechanicalCostMapper; | ||
| 18 | -//import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; | ||
| 19 | -//import com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper; | ||
| 20 | -//import com.zteits.urbanops.module.garden.enums.AssignTasksConstants; | ||
| 21 | -//import com.zteits.urbanops.module.garden.enums.CostFeeConstants; | ||
| 22 | -//import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 23 | -//import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 24 | -//import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 25 | -//import jakarta.annotation.Resource; | ||
| 26 | -//import jakarta.validation.ConstraintViolation; | ||
| 27 | -//import jakarta.validation.ConstraintViolationException; | ||
| 28 | -//import jakarta.validation.Validator; | ||
| 29 | -//import lombok.extern.slf4j.Slf4j; | ||
| 30 | -//import org.apache.commons.lang3.StringUtils; | ||
| 31 | -//import org.springframework.beans.BeanUtils; | ||
| 32 | -//import org.springframework.beans.factory.annotation.Autowired; | ||
| 33 | -//import org.springframework.data.redis.core.BoundHashOperations; | ||
| 34 | -//import org.springframework.data.redis.core.RedisTemplate; | ||
| 35 | -//import org.springframework.stereotype.Service; | ||
| 36 | -//import org.springframework.transaction.annotation.Transactional; | ||
| 37 | -//import org.springframework.util.CollectionUtils; | ||
| 38 | -// | ||
| 39 | -//import java.io.IOException; | ||
| 40 | -//import java.time.LocalDate; | ||
| 41 | -//import java.util.*; | ||
| 42 | -//import java.util.concurrent.TimeUnit; | ||
| 43 | -//import java.util.function.Function; | ||
| 44 | -//import java.util.stream.Collectors; | ||
| 45 | -//import java.util.stream.IntStream; | ||
| 46 | -// | ||
| 47 | -//import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||
| 48 | -//import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 49 | -//import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | ||
| 50 | -//import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; | ||
| 51 | -//import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.COST_FEE_NOT_EXISTS_001; | ||
| 52 | -// | ||
| 53 | -///** | ||
| 54 | -// * @Classname CostFeeService | ||
| 55 | -// * @Description 模板导入公共实现 | ||
| 56 | -// * @Date 2025/7/20 15:12 | ||
| 57 | -// * @Created by wangqian | ||
| 58 | -// */ | ||
| 59 | -//@Service | ||
| 60 | -//@Slf4j | ||
| 61 | -//public class CostFeeServiceImpl implements CostFeeService { | ||
| 62 | -// | ||
| 63 | -// // Redis相关配置 | ||
| 64 | -// private static final String REDIS_KEY_PREFIX = "excel:import:"; | ||
| 65 | -// private static final long EXPIRE_TIME = 30; // 预览数据过期时间:30分钟 | ||
| 66 | -// private static final TimeUnit EXPIRE_UNIT = TimeUnit.MINUTES; | ||
| 67 | -// @Autowired | ||
| 68 | -// private RedisTemplate<String, String> redisTemplate; | ||
| 69 | -// @Autowired | ||
| 70 | -// private ObjectMapper objectMapper; | ||
| 71 | -// @Resource | ||
| 72 | -// private Validator validator; | ||
| 73 | -// @Resource | ||
| 74 | -// private DeptApi deptApi; | ||
| 75 | -// | ||
| 76 | -// @Autowired | ||
| 77 | -// private WaterFeeMapper waterFeeMapper; | ||
| 78 | -// @Autowired | ||
| 79 | -// private PersonalCostMapper personalCostMapper; | ||
| 80 | -// @Autowired | ||
| 81 | -// private DepartmentCostMapper departmentCostMapper; | ||
| 82 | -// @Autowired | ||
| 83 | -// private MechanicalCostMapper mechanicalCostMapper; | ||
| 84 | -// @Autowired | ||
| 85 | -// private RentalMechanicalCostMapper rentalMechanicalCostMapper; | ||
| 86 | -// @Autowired | ||
| 87 | -// private WasteRecordMapper wasteRecordMapper; | ||
| 88 | -// | ||
| 89 | -// | ||
| 90 | -// @Override | ||
| 91 | -// /** | ||
| 92 | -// * 保存数据到Redis | ||
| 93 | -// */ | ||
| 94 | -// public String saveDataToRedis(List<?> dataList, String templateName){ | ||
| 95 | -// String batchId = UUID.randomUUID().toString().replace("-", ""); | ||
| 96 | -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 97 | -// | ||
| 98 | -// // 存储总记录数 | ||
| 99 | -// redisTemplate.opsForValue().set( | ||
| 100 | -// redisKey + ":total", | ||
| 101 | -// String.valueOf(dataList.size()), | ||
| 102 | -// EXPIRE_TIME, | ||
| 103 | -// EXPIRE_UNIT | ||
| 104 | -// ); | ||
| 105 | -// | ||
| 106 | -// // 使用Hash存储每条数据(key:索引,value:JSON字符串) | ||
| 107 | -// BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 108 | -// try { | ||
| 109 | -// for (int i = 0; i < dataList.size(); i++) { | ||
| 110 | -// String jsonData = objectMapper.writeValueAsString(dataList.get(i)); | ||
| 111 | -// hashOps.put(String.valueOf(i), jsonData); | ||
| 112 | -// } | ||
| 113 | -// } catch (JsonProcessingException e) { | ||
| 114 | -// log.error(e.getMessage()); | ||
| 115 | -// } | ||
| 116 | -// // 设置Hash过期时间 | ||
| 117 | -// hashOps.expire(EXPIRE_TIME, EXPIRE_UNIT); | ||
| 118 | -// | ||
| 119 | -// log.info("数据保存到Redis成功,批次ID:{},记录数:{}", batchId, dataList.size()); | ||
| 120 | -// return batchId; | ||
| 121 | -// } | ||
| 122 | -// | ||
| 123 | -// @Override | ||
| 124 | -// @Transactional | ||
| 125 | -// public int submitImport(String batchId, String templateName, String operator){ | ||
| 126 | -// // 模板类型校验 | ||
| 127 | -// TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 128 | -// Class<?> importClass = template.getImportClass(); | ||
| 129 | -// if (importClass == null) { | ||
| 130 | -// throw exception0(BAD_REQUEST.getCode(),"导入模板配置异常"); | ||
| 131 | -// } | ||
| 132 | -// | ||
| 133 | -// // 分页参数设置 | ||
| 134 | -// int pageNum = 1; | ||
| 135 | -// final int pageSize = 100000; // 每页处理条数,可根据实际情况调整 | ||
| 136 | -// int totalImported = 0; // 总导入数量累计 | ||
| 137 | -// | ||
| 138 | -// try { | ||
| 139 | -// while (true) { | ||
| 140 | -// // 分页从Redis获取数据 | ||
| 141 | -// CostFeeController.PageResult<?> pageResult = getPagedDataFromRedis( | ||
| 142 | -// batchId, templateName, pageNum, pageSize, importClass); | ||
| 143 | -// List<?> dataList = pageResult.getData(); | ||
| 144 | -// | ||
| 145 | -// // 如果当前页无数据,说明已处理完毕 | ||
| 146 | -// if (dataList == null || dataList.isEmpty()) { | ||
| 147 | -// // 首次分页就无数据,抛出异常 | ||
| 148 | -// if (pageNum == 1) { | ||
| 149 | -// throw exception0(BAD_REQUEST.getCode(),"没有可导入的数据或数据已过期"); | ||
| 150 | -// } | ||
| 151 | -// break; | 1 | +package com.zteits.urbanops.module.garden.service.costfee; |
| 2 | + | ||
| 3 | +import com.fasterxml.jackson.core.JsonProcessingException; | ||
| 4 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
| 5 | +import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; | ||
| 6 | +import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.*; | ||
| 7 | +import com.zteits.urbanops.module.garden.dal.dataobject.departmentcost.DepartmentCostDO; | ||
| 8 | +import com.zteits.urbanops.module.garden.dal.dataobject.mechanicalcost.MechanicalCostDO; | ||
| 9 | +import com.zteits.urbanops.module.garden.dal.dataobject.personalcost.PersonalCostDO; | ||
| 10 | +import com.zteits.urbanops.module.garden.dal.dataobject.rentalmechanicalcost.RentalMechanicalCostDO; | ||
| 11 | +import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; | ||
| 12 | +import com.zteits.urbanops.module.garden.dal.dataobject.waterfee.WaterFeeDO; | ||
| 13 | +import com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper; | ||
| 14 | +import com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper; | ||
| 15 | +import com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper; | ||
| 16 | +import com.zteits.urbanops.module.garden.dal.mysql.rentalmechanicalcost.RentalMechanicalCostMapper; | ||
| 17 | +import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; | ||
| 18 | +import com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper; | ||
| 19 | +import com.zteits.urbanops.module.garden.enums.AssignTasksConstants; | ||
| 20 | +import com.zteits.urbanops.module.garden.enums.CostFeeConstants; | ||
| 21 | +import com.zteits.urbanops.module.garden.enums.TemplateEnum; | ||
| 22 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 23 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 24 | +import jakarta.annotation.Resource; | ||
| 25 | +import jakarta.validation.ConstraintViolation; | ||
| 26 | +import jakarta.validation.ConstraintViolationException; | ||
| 27 | +import jakarta.validation.Validator; | ||
| 28 | +import lombok.extern.slf4j.Slf4j; | ||
| 29 | +import org.apache.commons.lang3.StringUtils; | ||
| 30 | +import org.springframework.beans.BeanUtils; | ||
| 31 | +import org.springframework.beans.factory.annotation.Autowired; | ||
| 32 | +import org.springframework.data.redis.core.BoundHashOperations; | ||
| 33 | +import org.springframework.data.redis.core.RedisTemplate; | ||
| 34 | +import org.springframework.stereotype.Service; | ||
| 35 | +import org.springframework.transaction.annotation.Transactional; | ||
| 36 | +import org.springframework.util.CollectionUtils; | ||
| 37 | + | ||
| 38 | +import java.io.IOException; | ||
| 39 | +import java.time.LocalDate; | ||
| 40 | +import java.util.*; | ||
| 41 | +import java.util.concurrent.TimeUnit; | ||
| 42 | +import java.util.function.Function; | ||
| 43 | +import java.util.stream.Collectors; | ||
| 44 | +import java.util.stream.IntStream; | ||
| 45 | + | ||
| 46 | +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||
| 47 | +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 48 | +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | ||
| 49 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; | ||
| 50 | +import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.COST_FEE_NOT_EXISTS_001; | ||
| 51 | + | ||
| 52 | +/** | ||
| 53 | + * @Classname CostFeeService | ||
| 54 | + * @Description 模板导入公共实现 | ||
| 55 | + * @Date 2025/7/20 15:12 | ||
| 56 | + * @Created by wangqian | ||
| 57 | + */ | ||
| 58 | +@Service | ||
| 59 | +@Slf4j | ||
| 60 | +public class CostFeeServiceImpl implements CostFeeService { | ||
| 61 | + | ||
| 62 | + // Redis相关配置 | ||
| 63 | + private static final String REDIS_KEY_PREFIX = "excel:import:"; | ||
| 64 | + private static final long EXPIRE_TIME = 30; // 预览数据过期时间:30分钟 | ||
| 65 | + private static final TimeUnit EXPIRE_UNIT = TimeUnit.MINUTES; | ||
| 66 | + @Autowired | ||
| 67 | + private RedisTemplate<String, String> redisTemplate; | ||
| 68 | + @Autowired | ||
| 69 | + private ObjectMapper objectMapper; | ||
| 70 | + @Resource | ||
| 71 | + private Validator validator; | ||
| 72 | + @Resource | ||
| 73 | + private DeptApi deptApi; | ||
| 74 | + | ||
| 75 | + @Autowired | ||
| 76 | + private WaterFeeMapper waterFeeMapper; | ||
| 77 | + @Autowired | ||
| 78 | + private PersonalCostMapper personalCostMapper; | ||
| 79 | + @Autowired | ||
| 80 | + private DepartmentCostMapper departmentCostMapper; | ||
| 81 | + @Autowired | ||
| 82 | + private MechanicalCostMapper mechanicalCostMapper; | ||
| 83 | + @Autowired | ||
| 84 | + private RentalMechanicalCostMapper rentalMechanicalCostMapper; | ||
| 85 | + @Autowired | ||
| 86 | + private WasteRecordMapper wasteRecordMapper; | ||
| 87 | + | ||
| 88 | + | ||
| 89 | + @Override | ||
| 90 | + /** | ||
| 91 | + * 保存数据到Redis | ||
| 92 | + */ | ||
| 93 | + public String saveDataToRedis(List<?> dataList, String templateName){ | ||
| 94 | + String batchId = UUID.randomUUID().toString().replace("-", ""); | ||
| 95 | + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 96 | + | ||
| 97 | + // 存储总记录数 | ||
| 98 | + redisTemplate.opsForValue().set( | ||
| 99 | + redisKey + ":total", | ||
| 100 | + String.valueOf(dataList.size()), | ||
| 101 | + EXPIRE_TIME, | ||
| 102 | + EXPIRE_UNIT | ||
| 103 | + ); | ||
| 104 | + | ||
| 105 | + // 使用Hash存储每条数据(key:索引,value:JSON字符串) | ||
| 106 | + BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 107 | + try { | ||
| 108 | + for (int i = 0; i < dataList.size(); i++) { | ||
| 109 | + String jsonData = objectMapper.writeValueAsString(dataList.get(i)); | ||
| 110 | + hashOps.put(String.valueOf(i), jsonData); | ||
| 111 | + } | ||
| 112 | + } catch (JsonProcessingException e) { | ||
| 113 | + log.error(e.getMessage()); | ||
| 114 | + } | ||
| 115 | + // 设置Hash过期时间 | ||
| 116 | + hashOps.expire(EXPIRE_TIME, EXPIRE_UNIT); | ||
| 117 | + | ||
| 118 | + log.info("数据保存到Redis成功,批次ID:{},记录数:{}", batchId, dataList.size()); | ||
| 119 | + return batchId; | ||
| 120 | + } | ||
| 121 | + | ||
| 122 | + @Override | ||
| 123 | + @Transactional | ||
| 124 | + public int submitImport(String batchId, String templateName, String operator){ | ||
| 125 | + // 模板类型校验 | ||
| 126 | + TemplateEnum template = TemplateEnum.getByCode(templateName); | ||
| 127 | + Class<?> importClass = template.getImportClass(); | ||
| 128 | + if (importClass == null) { | ||
| 129 | + throw exception0(BAD_REQUEST.getCode(),"导入模板配置异常"); | ||
| 130 | + } | ||
| 131 | + | ||
| 132 | + // 分页参数设置 | ||
| 133 | + int pageNum = 1; | ||
| 134 | + final int pageSize = 100000; // 每页处理条数,可根据实际情况调整 | ||
| 135 | + int totalImported = 0; // 总导入数量累计 | ||
| 136 | + | ||
| 137 | + try { | ||
| 138 | + while (true) { | ||
| 139 | + // 分页从Redis获取数据 | ||
| 140 | + CostFeeController.PageResult<?> pageResult = getPagedDataFromRedis( | ||
| 141 | + batchId, templateName, pageNum, pageSize, importClass); | ||
| 142 | + List<?> dataList = pageResult.getData(); | ||
| 143 | + | ||
| 144 | + // 如果当前页无数据,说明已处理完毕 | ||
| 145 | + if (dataList == null || dataList.isEmpty()) { | ||
| 146 | + // 首次分页就无数据,抛出异常 | ||
| 147 | + if (pageNum == 1) { | ||
| 148 | + throw exception0(BAD_REQUEST.getCode(),"没有可导入的数据或数据已过期"); | ||
| 149 | + } | ||
| 150 | + break; | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + // 业务校验(每页单独校验) | ||
| 154 | + //validateBusinessData(dataList, template); | ||
| 155 | + | ||
| 156 | + // 调用业务服务执行本页数据导入 | ||
| 157 | + int currentPageCount = dataList.size(); | ||
| 158 | + insertData(template, dataList, operator); | ||
| 159 | + totalImported += currentPageCount; | ||
| 160 | + | ||
| 161 | + // 打印分页处理日志(可选) | ||
| 162 | + log.info("批次[{}]第{}页导入完成,本页{}条,累计{}条", | ||
| 163 | + batchId, pageNum, currentPageCount, totalImported); | ||
| 164 | + | ||
| 165 | + // 准备下一页 | ||
| 166 | + pageNum++; | ||
| 167 | + | ||
| 168 | + // 如果当前页数据小于页大小,说明已到最后一页 | ||
| 169 | + if (dataList.size() < pageSize) { | ||
| 170 | + break; | ||
| 171 | + } | ||
| 172 | + } | ||
| 173 | + } catch (IOException e) { | ||
| 174 | + log.error(e.getMessage()); | ||
| 175 | + } finally { | ||
| 176 | + // 无论导入成功与否,都清理Redis临时数据 | ||
| 177 | + deleteRedisTempData(batchId, templateName); | ||
| 178 | + } | ||
| 179 | + | ||
| 180 | + return totalImported; | ||
| 181 | + } | ||
| 182 | + /** | ||
| 183 | + * @Author wangqian | ||
| 184 | + * @Description 保存数据 | ||
| 185 | + * @Date 2025/7/20 11:30 | ||
| 186 | + * @Param template | ||
| 187 | + * @param dataList | ||
| 188 | + * @param operator | ||
| 189 | + * @Return void | ||
| 190 | + */ | ||
| 191 | + private void insertData(TemplateEnum template, List<?> dataList, String operator) { | ||
| 192 | + if (CollectionUtils.isEmpty(dataList)) { | ||
| 193 | + log.info("数据列表为空,跳过插入操作"); | ||
| 194 | + return; | ||
| 195 | + } | ||
| 196 | + //单位集合 | ||
| 197 | + List<DeptRespDTO> companyList = deptApi.getSubDeptList(); | ||
| 198 | + // 1. 预加载部门映射(循环外加载,避免重复查询) | ||
| 199 | + List<Long> deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); | ||
| 200 | + Map<String, Long> nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) ? Map.of() | ||
| 201 | + : companyList.stream() | ||
| 202 | + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 203 | + .collect(Collectors.toMap( | ||
| 204 | + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 205 | + DeptRespDTO::getId, // key 映射:取 companyId | ||
| 206 | + (oldValue, newValue) -> newValue | ||
| 207 | + )); | ||
| 208 | + | ||
| 209 | + | ||
| 210 | + // 模板类型校验 | ||
| 211 | + Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 212 | + // 根据不同模板类型执行不同的处理逻辑 | ||
| 213 | + if (WaterFeeImport.class.isAssignableFrom(importClass)) { | ||
| 214 | + Map<Long, String> companyIdToNamedMap = CollectionUtils.isEmpty(companyList) ? Map.of() | ||
| 215 | + : companyList.stream() | ||
| 216 | + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 217 | + .collect(Collectors.toMap( | ||
| 218 | + DeptRespDTO::getId, // key 映射:取 companyId | ||
| 219 | + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 220 | + (oldValue, newValue) -> newValue | ||
| 221 | + )); | ||
| 222 | + | ||
| 223 | + // 处理水费数据 | ||
| 224 | + List<WaterFeeDO> feeList = ((List<WaterFeeImport>) dataList).stream() | ||
| 225 | + .map(importObj -> { | ||
| 226 | + WaterFeeDO fee = new WaterFeeDO(); | ||
| 227 | + BeanUtils.copyProperties(importObj, fee); | ||
| 228 | + fee.setCompanyId(String.valueOf(getCompanyId())); | ||
| 229 | + if (getCompanyId() == 100) { | ||
| 230 | + fee.setCompanyName("蓟城山水"); | ||
| 231 | + } else { | ||
| 232 | + // 设置单位id | ||
| 233 | + fee.setCompanyName(String.valueOf(companyIdToNamedMap.get(fee.getCompanyId()))); | ||
| 234 | + } | ||
| 235 | + return fee; | ||
| 236 | + }) | ||
| 237 | + .collect(Collectors.toMap( | ||
| 238 | + // 生成唯一键:feeMonth + 分隔符 + meterCode(避免不同组合拼接后重复) | ||
| 239 | + fee -> fee.getFeeMonth() + "_" + fee.getMeterCode(), | ||
| 240 | + // 值为对象本身 | ||
| 241 | + Function.identity(), | ||
| 242 | + // 当键重复时,保留第一个出现的对象(可根据需求改为保留最后一个:(existing, replacement) -> replacement) | ||
| 243 | + (existing, replacement) -> replacement | ||
| 244 | + )) | ||
| 245 | + .values() | ||
| 246 | + .stream() | ||
| 247 | + .collect(Collectors.toList()); | ||
| 248 | + | ||
| 249 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 250 | + //查询重复项,进行删除 | ||
| 251 | + List<Long> ids = waterFeeMapper.selectDuplicateIds(feeList); | ||
| 252 | + if (!CollectionUtils.isEmpty(ids)) { | ||
| 253 | + waterFeeMapper.deleteByIds(ids); | ||
| 254 | + } | ||
| 255 | + // 调用批量插入方法 | ||
| 256 | + waterFeeMapper.insertBatch(feeList); | ||
| 257 | + } | ||
| 258 | + log.info("成功插入 {} 条水费数据", feeList.size()); | ||
| 259 | + } else if (PersonalCostImport.class.isAssignableFrom(importClass)) { | ||
| 260 | + //去重 | ||
| 261 | + dataList = personalFeeReplacement((List<PersonalCostImport>) dataList); | ||
| 262 | + //获取班组对象 | ||
| 263 | + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 264 | + | ||
| 265 | + // 3. 转换导入数据为PersonalCost列表(Stream优化+空指针防护) | ||
| 266 | + List<PersonalCostImport> feeList = (List<PersonalCostImport>)dataList; | ||
| 267 | + List<PersonalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 268 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 269 | + for (PersonalCostImport importObj : feeList) { | ||
| 270 | + PersonalCostDO fee = new PersonalCostDO(); | ||
| 271 | + BeanUtils.copyProperties(importObj, fee); | ||
| 272 | + // 设置单位id | ||
| 273 | + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 274 | + //设置班组id | ||
| 275 | + //fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 276 | + saveFeeList.add(fee); | ||
| 277 | + } | ||
| 278 | + } | ||
| 279 | + // 4. 处理重复数据(删除+插入原子性) | ||
| 280 | + if (!feeList.isEmpty()) { | ||
| 281 | + // 4.1 查询并删除重复项 | ||
| 282 | + | ||
| 283 | +// List<Long> duplicateIds = personalCostMapper.selectDuplicateIds(saveFeeList); | ||
| 284 | +// if (!duplicateIds.isEmpty()) { | ||
| 285 | +// personalCostMapper.deleteByIds(duplicateIds); | ||
| 152 | // } | 286 | // } |
| 153 | -// | ||
| 154 | -// // 业务校验(每页单独校验) | ||
| 155 | -// //validateBusinessData(dataList, template); | ||
| 156 | -// | ||
| 157 | -// // 调用业务服务执行本页数据导入 | ||
| 158 | -// int currentPageCount = dataList.size(); | ||
| 159 | -// insertData(template, dataList, operator); | ||
| 160 | -// totalImported += currentPageCount; | ||
| 161 | -// | ||
| 162 | -// // 打印分页处理日志(可选) | ||
| 163 | -// log.info("批次[{}]第{}页导入完成,本页{}条,累计{}条", | ||
| 164 | -// batchId, pageNum, currentPageCount, totalImported); | ||
| 165 | -// | ||
| 166 | -// // 准备下一页 | ||
| 167 | -// pageNum++; | ||
| 168 | -// | ||
| 169 | -// // 如果当前页数据小于页大小,说明已到最后一页 | ||
| 170 | -// if (dataList.size() < pageSize) { | ||
| 171 | -// break; | ||
| 172 | -// } | ||
| 173 | -// } | ||
| 174 | -// } catch (IOException e) { | ||
| 175 | -// log.error(e.getMessage()); | ||
| 176 | -// } finally { | ||
| 177 | -// // 无论导入成功与否,都清理Redis临时数据 | ||
| 178 | -// deleteRedisTempData(batchId, templateName); | ||
| 179 | -// } | ||
| 180 | -// | ||
| 181 | -// return totalImported; | ||
| 182 | -// } | ||
| 183 | -// /** | ||
| 184 | -// * @Author wangqian | ||
| 185 | -// * @Description 保存数据 | ||
| 186 | -// * @Date 2025/7/20 11:30 | ||
| 187 | -// * @Param template | ||
| 188 | -// * @param dataList | ||
| 189 | -// * @param operator | ||
| 190 | -// * @Return void | ||
| 191 | -// */ | ||
| 192 | -// private void insertData(TemplateEnum template, List<?> dataList, String operator) { | ||
| 193 | -// if (CollectionUtils.isEmpty(dataList)) { | ||
| 194 | -// log.info("数据列表为空,跳过插入操作"); | ||
| 195 | -// return; | ||
| 196 | -// } | ||
| 197 | -// //单位集合 | ||
| 198 | -// List<DeptRespDTO> companyList = deptApi.getSubDeptList(); | ||
| 199 | -// // 1. 预加载部门映射(循环外加载,避免重复查询) | ||
| 200 | -// List<Long> deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); | ||
| 201 | -// Map<String, Long> nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) ? Map.of() | ||
| 202 | -// : companyList.stream() | ||
| 203 | -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 204 | -// .collect(Collectors.toMap( | ||
| 205 | -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 206 | -// DeptRespDTO::getId, // key 映射:取 companyId | ||
| 207 | -// (oldValue, newValue) -> newValue | ||
| 208 | -// )); | ||
| 209 | -// | ||
| 210 | -// | ||
| 211 | -// // 模板类型校验 | ||
| 212 | -// Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 213 | -// // 根据不同模板类型执行不同的处理逻辑 | ||
| 214 | -// if (WaterFeeImport.class.isAssignableFrom(importClass)) { | ||
| 215 | -// Map<Long, String> companyIdToNamedMap = CollectionUtils.isEmpty(companyList) ? Map.of() | ||
| 216 | -// : companyList.stream() | ||
| 217 | -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 218 | -// .collect(Collectors.toMap( | ||
| 219 | -// DeptRespDTO::getId, // key 映射:取 companyId | ||
| 220 | -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 221 | -// (oldValue, newValue) -> newValue | ||
| 222 | -// )); | ||
| 223 | -// | ||
| 224 | -// // 处理水费数据 | ||
| 225 | -// List<WaterFeeDO> feeList = ((List<WaterFeeImport>) dataList).stream() | ||
| 226 | -// .map(importObj -> { | ||
| 227 | -// WaterFeeDO fee = new WaterFeeDO(); | ||
| 228 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 229 | -// fee.setCompanyId(String.valueOf(getCompanyId())); | ||
| 230 | -// if (getCompanyId() == 100) { | ||
| 231 | -// fee.setCompanyName("蓟城山水"); | ||
| 232 | -// } else { | ||
| 233 | -// // 设置单位id | ||
| 234 | -// fee.setCompanyName(String.valueOf(companyIdToNamedMap.get(fee.getCompanyId()))); | ||
| 235 | -// } | ||
| 236 | -// return fee; | ||
| 237 | -// }) | ||
| 238 | -// .collect(Collectors.toMap( | ||
| 239 | -// // 生成唯一键:feeMonth + 分隔符 + meterCode(避免不同组合拼接后重复) | ||
| 240 | -// fee -> fee.getFeeMonth() + "_" + fee.getMeterCode(), | ||
| 241 | -// // 值为对象本身 | ||
| 242 | -// Function.identity(), | ||
| 243 | -// // 当键重复时,保留第一个出现的对象(可根据需求改为保留最后一个:(existing, replacement) -> replacement) | ||
| 244 | -// (existing, replacement) -> replacement | ||
| 245 | -// )) | ||
| 246 | -// .values() | ||
| 247 | -// .stream() | ||
| 248 | -// .collect(Collectors.toList()); | ||
| 249 | -// | ||
| 250 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 251 | -// //查询重复项,进行删除 | ||
| 252 | -// List<Long> ids = waterFeeMapper.selectDuplicateIds(feeList); | ||
| 253 | -// if (!CollectionUtils.isEmpty(ids)) { | ||
| 254 | -// waterFeeMapper.deleteByIds(ids); | ||
| 255 | -// } | ||
| 256 | -// // 调用批量插入方法 | ||
| 257 | -// waterFeeMapper.insertBatch(feeList); | ||
| 258 | -// } | ||
| 259 | -// log.info("成功插入 {} 条水费数据", feeList.size()); | ||
| 260 | -// } else if (PersonalCostImport.class.isAssignableFrom(importClass)) { | ||
| 261 | -// //去重 | ||
| 262 | -// dataList = personalFeeReplacement((List<PersonalCostImport>) dataList); | ||
| 263 | -// //获取班组对象 | ||
| 264 | -// Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 265 | -// | ||
| 266 | -// // 3. 转换导入数据为PersonalCost列表(Stream优化+空指针防护) | ||
| 267 | -// List<PersonalCostImport> feeList = (List<PersonalCostImport>)dataList; | ||
| 268 | -// List<PersonalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 269 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 270 | -// for (PersonalCostImport importObj : feeList) { | ||
| 271 | -// PersonalCostDO fee = new PersonalCostDO(); | ||
| 272 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 273 | -// // 设置单位id | ||
| 274 | -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 275 | -// //设置班组id | ||
| 276 | -// //fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 277 | -// saveFeeList.add(fee); | ||
| 278 | -// } | ||
| 279 | -// } | ||
| 280 | -// // 4. 处理重复数据(删除+插入原子性) | ||
| 281 | -// if (!feeList.isEmpty()) { | ||
| 282 | -// // 4.1 查询并删除重复项 | ||
| 283 | -// | ||
| 284 | -//// List<Long> duplicateIds = personalCostMapper.selectDuplicateIds(saveFeeList); | ||
| 285 | -//// if (!duplicateIds.isEmpty()) { | ||
| 286 | -//// personalCostMapper.deleteByIds(duplicateIds); | ||
| 287 | -//// } | ||
| 288 | -// | ||
| 289 | -// // 4.2 批量插入主表 | ||
| 290 | -// personalCostMapper.insertBatch(saveFeeList); | ||
| 291 | -// } | ||
| 292 | -// log.info("成功插入 {} 人员费用数据", dataList.size()); | ||
| 293 | -// } else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { | ||
| 294 | -// //去重 | ||
| 295 | -// dataList = deptFeeReplacement((List<DepartmentCostImport>)dataList); | ||
| 296 | -// List<DepartmentCostDO> feeList = ((List<DepartmentCostImport>) dataList).stream() | ||
| 297 | -// .map(importObj -> { | ||
| 298 | -// DepartmentCostDO fee = new DepartmentCostDO(); | ||
| 299 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 300 | -// // 设置单位id | ||
| 301 | -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 302 | -// return fee; | ||
| 303 | -// }) | ||
| 304 | -// .collect(Collectors.toList()); | ||
| 305 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 306 | -// //查询重复项,进行删除 | ||
| 307 | -// List<Long> ids = departmentCostMapper.selectDuplicateIds(feeList); | ||
| 308 | -// if (!CollectionUtils.isEmpty(ids)) { | ||
| 309 | -// departmentCostMapper.deleteByIds(ids); | ||
| 310 | -// } | ||
| 311 | -// // 调用批量插入方法 | ||
| 312 | -// departmentCostMapper.insertBatch(feeList); | ||
| 313 | -// } | ||
| 314 | -// log.info("成功插入 {} 综合费用数据", feeList.size()); | ||
| 315 | -// } else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 316 | -// List<MechanicalCostImport> feeList = (List<MechanicalCostImport>) dataList; | ||
| 317 | -// List<MechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 318 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 319 | -// //获取班组对象 | ||
| 320 | -// Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 321 | -// for (MechanicalCostImport importObj : feeList) { | ||
| 322 | -// MechanicalCostDO fee = new MechanicalCostDO(); | ||
| 323 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 324 | -// // 设置单位id | ||
| 325 | -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 326 | -// //设置班组id | ||
| 327 | -// fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 328 | -// // 3.3 计算结束时间(空指针防护) | ||
| 329 | -// Optional.ofNullable(fee.getStartUseTime()) | ||
| 330 | -// .ifPresent(startTime -> { | ||
| 331 | -// int useDuration = Optional.ofNullable(fee.getServiceLife()) | ||
| 332 | -// .map(Math::toIntExact) // 处理可能的溢出 | ||
| 333 | -// .orElse(0); | ||
| 334 | -// fee.setEndUseTime(this.getEndLocalDateForYears(startTime, useDuration)); | ||
| 335 | -// }); | ||
| 336 | -// | ||
| 337 | -// saveFeeList.add(fee); | ||
| 338 | -// } | ||
| 339 | -// } | ||
| 340 | -// // 4. 批量插入主表(空集合直接跳过) | ||
| 341 | -// if (!feeList.isEmpty()) { | ||
| 342 | -// //查询重复项,进行删除 | ||
| 343 | -// List<Long> ids = mechanicalCostMapper.selectDuplicateIds(saveFeeList); | ||
| 344 | -// if (!CollectionUtils.isEmpty(ids)) { | ||
| 345 | -// mechanicalCostMapper.deleteByIds(ids); | ||
| 346 | -// } | ||
| 347 | -// mechanicalCostMapper.insertBatch(saveFeeList); | ||
| 348 | -// } | ||
| 349 | -// log.info("成功插入 {} 自有机械费用数据", dataList.size()); | ||
| 350 | -// } else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 351 | -// List<RentalMechanicalCostImport> feeList = (List<RentalMechanicalCostImport>)dataList; | ||
| 352 | -// List<RentalMechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 353 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 354 | -// //获取班组对象 | ||
| 355 | -// Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 356 | -// for (RentalMechanicalCostImport importObj : feeList) { | ||
| 357 | -// RentalMechanicalCostDO fee = new RentalMechanicalCostDO(); | ||
| 358 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 359 | -// // 设置单位id | ||
| 360 | -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 361 | -// //设置班组id | ||
| 362 | -// List<TeamBizdomainRelVo> teamRels = new ArrayList<>(); | ||
| 363 | -// String teamIdsStr = StringUtils.defaultString(importObj.getTeamIds()).replace(",", ","); | ||
| 364 | -// String weightsStr = StringUtils.defaultString(importObj.getWeight()).replace(",", ","); | ||
| 365 | -// | ||
| 366 | -// String[] teamIds = teamIdsStr.split(","); | ||
| 367 | -// String[] weights = weightsStr.split(","); | ||
| 368 | -// for (int i = 0; i < teamIds.length; i++) { | ||
| 369 | -// TeamBizdomainRelVo rel = new TeamBizdomainRelVo(); | ||
| 370 | -// rel.setTeamName(teamIds[i].trim()); | ||
| 371 | -// // 提前处理部门ID映射(复用deptMap) | ||
| 372 | -// rel.setTeamId(Optional.ofNullable(teamIdMap.get(teamIds[i].trim())).map(String::valueOf).orElse(null)); | ||
| 373 | -// rel.setWeight(Long.valueOf(weights[i].trim())); | ||
| 374 | -// teamRels.add(rel); | ||
| 375 | -// } | ||
| 376 | -// fee.setTeamIds(teamRels); | ||
| 377 | -// | ||
| 378 | -// // 3.3 计算结束时间(空指针防护) | ||
| 379 | -// Optional.ofNullable(fee.getStartUseTime()) | ||
| 380 | -// .ifPresent(startTime -> { | ||
| 381 | -// int useDuration = Optional.ofNullable(fee.getUseDuration()) | ||
| 382 | -// .map(Math::toIntExact) // 处理可能的溢出 | ||
| 383 | -// .orElse(0); | ||
| 384 | -// fee.setEndUseTime(getEndLocalDateForYears(startTime, useDuration)); | ||
| 385 | -// }); | ||
| 386 | -// | ||
| 387 | -// saveFeeList.add(fee); | ||
| 388 | -// } | ||
| 389 | -// } | ||
| 390 | -// if (!feeList.isEmpty()) { | ||
| 391 | -// rentalMechanicalCostMapper.insertBatch(saveFeeList); | ||
| 392 | -// } | ||
| 393 | -// log.info("成功插入 {} 租赁机械费用数据", dataList.size()); | ||
| 394 | -// } else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { | ||
| 395 | -// //获取班组对象 | ||
| 396 | -// List<GardenWasteRecordImport> feeList = (List<GardenWasteRecordImport>)dataList; | ||
| 397 | -// List<WasteRecordDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 398 | -// if (!CollectionUtils.isEmpty(feeList)) { | ||
| 399 | -// Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 400 | -// for (GardenWasteRecordImport importObj : feeList) { | ||
| 401 | -// WasteRecordDO fee = new WasteRecordDO(); | ||
| 402 | -// BeanUtils.copyProperties(importObj, fee); | ||
| 403 | -// // 设置单位id | ||
| 404 | -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 405 | -// //设置班组id | ||
| 406 | -// fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 407 | -// | ||
| 408 | -// saveFeeList.add(fee); | ||
| 409 | -// } | ||
| 410 | -// } | ||
| 411 | -// // 4. 批量插入主表(若主表插入失败,事务回滚) | ||
| 412 | -// if (!feeList.isEmpty()) { | ||
| 413 | -// wasteRecordMapper.insertBatch(saveFeeList); | ||
| 414 | -// } | ||
| 415 | -// | ||
| 416 | -// log.info("成功插入 {} 园林废弃物费用数据", dataList.size()); | ||
| 417 | -// } | ||
| 418 | -// | ||
| 419 | -// } | ||
| 420 | -// | ||
| 421 | -// /** | ||
| 422 | -// * 从Redis获取分页数据 | ||
| 423 | -// */ | ||
| 424 | -// @Override | ||
| 425 | -// public <T > CostFeeController.PageResult < T > getPagedDataFromRedis(String batchId, String templateName, | ||
| 426 | -// int page, int size, Class<T > clazz) throws IOException { | ||
| 427 | -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 428 | -// | ||
| 429 | -// // 获取总记录数 | ||
| 430 | -// String totalStr = redisTemplate.opsForValue().get(redisKey + ":total"); | ||
| 431 | -// int total = totalStr != null ? Integer.parseInt(totalStr) : 0; | ||
| 432 | -// | ||
| 433 | -// // 计算分页范围 | ||
| 434 | -// int start = (page - 1) * size; | ||
| 435 | -// int end = Math.min(start + size - 1, total - 1); | ||
| 436 | -// | ||
| 437 | -// // 分页查询数据 | ||
| 438 | -// List<T> pageData = new ArrayList<>(); | ||
| 439 | -// if (start <= end && total > 0) { | ||
| 440 | -// BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 441 | -// | ||
| 442 | -// // 批量获取指定范围的索引对应的数据 | ||
| 443 | -// List<String> indexes = IntStream.rangeClosed(start, end) | ||
| 444 | -// .mapToObj(String::valueOf) | ||
| 445 | -// .collect(Collectors.toList()); | ||
| 446 | -// List<String> jsonList = hashOps.multiGet(indexes); | ||
| 447 | -// | ||
| 448 | -// // 反序列化为对象 | ||
| 449 | -// for (String json : jsonList) { | ||
| 450 | -// if (json != null) { | ||
| 451 | -// pageData.add(objectMapper.readValue(json, clazz)); | ||
| 452 | -// } | ||
| 453 | -// } | ||
| 454 | -// } | ||
| 455 | -// | ||
| 456 | -// return new CostFeeController.PageResult<>(page, size, total, pageData); | ||
| 457 | -// } | ||
| 458 | -// | ||
| 459 | -// | ||
| 460 | -// /** | ||
| 461 | -// * 从Redis获取全部数据 | ||
| 462 | -// */ | ||
| 463 | -// private <T > List < T > getAllDataFromRedis(String batchId, String templateName, Class < T > clazz) throws | ||
| 464 | -// IOException { | ||
| 465 | -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 466 | -// BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 467 | -// | ||
| 468 | -// // 获取所有数据的JSON字符串 | ||
| 469 | -// List<String> jsonList = hashOps.values(); | ||
| 470 | -// List<T> dataList = new ArrayList<>(jsonList.size()); | ||
| 471 | -// | ||
| 472 | -// // 反序列化为对象 | ||
| 473 | -// for (String json : jsonList) { | ||
| 474 | -// if (json != null) { | ||
| 475 | -// dataList.add(objectMapper.readValue(json, clazz)); | ||
| 476 | -// } | ||
| 477 | -// } | ||
| 478 | -// | ||
| 479 | -// return dataList; | ||
| 480 | -// } | ||
| 481 | -// | ||
| 482 | -// | ||
| 483 | -// /** | ||
| 484 | -// * 删除Redis临时数据 | ||
| 485 | -// */ | ||
| 486 | -// private void deleteRedisTempData (String batchId, String templateName){ | ||
| 487 | -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 488 | -// redisTemplate.delete(redisKey + ":total"); | ||
| 489 | -// redisTemplate.delete(redisKey + ":items"); | ||
| 490 | -// log.info("Redis临时数据已删除,批次ID:{}", batchId); | ||
| 491 | -// } | ||
| 492 | -// /** | ||
| 493 | -// * @Author wangqian | ||
| 494 | -// * @Description 导入内容校验 | ||
| 495 | -// * @Date 2025/11/24 8:40 | ||
| 496 | -// * @Param dataList | ||
| 497 | -// * @param template | ||
| 498 | -// * @Return void | ||
| 499 | -// */ | ||
| 500 | -// public void validateImportData(List dataList, TemplateEnum template) { | ||
| 501 | -// if (CollectionUtils.isEmpty(dataList)) { | ||
| 502 | -// return; // 空列表无需校验 | ||
| 503 | -// } | ||
| 504 | -// //登录用户单位 | ||
| 505 | -// Long belongCompanyId = getCompanyId(); | ||
| 506 | -// //单位集合 | ||
| 507 | -// List<DeptRespDTO> companyList = deptApi.getSubDeptList(); | ||
| 508 | -// List<Long> deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); | ||
| 509 | -// Map<String, Long> nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) | ||
| 510 | -// ? Map.of() // 空列表返回空不可变 Map(避免 null) | ||
| 511 | -// : companyList.stream() | ||
| 512 | -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 513 | -// .collect(Collectors.toMap( | ||
| 514 | -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 515 | -// DeptRespDTO::getId, // key 映射:取 companyId | ||
| 516 | -// (oldValue, newValue) -> newValue | ||
| 517 | -// )); | ||
| 518 | -// //班组集合 | ||
| 519 | -// Map<Long, List<String>> deptList = this.getDetpList(deptIds); | ||
| 520 | -// // 模板类型校验 | ||
| 521 | -// Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 522 | -// // 根据不同模板类型执行不同的处理逻辑 | ||
| 523 | -// if (WaterFeeImport.class.isAssignableFrom(importClass)) { | ||
| 524 | -// | ||
| 525 | -// List<WaterFeeImport> importList = (List<WaterFeeImport>) dataList; | ||
| 526 | -// | ||
| 527 | -// // 2. 遍历,逐个创建 | ||
| 528 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 529 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 530 | -// WaterFeeImport importBean = importList.get(i); | ||
| 531 | -// | ||
| 532 | -// try { | ||
| 533 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 534 | -// Set<ConstraintViolation<WaterFeeImport>> violations = validator.validate(importBean); | ||
| 535 | -// if (!violations.isEmpty()) { | ||
| 536 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 537 | -// // StringBuilder errorMsg = new StringBuilder(); | ||
| 538 | -// for (ConstraintViolation<WaterFeeImport> violation : violations) { | ||
| 539 | -// //errorMsg.append(violation.getMessage()).append(";"); | ||
| 540 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 541 | -// "第[{}]条数据:"+ violation.getMessage() | ||
| 542 | -// ), rowNum); | ||
| 543 | -// } | ||
| 544 | -// // 抛出包含行号和所有错误的异常 | ||
| 545 | -// //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 546 | -// } | ||
| 547 | -// | ||
| 548 | -// } catch (ConstraintViolationException ex) { | ||
| 549 | -// // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 550 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 551 | -// } | ||
| 552 | -// } | ||
| 553 | -// } | ||
| 554 | -// else if (PersonalCostImport.class.isAssignableFrom(importClass)) { | ||
| 555 | -// | ||
| 556 | -// List<PersonalCostImport> importList = (List<PersonalCostImport>) dataList; | ||
| 557 | -// // 2. 遍历,逐个创建 | ||
| 558 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 559 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 560 | -// PersonalCostImport importBean = importList.get(i); | ||
| 561 | -// | ||
| 562 | -// try { | ||
| 563 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 564 | -// Set<ConstraintViolation<PersonalCostImport>> violations = validator.validate(importBean); | ||
| 565 | -// if (!violations.isEmpty()) { | ||
| 566 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 567 | -// //StringBuilder errorMsg = new StringBuilder(); | ||
| 568 | -// for (ConstraintViolation<PersonalCostImport> violation : violations) { | ||
| 569 | -// // errorMsg.append(violation.getMessage()).append(";"); | ||
| 570 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 571 | -// "第[{}]条数据:"+ violation.getMessage() | ||
| 572 | -// ), rowNum); | ||
| 573 | -// } | ||
| 574 | -// // 抛出包含行号和所有错误的异常 | ||
| 575 | -// // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 576 | -// } | ||
| 577 | -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 578 | -// //单位校验 | ||
| 579 | -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 580 | -// //部门名称校验 | ||
| 581 | -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 582 | -// | ||
| 583 | -// } catch (ConstraintViolationException ex) { | ||
| 584 | -// // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 585 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 586 | -// } | ||
| 587 | -// } | ||
| 588 | -// } | ||
| 589 | -// else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { | ||
| 590 | -// | ||
| 591 | -// List<GardenWasteRecordImport> importList = (List<GardenWasteRecordImport>) dataList; | ||
| 592 | -// // 2. 遍历,逐个创建 | ||
| 593 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 594 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 595 | -// GardenWasteRecordImport importBean = importList.get(i); | ||
| 596 | -// | ||
| 597 | -// try { | ||
| 598 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 599 | -// Set<ConstraintViolation<GardenWasteRecordImport>> violations = validator.validate(importBean); | ||
| 600 | -// if (!violations.isEmpty()) { | ||
| 601 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 602 | -// //StringBuilder errorMsg = new StringBuilder(); | ||
| 603 | -// for (ConstraintViolation<GardenWasteRecordImport> violation : violations) { | ||
| 604 | -// //errorMsg.append(violation.getMessage()).append(";"); | ||
| 605 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 606 | -// "第[{}]条数据:"+ violation.getMessage() | ||
| 607 | -// ), rowNum); | ||
| 608 | -// } | ||
| 609 | -// // 抛出包含行号和所有错误的异常 | ||
| 610 | -// //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 611 | -// } | ||
| 612 | -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 613 | -// //单位校验 | ||
| 614 | -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 615 | -// //部门名称校验 | ||
| 616 | -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 617 | -// | ||
| 618 | -// } catch (ConstraintViolationException ex) { | ||
| 619 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 620 | -// } | ||
| 621 | -// } | ||
| 622 | -// } | ||
| 623 | -// else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 624 | -// | ||
| 625 | -// List<MechanicalCostImport> importList = (List<MechanicalCostImport>) dataList; | ||
| 626 | -// // 2. 遍历,逐个创建 | ||
| 627 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 628 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 629 | -// MechanicalCostImport importBean = importList.get(i); | ||
| 630 | -// | ||
| 631 | -// try { | ||
| 632 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 633 | -// Set<ConstraintViolation<MechanicalCostImport>> violations = validator.validate(importBean); | ||
| 634 | -// if (!violations.isEmpty()) { | ||
| 635 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 636 | -// //StringBuilder errorMsg = new StringBuilder(); | ||
| 637 | -// for (ConstraintViolation<MechanicalCostImport> violation : violations) { | ||
| 638 | -// //errorMsg.append(violation.getMessage()).append(";"); | ||
| 639 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 640 | -// "第[{}]条数据:"+ violation.getMessage() | ||
| 641 | -// ), rowNum); | ||
| 642 | -// } | ||
| 643 | -// // 抛出包含行号和所有错误的异常 | ||
| 644 | -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 645 | -// } | ||
| 646 | -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 647 | -// //单位校验 | ||
| 648 | -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 649 | -// //部门名称校验 | ||
| 650 | -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 651 | -// | ||
| 652 | -// //车辆校验 | ||
| 653 | -// if (CostFeeConstants.MECH_TYPE.equals(importBean.getMechType())) { | ||
| 654 | -// if (StringUtils.isEmpty(importBean.getLicensePlate())) { | ||
| 655 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 656 | -// "第[{}]条数据:车牌号不能为空" | ||
| 657 | -// ), rowNum); | ||
| 658 | -// } | ||
| 659 | -// if (StringUtils.isEmpty(importBean.getVehicleType())) { | ||
| 660 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 661 | -// "第[{}]条数据:车辆类型不能为空" | ||
| 662 | -// ), rowNum); | ||
| 663 | -// | ||
| 664 | -// } | ||
| 665 | -// if (StringUtils.isEmpty(importBean.getVehicleCategory())) { | ||
| 666 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 667 | -// "第%d条数据:车辆类别不能为空" | ||
| 668 | -// ), rowNum); | ||
| 669 | -// } | ||
| 670 | -// } | ||
| 671 | -// | ||
| 672 | -// } catch (ConstraintViolationException ex) { | ||
| 673 | -// // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 674 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 675 | -// } | ||
| 676 | -// } | ||
| 677 | -// } | ||
| 678 | -// else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 679 | -// | ||
| 680 | -// List<RentalMechanicalCostImport> importList = (List<RentalMechanicalCostImport>) dataList; | ||
| 681 | -// // 2. 遍历,逐个创建 | ||
| 682 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 683 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 684 | -// RentalMechanicalCostImport importBean = importList.get(i); | ||
| 685 | -// | ||
| 686 | -// try { | ||
| 687 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 688 | -// Set<ConstraintViolation<RentalMechanicalCostImport>> violations = validator.validate(importBean); | ||
| 689 | -// if (!violations.isEmpty()) { | ||
| 690 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 691 | -// // StringBuilder errorMsg = new StringBuilder(); | ||
| 692 | -// for (ConstraintViolation<RentalMechanicalCostImport> violation : violations) { | ||
| 693 | -// // errorMsg.append(violation.getMessage()).append(";"); | ||
| 694 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 695 | -// "第[{}]条数据:"+violation.getMessage() | ||
| 696 | -// ), rowNum); | ||
| 697 | -// } | ||
| 698 | -// // 抛出包含行号和所有错误的异常 | ||
| 699 | -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 700 | -// } | ||
| 701 | -// // 处理可能的空值 | ||
| 702 | -// String teamIdsStr = StringUtils.defaultString(importBean.getTeamIds()); | ||
| 703 | -// String weightsStr = StringUtils.defaultString(importBean.getWeight()); | ||
| 704 | -// | ||
| 705 | -// // 统一替换中文逗号并分割 | ||
| 706 | -// String[] teamIds = teamIdsStr.replace(",", ",").split(","); | ||
| 707 | -// String[] weights = weightsStr.replace(",", ",").split(","); | ||
| 708 | -// | ||
| 709 | -// // 校验数组长度是否一致 | ||
| 710 | -// if (teamIds.length != weights.length) { | ||
| 711 | -// throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 712 | -// "第%d条数据:权重值和所选班组数量不符(班组:%d个,权重:%d个)", | ||
| 713 | -// rowNum, teamIds.length, weights.length | ||
| 714 | -// )); | ||
| 715 | -// } | ||
| 716 | -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 717 | -// //单位校验 | ||
| 718 | -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 719 | -// //部门名称校验 | ||
| 720 | -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 721 | -// | ||
| 722 | -// } catch (ConstraintViolationException ex) { | ||
| 723 | -// // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 724 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 725 | -// } | ||
| 726 | -// } | ||
| 727 | -// } | ||
| 728 | -// else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { | ||
| 729 | -// | ||
| 730 | -// List<DepartmentCostImport> importList = (List<DepartmentCostImport>) dataList; | ||
| 731 | -// // 2. 遍历,逐个创建 | ||
| 732 | -// for (int i = 0; i < importList.size(); i++) { | ||
| 733 | -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 734 | -// DepartmentCostImport importBean = importList.get(i); | ||
| 735 | -// | ||
| 736 | -// try { | ||
| 737 | -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 738 | -// Set<ConstraintViolation<DepartmentCostImport>> violations = validator.validate(importBean); | ||
| 739 | -// if (!violations.isEmpty()) { | ||
| 740 | -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 741 | -// //StringBuilder errorMsg = new StringBuilder(); | ||
| 742 | -// for (ConstraintViolation<DepartmentCostImport> violation : violations) { | ||
| 743 | -// //errorMsg.append(violation.getMessage()).append(";"); | ||
| 744 | -// throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 745 | -// "第[{}]条数据:"+violation.getMessage() | ||
| 746 | -// ), rowNum); | ||
| 747 | -// } | ||
| 748 | -// // 抛出包含行号和所有错误的异常 | ||
| 749 | -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 750 | -// } | ||
| 751 | -// | ||
| 752 | -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 753 | -// //单位校验 | ||
| 754 | -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 755 | -// } catch (ConstraintViolationException ex) { | ||
| 756 | -// // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 757 | -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 758 | -// } | ||
| 759 | -// } | ||
| 760 | -// } | ||
| 761 | -// } | ||
| 762 | -// | ||
| 763 | -// private void validatecompany(int rowNum, String companyName, Long companyId, Long belongCompanyId){ | ||
| 764 | -// // 校验数组长度是否一致 | ||
| 765 | -// if (companyId == null) { | ||
| 766 | -// throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 767 | -// "第%d条数据:单位名称不正确(%s)", | ||
| 768 | -// rowNum, companyName | ||
| 769 | -// )); | ||
| 770 | -// } | ||
| 771 | -// // 校验导入单位 | ||
| 772 | -// if (belongCompanyId != 100 && !belongCompanyId.equals(companyId)) { | ||
| 773 | -// throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 774 | -// "第%d条数据:导入单位%s与当前操作员单位不一致", | ||
| 775 | -// rowNum, companyName | ||
| 776 | -// )); | ||
| 777 | -// } | ||
| 778 | -// } | ||
| 779 | -// private void validateTeams(int rowNum, List<String> dept, String teamIds) { | ||
| 780 | -// if (StringUtils.isNotEmpty(teamIds)) { | ||
| 781 | -// String[] teamNames = teamIds.split(","); | ||
| 782 | -// Set<String> checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复 | ||
| 783 | -// | ||
| 784 | -// for (String teamName : teamNames) { | ||
| 785 | -// String trimmedTeamName = teamName.trim(); // 处理前后空格 | ||
| 786 | -// | ||
| 787 | -// // 重复校验:如果Set中已存在该团队名称,说明重复 | ||
| 788 | -// if (checkedTeams.contains(trimmedTeamName)) { | ||
| 789 | -// throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 790 | -// "第%d条数据:%s名称重复", | ||
| 791 | -// rowNum, teamName | ||
| 792 | -// )); | ||
| 793 | -// } | ||
| 794 | -// | ||
| 795 | -// // 存在性校验:检查团队是否在dept中存在 | ||
| 796 | -// if (!dept.contains(trimmedTeamName)) { | ||
| 797 | -// throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 798 | -// "第%d条数据:%s名称不正确", | ||
| 799 | -// rowNum, teamName | ||
| 800 | -// )); | ||
| 801 | -// } | ||
| 802 | -// | ||
| 803 | -// // 将校验通过的团队名称加入Set,用于后续重复判断 | ||
| 804 | -// checkedTeams.add(trimmedTeamName); | ||
| 805 | -// } | ||
| 806 | -// } | ||
| 807 | -// } | ||
| 808 | -// | ||
| 809 | -// //查询单位下的班组信息 | ||
| 810 | -// private Map<Long,List<String>> getDetpList (List<Long> deptIds) { | ||
| 811 | -// Map<Long,List<String>> listMap = new HashMap<>(); | ||
| 812 | -// for (Long depId : deptIds) { | ||
| 813 | -// List<DeptRespDTO> deptList = deptApi.getChildDeptList(depId); | ||
| 814 | -// List<String> names = deptList.stream().map(DeptRespDTO::getName).collect(Collectors.toList()); | ||
| 815 | -// listMap.put(depId,names); | ||
| 816 | -// } | ||
| 817 | -// return listMap; | ||
| 818 | -// } | ||
| 819 | -// | ||
| 820 | -// /** | ||
| 821 | -// * 按salaryMonth、expenseName、companyId三个字段去重 | ||
| 822 | -// * @param costList 原始部门成本列表 | ||
| 823 | -// * @return 去重后的列表 | ||
| 824 | -// */ | ||
| 825 | -// public List<DepartmentCostImport> deptFeeReplacement(List<DepartmentCostImport> costList) { | ||
| 826 | -// if (costList == null || costList.isEmpty()) { | ||
| 827 | -// return new ArrayList<>(); | ||
| 828 | -// } | ||
| 829 | -// | ||
| 830 | -// // 1. 以三个字段的组合作为键(使用分隔符避免不同组合产生相同键) | ||
| 831 | -// // 2. 遇到重复键时保留第一条记录 | ||
| 832 | -// Map<String, DepartmentCostImport> uniqueMap = costList.stream() | ||
| 833 | -// .collect(Collectors.toMap( | ||
| 834 | -// // 生成唯一键:salaryMonth|expenseName|companyId | ||
| 835 | -// cost -> buildUniqueKey(cost), | ||
| 836 | -// // 键不存在时存入当前对象 | ||
| 837 | -// cost -> cost, | ||
| 838 | -// // 键已存在时保留最后一个 | ||
| 839 | -// (existing, replacement) -> replacement | ||
| 840 | -// )); | ||
| 841 | -// | ||
| 842 | -// // 将去重后的结果转为List返回 | ||
| 843 | -// return new ArrayList<>(uniqueMap.values()); | ||
| 844 | -// } | ||
| 845 | -// | ||
| 846 | -// /** | ||
| 847 | -// * 构建三个字段的唯一组合键(处理null值) | ||
| 848 | -// */ | ||
| 849 | -// private String buildUniqueKey(DepartmentCostImport cost) { | ||
| 850 | -// // 使用Objects.toString处理可能的null值,避免NullPointerException | ||
| 851 | -// return Objects.toString(cost.getSalaryMonth(), "") + "|" + | ||
| 852 | -// Objects.toString(cost.getExpenseName(), "") + "|" + | ||
| 853 | -// Objects.toString(cost.getCompanyName(), ""); | ||
| 854 | -// } | ||
| 855 | -// | ||
| 856 | -// /** | ||
| 857 | -// * 按salaryMonth、name、companyId、teamIdList四个字段去重 | ||
| 858 | -// * @param costList 原始个人成本列表 | ||
| 859 | -// * @return 去重后的列表 | ||
| 860 | -// */ | ||
| 861 | -// public List<PersonalCostImport> personalFeeReplacement(List<PersonalCostImport> costList) { | ||
| 862 | -// if (costList == null || costList.isEmpty()) { | ||
| 863 | -// return new ArrayList<>(); | ||
| 864 | -// } | ||
| 865 | -// | ||
| 866 | -// // 1. 以四个字段的组合作为键(特别处理集合类型的teamIdList) | ||
| 867 | -// // 2. 遇到重复键时保留第一条记录 | ||
| 868 | -// Map<String, PersonalCostImport> uniqueMap = costList.stream() | ||
| 869 | -// .collect(Collectors.toMap( | ||
| 870 | -// // 生成唯一键:salaryMonth|name|companyId|teamIdListStr | ||
| 871 | -// this::buildUniqueKey, | ||
| 872 | -// // 键不存在时存入当前对象 | ||
| 873 | -// cost -> cost, | ||
| 874 | -// // 键已存在时保留第一个对象(可改为replacement保留最后一个) | ||
| 875 | -// (existing, replacement) -> replacement | ||
| 876 | -// )); | ||
| 877 | -// | ||
| 878 | -// // 将去重后的结果转为List返回 | ||
| 879 | -// return new ArrayList<>(uniqueMap.values()); | ||
| 880 | -// } | ||
| 881 | -// | ||
| 882 | -// /** | ||
| 883 | -// * 构建四个字段的唯一组合键(处理null值和集合类型) | ||
| 884 | -// */ | ||
| 885 | -// private String buildUniqueKey(PersonalCostImport cost) { | ||
| 886 | -// // 处理普通字段的null值 | ||
| 887 | -// String salaryMonth = Objects.toString(cost.getSalaryMonth(), ""); | ||
| 888 | -// String name = Objects.toString(cost.getName(), ""); | ||
| 889 | -// String companyId = Objects.toString(cost.getCompanyName(), ""); | ||
| 890 | -// String teamIds = Objects.toString(cost.getTeamIds(), ""); | ||
| 891 | -// // 拼接唯一键 | ||
| 892 | -// return salaryMonth + "|" + name + "|" + companyId + "|" + teamIds; | ||
| 893 | -// } | ||
| 894 | -// | ||
| 895 | -// | ||
| 896 | -// public static LocalDate getEndLocalDateForYears(LocalDate startDate, int years) { | ||
| 897 | -// if (startDate == null) { | ||
| 898 | -// throw new IllegalArgumentException("开始日期不能为null"); | ||
| 899 | -// } | ||
| 900 | -// if (years < 0) { | ||
| 901 | -// throw new IllegalArgumentException("年数不能为负数"); | ||
| 902 | -// } | ||
| 903 | -// return startDate.plusYears(years); | ||
| 904 | -// } | ||
| 905 | -// | ||
| 906 | -// /** | ||
| 907 | -// * @Author wangqian | ||
| 908 | -// * @Description 根据班组名称获取班组id | ||
| 909 | -// * @Date 2025/11/26 19:38 | ||
| 910 | -// */ | ||
| 911 | -// /** | ||
| 912 | -// * 根据部门ID列表获取「部门名称→部门ID」的映射(含空值防护) | ||
| 913 | -// */ | ||
| 914 | -// private Map<String, Long> getDeptListAll(List<Long> deptIds) { | ||
| 915 | -// Map<String, Long> deptNameMap = new HashMap<>(); | ||
| 916 | -// | ||
| 917 | -// // 1. 空值安全:避免 deptIds 为 null/空列表导致 NPE | ||
| 918 | -// if (CollectionUtils.isEmpty(deptIds)) { | ||
| 919 | -// return deptNameMap; | ||
| 920 | -// } | ||
| 921 | -// | ||
| 922 | -// for (Long deptId : deptIds) { | ||
| 923 | -// // 2. 跳过 null 的部门ID | ||
| 924 | -// if (deptId == null) { | ||
| 925 | -// continue; | ||
| 926 | -// } | ||
| 927 | -// | ||
| 928 | -// // 3. 获取子部门列表(防护接口返回 null) | ||
| 929 | -// List<DeptRespDTO> deptList = deptApi.getChildDeptList(deptId); | ||
| 930 | -// if (CollectionUtils.isEmpty(deptList)) { | ||
| 931 | -// continue; | ||
| 932 | -// } | ||
| 933 | -// | ||
| 934 | -// // 4. 遍历子部门:过滤无效名称,避免 Map 出现 null/空字符串键 | ||
| 935 | -// for (DeptRespDTO dept : deptList) { | ||
| 936 | -// if (dept == null) { | ||
| 937 | -// continue; | ||
| 938 | -// } | ||
| 939 | -// String deptName = dept.getName(); | ||
| 940 | -// // 过滤 null/空白名称(避免 Map 键为 null 或空字符串) | ||
| 941 | -// if (deptName == null || deptName.trim().isEmpty()) { | ||
| 942 | -// continue; | ||
| 943 | -// } | ||
| 944 | -// // 若名称重复,后遍历的部门会覆盖先遍历的(可根据业务调整规则) | ||
| 945 | -// deptNameMap.put(deptName.trim(), dept.getId()); | ||
| 946 | -// } | ||
| 947 | -// } | ||
| 948 | -// | ||
| 949 | -// return deptNameMap; | ||
| 950 | -// } | ||
| 951 | -// /** | ||
| 952 | -// * @Author wangqian | ||
| 953 | -// * @Description 根据班组名称获取班组id集合 | ||
| 954 | -// * @Date 2025/11/26 20:07 | ||
| 955 | -// */ | ||
| 956 | -// private List<String> getDeptList(Map<String, Long> teamIdMap, String teamId) { | ||
| 957 | -// List<String> list = new ArrayList<>(); | ||
| 958 | -// | ||
| 959 | -// // 1. 处理 teamId 为空的情况,直接返回空列表 | ||
| 960 | -// if (StringUtils.isEmpty(teamId)) { | ||
| 961 | -// return list; | ||
| 962 | -// } | ||
| 963 | -// | ||
| 964 | -// // 2. 分割字符串,处理空元素 | ||
| 965 | -// String[] teamArray = teamId.split(","); | ||
| 966 | -// for (String teamName : teamArray) { | ||
| 967 | -// // 3. 去除首尾空格,避免因空格导致匹配不到 | ||
| 968 | -// String trimTeamName = teamName.trim(); | ||
| 969 | -// if (StringUtils.isEmpty(trimTeamName)) { | ||
| 970 | -// continue; // 跳过空字符串元素 | ||
| 971 | -// } | ||
| 972 | -// | ||
| 973 | -// // 4. 从Map中获取ID,处理null值(避免添加null到列表) | ||
| 974 | -// Long deptId = teamIdMap.get(trimTeamName); | ||
| 975 | -// if (deptId != null) { | ||
| 976 | -// list.add(String.valueOf(deptId)); // 直接添加Long类型,修正原String类型错误 | ||
| 977 | -// } | ||
| 978 | -// } | ||
| 979 | -// | ||
| 980 | -// return list; | ||
| 981 | -// } | ||
| 982 | -//} | 287 | + |
| 288 | + // 4.2 批量插入主表 | ||
| 289 | + personalCostMapper.insertBatch(saveFeeList); | ||
| 290 | + } | ||
| 291 | + log.info("成功插入 {} 人员费用数据", dataList.size()); | ||
| 292 | + } else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { | ||
| 293 | + //去重 | ||
| 294 | + dataList = deptFeeReplacement((List<DepartmentCostImport>)dataList); | ||
| 295 | + List<DepartmentCostDO> feeList = ((List<DepartmentCostImport>) dataList).stream() | ||
| 296 | + .map(importObj -> { | ||
| 297 | + DepartmentCostDO fee = new DepartmentCostDO(); | ||
| 298 | + BeanUtils.copyProperties(importObj, fee); | ||
| 299 | + // 设置单位id | ||
| 300 | + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 301 | + return fee; | ||
| 302 | + }) | ||
| 303 | + .collect(Collectors.toList()); | ||
| 304 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 305 | + //查询重复项,进行删除 | ||
| 306 | + List<Long> ids = departmentCostMapper.selectDuplicateIds(feeList); | ||
| 307 | + if (!CollectionUtils.isEmpty(ids)) { | ||
| 308 | + departmentCostMapper.deleteByIds(ids); | ||
| 309 | + } | ||
| 310 | + // 调用批量插入方法 | ||
| 311 | + departmentCostMapper.insertBatch(feeList); | ||
| 312 | + } | ||
| 313 | + log.info("成功插入 {} 综合费用数据", feeList.size()); | ||
| 314 | + } else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 315 | + List<MechanicalCostImport> feeList = (List<MechanicalCostImport>) dataList; | ||
| 316 | + List<MechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 317 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 318 | + //获取班组对象 | ||
| 319 | + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 320 | + for (MechanicalCostImport importObj : feeList) { | ||
| 321 | + MechanicalCostDO fee = new MechanicalCostDO(); | ||
| 322 | + BeanUtils.copyProperties(importObj, fee); | ||
| 323 | + // 设置单位id | ||
| 324 | + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 325 | + //设置班组id | ||
| 326 | + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 327 | + // 3.3 计算结束时间(空指针防护) | ||
| 328 | + Optional.ofNullable(fee.getStartUseTime()) | ||
| 329 | + .ifPresent(startTime -> { | ||
| 330 | + int useDuration = Optional.ofNullable(fee.getServiceLife()) | ||
| 331 | + .map(Math::toIntExact) // 处理可能的溢出 | ||
| 332 | + .orElse(0); | ||
| 333 | + fee.setEndUseTime(this.getEndLocalDateForYears(startTime, useDuration)); | ||
| 334 | + }); | ||
| 335 | + | ||
| 336 | + saveFeeList.add(fee); | ||
| 337 | + } | ||
| 338 | + } | ||
| 339 | + // 4. 批量插入主表(空集合直接跳过) | ||
| 340 | + if (!feeList.isEmpty()) { | ||
| 341 | + //查询重复项,进行删除 | ||
| 342 | + List<Long> ids = mechanicalCostMapper.selectDuplicateIds(saveFeeList); | ||
| 343 | + if (!CollectionUtils.isEmpty(ids)) { | ||
| 344 | + mechanicalCostMapper.deleteByIds(ids); | ||
| 345 | + } | ||
| 346 | + mechanicalCostMapper.insertBatch(saveFeeList); | ||
| 347 | + } | ||
| 348 | + log.info("成功插入 {} 自有机械费用数据", dataList.size()); | ||
| 349 | + } else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 350 | + List<RentalMechanicalCostImport> feeList = (List<RentalMechanicalCostImport>)dataList; | ||
| 351 | + List<RentalMechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 352 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 353 | + //获取班组对象 | ||
| 354 | + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 355 | + for (RentalMechanicalCostImport importObj : feeList) { | ||
| 356 | + RentalMechanicalCostDO fee = new RentalMechanicalCostDO(); | ||
| 357 | + BeanUtils.copyProperties(importObj, fee); | ||
| 358 | + // 设置单位id | ||
| 359 | + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 360 | + //设置班组id | ||
| 361 | + List<TeamBizdomainRelVo> teamRels = new ArrayList<>(); | ||
| 362 | + String teamIdsStr = StringUtils.defaultString(importObj.getTeamIds()).replace(",", ","); | ||
| 363 | + String weightsStr = StringUtils.defaultString(importObj.getWeight()).replace(",", ","); | ||
| 364 | + | ||
| 365 | + String[] teamIds = teamIdsStr.split(","); | ||
| 366 | + String[] weights = weightsStr.split(","); | ||
| 367 | + for (int i = 0; i < teamIds.length; i++) { | ||
| 368 | + TeamBizdomainRelVo rel = new TeamBizdomainRelVo(); | ||
| 369 | + rel.setTeamName(teamIds[i].trim()); | ||
| 370 | + // 提前处理部门ID映射(复用deptMap) | ||
| 371 | + rel.setTeamId(Optional.ofNullable(teamIdMap.get(teamIds[i].trim())).map(Long::valueOf).orElse(null)); | ||
| 372 | + rel.setWeight(Long.valueOf(weights[i].trim())); | ||
| 373 | + teamRels.add(rel); | ||
| 374 | + } | ||
| 375 | + fee.setTeamIds(teamRels); | ||
| 376 | + | ||
| 377 | + // 3.3 计算结束时间(空指针防护) | ||
| 378 | + Optional.ofNullable(fee.getStartUseTime()) | ||
| 379 | + .ifPresent(startTime -> { | ||
| 380 | + int useDuration = Optional.ofNullable(fee.getUseDuration()) | ||
| 381 | + .map(Math::toIntExact) // 处理可能的溢出 | ||
| 382 | + .orElse(0); | ||
| 383 | + fee.setEndUseTime(getEndLocalDateForYears(startTime, useDuration)); | ||
| 384 | + }); | ||
| 385 | + | ||
| 386 | + saveFeeList.add(fee); | ||
| 387 | + } | ||
| 388 | + } | ||
| 389 | + if (!feeList.isEmpty()) { | ||
| 390 | + rentalMechanicalCostMapper.insertBatch(saveFeeList); | ||
| 391 | + } | ||
| 392 | + log.info("成功插入 {} 租赁机械费用数据", dataList.size()); | ||
| 393 | + } else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { | ||
| 394 | + //获取班组对象 | ||
| 395 | + List<GardenWasteRecordImport> feeList = (List<GardenWasteRecordImport>)dataList; | ||
| 396 | + List<WasteRecordDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); | ||
| 397 | + if (!CollectionUtils.isEmpty(feeList)) { | ||
| 398 | + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds); | ||
| 399 | + for (GardenWasteRecordImport importObj : feeList) { | ||
| 400 | + WasteRecordDO fee = new WasteRecordDO(); | ||
| 401 | + BeanUtils.copyProperties(importObj, fee); | ||
| 402 | + // 设置单位id | ||
| 403 | + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); | ||
| 404 | + //设置班组id | ||
| 405 | + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); | ||
| 406 | + | ||
| 407 | + saveFeeList.add(fee); | ||
| 408 | + } | ||
| 409 | + } | ||
| 410 | + // 4. 批量插入主表(若主表插入失败,事务回滚) | ||
| 411 | + if (!feeList.isEmpty()) { | ||
| 412 | + wasteRecordMapper.insertBatch(saveFeeList); | ||
| 413 | + } | ||
| 414 | + | ||
| 415 | + log.info("成功插入 {} 园林废弃物费用数据", dataList.size()); | ||
| 416 | + } | ||
| 417 | + | ||
| 418 | + } | ||
| 419 | + | ||
| 420 | + /** | ||
| 421 | + * 从Redis获取分页数据 | ||
| 422 | + */ | ||
| 423 | + @Override | ||
| 424 | + public <T > CostFeeController.PageResult < T > getPagedDataFromRedis(String batchId, String templateName, | ||
| 425 | + int page, int size, Class<T > clazz) throws IOException { | ||
| 426 | + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 427 | + | ||
| 428 | + // 获取总记录数 | ||
| 429 | + String totalStr = redisTemplate.opsForValue().get(redisKey + ":total"); | ||
| 430 | + int total = totalStr != null ? Integer.parseInt(totalStr) : 0; | ||
| 431 | + | ||
| 432 | + // 计算分页范围 | ||
| 433 | + int start = (page - 1) * size; | ||
| 434 | + int end = Math.min(start + size - 1, total - 1); | ||
| 435 | + | ||
| 436 | + // 分页查询数据 | ||
| 437 | + List<T> pageData = new ArrayList<>(); | ||
| 438 | + if (start <= end && total > 0) { | ||
| 439 | + BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 440 | + | ||
| 441 | + // 批量获取指定范围的索引对应的数据 | ||
| 442 | + List<String> indexes = IntStream.rangeClosed(start, end) | ||
| 443 | + .mapToObj(String::valueOf) | ||
| 444 | + .collect(Collectors.toList()); | ||
| 445 | + List<String> jsonList = hashOps.multiGet(indexes); | ||
| 446 | + | ||
| 447 | + // 反序列化为对象 | ||
| 448 | + for (String json : jsonList) { | ||
| 449 | + if (json != null) { | ||
| 450 | + pageData.add(objectMapper.readValue(json, clazz)); | ||
| 451 | + } | ||
| 452 | + } | ||
| 453 | + } | ||
| 454 | + | ||
| 455 | + return new CostFeeController.PageResult<>(page, size, total, pageData); | ||
| 456 | + } | ||
| 457 | + | ||
| 458 | + | ||
| 459 | + /** | ||
| 460 | + * 从Redis获取全部数据 | ||
| 461 | + */ | ||
| 462 | + private <T > List < T > getAllDataFromRedis(String batchId, String templateName, Class < T > clazz) throws | ||
| 463 | + IOException { | ||
| 464 | + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 465 | + BoundHashOperations<String, String, String> hashOps = redisTemplate.boundHashOps(redisKey + ":items"); | ||
| 466 | + | ||
| 467 | + // 获取所有数据的JSON字符串 | ||
| 468 | + List<String> jsonList = hashOps.values(); | ||
| 469 | + List<T> dataList = new ArrayList<>(jsonList.size()); | ||
| 470 | + | ||
| 471 | + // 反序列化为对象 | ||
| 472 | + for (String json : jsonList) { | ||
| 473 | + if (json != null) { | ||
| 474 | + dataList.add(objectMapper.readValue(json, clazz)); | ||
| 475 | + } | ||
| 476 | + } | ||
| 477 | + | ||
| 478 | + return dataList; | ||
| 479 | + } | ||
| 480 | + | ||
| 481 | + | ||
| 482 | + /** | ||
| 483 | + * 删除Redis临时数据 | ||
| 484 | + */ | ||
| 485 | + private void deleteRedisTempData (String batchId, String templateName){ | ||
| 486 | + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; | ||
| 487 | + redisTemplate.delete(redisKey + ":total"); | ||
| 488 | + redisTemplate.delete(redisKey + ":items"); | ||
| 489 | + log.info("Redis临时数据已删除,批次ID:{}", batchId); | ||
| 490 | + } | ||
| 491 | + /** | ||
| 492 | + * @Author wangqian | ||
| 493 | + * @Description 导入内容校验 | ||
| 494 | + * @Date 2025/11/24 8:40 | ||
| 495 | + * @Param dataList | ||
| 496 | + * @param template | ||
| 497 | + * @Return void | ||
| 498 | + */ | ||
| 499 | + public void validateImportData(List dataList, TemplateEnum template) { | ||
| 500 | + if (CollectionUtils.isEmpty(dataList)) { | ||
| 501 | + return; // 空列表无需校验 | ||
| 502 | + } | ||
| 503 | + //登录用户单位 | ||
| 504 | + Long belongCompanyId = getCompanyId(); | ||
| 505 | + //单位集合 | ||
| 506 | + List<DeptRespDTO> companyList = deptApi.getSubDeptList(); | ||
| 507 | + List<Long> deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); | ||
| 508 | + Map<String, Long> nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) | ||
| 509 | + ? Map.of() // 空列表返回空不可变 Map(避免 null) | ||
| 510 | + : companyList.stream() | ||
| 511 | + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) | ||
| 512 | + .collect(Collectors.toMap( | ||
| 513 | + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) | ||
| 514 | + DeptRespDTO::getId, // key 映射:取 companyId | ||
| 515 | + (oldValue, newValue) -> newValue | ||
| 516 | + )); | ||
| 517 | + //班组集合 | ||
| 518 | + Map<Long, List<String>> deptList = this.getDetpList(deptIds); | ||
| 519 | + // 模板类型校验 | ||
| 520 | + Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch | ||
| 521 | + // 根据不同模板类型执行不同的处理逻辑 | ||
| 522 | + if (WaterFeeImport.class.isAssignableFrom(importClass)) { | ||
| 523 | + | ||
| 524 | + List<WaterFeeImport> importList = (List<WaterFeeImport>) dataList; | ||
| 525 | + | ||
| 526 | + // 2. 遍历,逐个创建 | ||
| 527 | + for (int i = 0; i < importList.size(); i++) { | ||
| 528 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 529 | + WaterFeeImport importBean = importList.get(i); | ||
| 530 | + | ||
| 531 | + try { | ||
| 532 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 533 | + Set<ConstraintViolation<WaterFeeImport>> violations = validator.validate(importBean); | ||
| 534 | + if (!violations.isEmpty()) { | ||
| 535 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 536 | + // StringBuilder errorMsg = new StringBuilder(); | ||
| 537 | + for (ConstraintViolation<WaterFeeImport> violation : violations) { | ||
| 538 | + //errorMsg.append(violation.getMessage()).append(";"); | ||
| 539 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 540 | + "第[{}]条数据:"+ violation.getMessage() | ||
| 541 | + ), rowNum); | ||
| 542 | + } | ||
| 543 | + // 抛出包含行号和所有错误的异常 | ||
| 544 | + //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 545 | + } | ||
| 546 | + | ||
| 547 | + } catch (ConstraintViolationException ex) { | ||
| 548 | + // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 549 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 550 | + } | ||
| 551 | + } | ||
| 552 | + } | ||
| 553 | + else if (PersonalCostImport.class.isAssignableFrom(importClass)) { | ||
| 554 | + | ||
| 555 | + List<PersonalCostImport> importList = (List<PersonalCostImport>) dataList; | ||
| 556 | + // 2. 遍历,逐个创建 | ||
| 557 | + for (int i = 0; i < importList.size(); i++) { | ||
| 558 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 559 | + PersonalCostImport importBean = importList.get(i); | ||
| 560 | + | ||
| 561 | + try { | ||
| 562 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 563 | + Set<ConstraintViolation<PersonalCostImport>> violations = validator.validate(importBean); | ||
| 564 | + if (!violations.isEmpty()) { | ||
| 565 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 566 | + //StringBuilder errorMsg = new StringBuilder(); | ||
| 567 | + for (ConstraintViolation<PersonalCostImport> violation : violations) { | ||
| 568 | + // errorMsg.append(violation.getMessage()).append(";"); | ||
| 569 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 570 | + "第[{}]条数据:"+ violation.getMessage() | ||
| 571 | + ), rowNum); | ||
| 572 | + } | ||
| 573 | + // 抛出包含行号和所有错误的异常 | ||
| 574 | + // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 575 | + } | ||
| 576 | + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 577 | + //单位校验 | ||
| 578 | + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 579 | + //部门名称校验 | ||
| 580 | + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 581 | + | ||
| 582 | + } catch (ConstraintViolationException ex) { | ||
| 583 | + // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 584 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 585 | + } | ||
| 586 | + } | ||
| 587 | + } | ||
| 588 | + else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { | ||
| 589 | + | ||
| 590 | + List<GardenWasteRecordImport> importList = (List<GardenWasteRecordImport>) dataList; | ||
| 591 | + // 2. 遍历,逐个创建 | ||
| 592 | + for (int i = 0; i < importList.size(); i++) { | ||
| 593 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 594 | + GardenWasteRecordImport importBean = importList.get(i); | ||
| 595 | + | ||
| 596 | + try { | ||
| 597 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 598 | + Set<ConstraintViolation<GardenWasteRecordImport>> violations = validator.validate(importBean); | ||
| 599 | + if (!violations.isEmpty()) { | ||
| 600 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 601 | + //StringBuilder errorMsg = new StringBuilder(); | ||
| 602 | + for (ConstraintViolation<GardenWasteRecordImport> violation : violations) { | ||
| 603 | + //errorMsg.append(violation.getMessage()).append(";"); | ||
| 604 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 605 | + "第[{}]条数据:"+ violation.getMessage() | ||
| 606 | + ), rowNum); | ||
| 607 | + } | ||
| 608 | + // 抛出包含行号和所有错误的异常 | ||
| 609 | + //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); | ||
| 610 | + } | ||
| 611 | + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 612 | + //单位校验 | ||
| 613 | + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 614 | + //部门名称校验 | ||
| 615 | + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 616 | + | ||
| 617 | + } catch (ConstraintViolationException ex) { | ||
| 618 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 619 | + } | ||
| 620 | + } | ||
| 621 | + } | ||
| 622 | + else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 623 | + | ||
| 624 | + List<MechanicalCostImport> importList = (List<MechanicalCostImport>) dataList; | ||
| 625 | + // 2. 遍历,逐个创建 | ||
| 626 | + for (int i = 0; i < importList.size(); i++) { | ||
| 627 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 628 | + MechanicalCostImport importBean = importList.get(i); | ||
| 629 | + | ||
| 630 | + try { | ||
| 631 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 632 | + Set<ConstraintViolation<MechanicalCostImport>> violations = validator.validate(importBean); | ||
| 633 | + if (!violations.isEmpty()) { | ||
| 634 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 635 | + //StringBuilder errorMsg = new StringBuilder(); | ||
| 636 | + for (ConstraintViolation<MechanicalCostImport> violation : violations) { | ||
| 637 | + //errorMsg.append(violation.getMessage()).append(";"); | ||
| 638 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 639 | + "第[{}]条数据:"+ violation.getMessage() | ||
| 640 | + ), rowNum); | ||
| 641 | + } | ||
| 642 | + // 抛出包含行号和所有错误的异常 | ||
| 643 | + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 644 | + } | ||
| 645 | + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 646 | + //单位校验 | ||
| 647 | + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 648 | + //部门名称校验 | ||
| 649 | + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 650 | + | ||
| 651 | + //车辆校验 | ||
| 652 | + if (CostFeeConstants.MECH_TYPE.equals(importBean.getMechType())) { | ||
| 653 | + if (StringUtils.isEmpty(importBean.getLicensePlate())) { | ||
| 654 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 655 | + "第[{}]条数据:车牌号不能为空" | ||
| 656 | + ), rowNum); | ||
| 657 | + } | ||
| 658 | + if (StringUtils.isEmpty(importBean.getVehicleType())) { | ||
| 659 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 660 | + "第[{}]条数据:车辆类型不能为空" | ||
| 661 | + ), rowNum); | ||
| 662 | + | ||
| 663 | + } | ||
| 664 | + if (StringUtils.isEmpty(importBean.getVehicleCategory())) { | ||
| 665 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 666 | + "第%d条数据:车辆类别不能为空" | ||
| 667 | + ), rowNum); | ||
| 668 | + } | ||
| 669 | + } | ||
| 670 | + | ||
| 671 | + } catch (ConstraintViolationException ex) { | ||
| 672 | + // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 673 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 674 | + } | ||
| 675 | + } | ||
| 676 | + } | ||
| 677 | + else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { | ||
| 678 | + | ||
| 679 | + List<RentalMechanicalCostImport> importList = (List<RentalMechanicalCostImport>) dataList; | ||
| 680 | + // 2. 遍历,逐个创建 | ||
| 681 | + for (int i = 0; i < importList.size(); i++) { | ||
| 682 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 683 | + RentalMechanicalCostImport importBean = importList.get(i); | ||
| 684 | + | ||
| 685 | + try { | ||
| 686 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 687 | + Set<ConstraintViolation<RentalMechanicalCostImport>> violations = validator.validate(importBean); | ||
| 688 | + if (!violations.isEmpty()) { | ||
| 689 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 690 | + // StringBuilder errorMsg = new StringBuilder(); | ||
| 691 | + for (ConstraintViolation<RentalMechanicalCostImport> violation : violations) { | ||
| 692 | + // errorMsg.append(violation.getMessage()).append(";"); | ||
| 693 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 694 | + "第[{}]条数据:"+violation.getMessage() | ||
| 695 | + ), rowNum); | ||
| 696 | + } | ||
| 697 | + // 抛出包含行号和所有错误的异常 | ||
| 698 | + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 699 | + } | ||
| 700 | + // 处理可能的空值 | ||
| 701 | + String teamIdsStr = StringUtils.defaultString(importBean.getTeamIds()); | ||
| 702 | + String weightsStr = StringUtils.defaultString(importBean.getWeight()); | ||
| 703 | + | ||
| 704 | + // 统一替换中文逗号并分割 | ||
| 705 | + String[] teamIds = teamIdsStr.replace(",", ",").split(","); | ||
| 706 | + String[] weights = weightsStr.replace(",", ",").split(","); | ||
| 707 | + | ||
| 708 | + // 校验数组长度是否一致 | ||
| 709 | + if (teamIds.length != weights.length) { | ||
| 710 | + throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 711 | + "第%d条数据:权重值和所选班组数量不符(班组:%d个,权重:%d个)", | ||
| 712 | + rowNum, teamIds.length, weights.length | ||
| 713 | + )); | ||
| 714 | + } | ||
| 715 | + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 716 | + //单位校验 | ||
| 717 | + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 718 | + //部门名称校验 | ||
| 719 | + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); | ||
| 720 | + | ||
| 721 | + } catch (ConstraintViolationException ex) { | ||
| 722 | + // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 723 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 724 | + } | ||
| 725 | + } | ||
| 726 | + } | ||
| 727 | + else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { | ||
| 728 | + | ||
| 729 | + List<DepartmentCostImport> importList = (List<DepartmentCostImport>) dataList; | ||
| 730 | + // 2. 遍历,逐个创建 | ||
| 731 | + for (int i = 0; i < importList.size(); i++) { | ||
| 732 | + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) | ||
| 733 | + DepartmentCostImport importBean = importList.get(i); | ||
| 734 | + | ||
| 735 | + try { | ||
| 736 | + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) | ||
| 737 | + Set<ConstraintViolation<DepartmentCostImport>> violations = validator.validate(importBean); | ||
| 738 | + if (!violations.isEmpty()) { | ||
| 739 | + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) | ||
| 740 | + //StringBuilder errorMsg = new StringBuilder(); | ||
| 741 | + for (ConstraintViolation<DepartmentCostImport> violation : violations) { | ||
| 742 | + //errorMsg.append(violation.getMessage()).append(";"); | ||
| 743 | + throw exception0(BAD_REQUEST.getCode() ,String.format( | ||
| 744 | + "第[{}]条数据:"+violation.getMessage() | ||
| 745 | + ), rowNum); | ||
| 746 | + } | ||
| 747 | + // 抛出包含行号和所有错误的异常 | ||
| 748 | + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); | ||
| 749 | + } | ||
| 750 | + | ||
| 751 | + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); | ||
| 752 | + //单位校验 | ||
| 753 | + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); | ||
| 754 | + } catch (ConstraintViolationException ex) { | ||
| 755 | + // 5. 包装异常,明确行号 + 真实校验错误( | ||
| 756 | + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); | ||
| 757 | + } | ||
| 758 | + } | ||
| 759 | + } | ||
| 760 | + } | ||
| 761 | + | ||
| 762 | + private void validatecompany(int rowNum, String companyName, Long companyId, Long belongCompanyId){ | ||
| 763 | + // 校验数组长度是否一致 | ||
| 764 | + if (companyId == null) { | ||
| 765 | + throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 766 | + "第%d条数据:单位名称不正确(%s)", | ||
| 767 | + rowNum, companyName | ||
| 768 | + )); | ||
| 769 | + } | ||
| 770 | + // 校验导入单位 | ||
| 771 | + if (belongCompanyId != 100 && !belongCompanyId.equals(companyId)) { | ||
| 772 | + throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 773 | + "第%d条数据:导入单位%s与当前操作员单位不一致", | ||
| 774 | + rowNum, companyName | ||
| 775 | + )); | ||
| 776 | + } | ||
| 777 | + } | ||
| 778 | + private void validateTeams(int rowNum, List<String> dept, String teamIds) { | ||
| 779 | + if (StringUtils.isNotEmpty(teamIds)) { | ||
| 780 | + String[] teamNames = teamIds.split(","); | ||
| 781 | + Set<String> checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复 | ||
| 782 | + | ||
| 783 | + for (String teamName : teamNames) { | ||
| 784 | + String trimmedTeamName = teamName.trim(); // 处理前后空格 | ||
| 785 | + | ||
| 786 | + // 重复校验:如果Set中已存在该团队名称,说明重复 | ||
| 787 | + if (checkedTeams.contains(trimmedTeamName)) { | ||
| 788 | + throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 789 | + "第%d条数据:%s名称重复", | ||
| 790 | + rowNum, teamName | ||
| 791 | + )); | ||
| 792 | + } | ||
| 793 | + | ||
| 794 | + // 存在性校验:检查团队是否在dept中存在 | ||
| 795 | + if (!dept.contains(trimmedTeamName)) { | ||
| 796 | + throw exception0(BAD_REQUEST.getCode(), String.format( | ||
| 797 | + "第%d条数据:%s名称不正确", | ||
| 798 | + rowNum, teamName | ||
| 799 | + )); | ||
| 800 | + } | ||
| 801 | + | ||
| 802 | + // 将校验通过的团队名称加入Set,用于后续重复判断 | ||
| 803 | + checkedTeams.add(trimmedTeamName); | ||
| 804 | + } | ||
| 805 | + } | ||
| 806 | + } | ||
| 807 | + | ||
| 808 | + //查询单位下的班组信息 | ||
| 809 | + private Map<Long,List<String>> getDetpList (List<Long> deptIds) { | ||
| 810 | + Map<Long,List<String>> listMap = new HashMap<>(); | ||
| 811 | + for (Long depId : deptIds) { | ||
| 812 | + List<DeptRespDTO> deptList = deptApi.getChildDeptList(depId); | ||
| 813 | + List<String> names = deptList.stream().map(DeptRespDTO::getName).collect(Collectors.toList()); | ||
| 814 | + listMap.put(depId,names); | ||
| 815 | + } | ||
| 816 | + return listMap; | ||
| 817 | + } | ||
| 818 | + | ||
| 819 | + /** | ||
| 820 | + * 按salaryMonth、expenseName、companyId三个字段去重 | ||
| 821 | + * @param costList 原始部门成本列表 | ||
| 822 | + * @return 去重后的列表 | ||
| 823 | + */ | ||
| 824 | + public List<DepartmentCostImport> deptFeeReplacement(List<DepartmentCostImport> costList) { | ||
| 825 | + if (costList == null || costList.isEmpty()) { | ||
| 826 | + return new ArrayList<>(); | ||
| 827 | + } | ||
| 828 | + | ||
| 829 | + // 1. 以三个字段的组合作为键(使用分隔符避免不同组合产生相同键) | ||
| 830 | + // 2. 遇到重复键时保留第一条记录 | ||
| 831 | + Map<String, DepartmentCostImport> uniqueMap = costList.stream() | ||
| 832 | + .collect(Collectors.toMap( | ||
| 833 | + // 生成唯一键:salaryMonth|expenseName|companyId | ||
| 834 | + cost -> buildUniqueKey(cost), | ||
| 835 | + // 键不存在时存入当前对象 | ||
| 836 | + cost -> cost, | ||
| 837 | + // 键已存在时保留最后一个 | ||
| 838 | + (existing, replacement) -> replacement | ||
| 839 | + )); | ||
| 840 | + | ||
| 841 | + // 将去重后的结果转为List返回 | ||
| 842 | + return new ArrayList<>(uniqueMap.values()); | ||
| 843 | + } | ||
| 844 | + | ||
| 845 | + /** | ||
| 846 | + * 构建三个字段的唯一组合键(处理null值) | ||
| 847 | + */ | ||
| 848 | + private String buildUniqueKey(DepartmentCostImport cost) { | ||
| 849 | + // 使用Objects.toString处理可能的null值,避免NullPointerException | ||
| 850 | + return Objects.toString(cost.getSalaryMonth(), "") + "|" + | ||
| 851 | + Objects.toString(cost.getExpenseName(), "") + "|" + | ||
| 852 | + Objects.toString(cost.getCompanyName(), ""); | ||
| 853 | + } | ||
| 854 | + | ||
| 855 | + /** | ||
| 856 | + * 按salaryMonth、name、companyId、teamIdList四个字段去重 | ||
| 857 | + * @param costList 原始个人成本列表 | ||
| 858 | + * @return 去重后的列表 | ||
| 859 | + */ | ||
| 860 | + public List<PersonalCostImport> personalFeeReplacement(List<PersonalCostImport> costList) { | ||
| 861 | + if (costList == null || costList.isEmpty()) { | ||
| 862 | + return new ArrayList<>(); | ||
| 863 | + } | ||
| 864 | + | ||
| 865 | + // 1. 以四个字段的组合作为键(特别处理集合类型的teamIdList) | ||
| 866 | + // 2. 遇到重复键时保留第一条记录 | ||
| 867 | + Map<String, PersonalCostImport> uniqueMap = costList.stream() | ||
| 868 | + .collect(Collectors.toMap( | ||
| 869 | + // 生成唯一键:salaryMonth|name|companyId|teamIdListStr | ||
| 870 | + this::buildUniqueKey, | ||
| 871 | + // 键不存在时存入当前对象 | ||
| 872 | + cost -> cost, | ||
| 873 | + // 键已存在时保留第一个对象(可改为replacement保留最后一个) | ||
| 874 | + (existing, replacement) -> replacement | ||
| 875 | + )); | ||
| 876 | + | ||
| 877 | + // 将去重后的结果转为List返回 | ||
| 878 | + return new ArrayList<>(uniqueMap.values()); | ||
| 879 | + } | ||
| 880 | + | ||
| 881 | + /** | ||
| 882 | + * 构建四个字段的唯一组合键(处理null值和集合类型) | ||
| 883 | + */ | ||
| 884 | + private String buildUniqueKey(PersonalCostImport cost) { | ||
| 885 | + // 处理普通字段的null值 | ||
| 886 | + String salaryMonth = Objects.toString(cost.getSalaryMonth(), ""); | ||
| 887 | + String name = Objects.toString(cost.getName(), ""); | ||
| 888 | + String companyId = Objects.toString(cost.getCompanyName(), ""); | ||
| 889 | + String teamIds = Objects.toString(cost.getTeamIds(), ""); | ||
| 890 | + // 拼接唯一键 | ||
| 891 | + return salaryMonth + "|" + name + "|" + companyId + "|" + teamIds; | ||
| 892 | + } | ||
| 893 | + | ||
| 894 | + | ||
| 895 | + public static LocalDate getEndLocalDateForYears(LocalDate startDate, int years) { | ||
| 896 | + if (startDate == null) { | ||
| 897 | + throw new IllegalArgumentException("开始日期不能为null"); | ||
| 898 | + } | ||
| 899 | + if (years < 0) { | ||
| 900 | + throw new IllegalArgumentException("年数不能为负数"); | ||
| 901 | + } | ||
| 902 | + return startDate.plusYears(years); | ||
| 903 | + } | ||
| 904 | + | ||
| 905 | + /** | ||
| 906 | + * @Author wangqian | ||
| 907 | + * @Description 根据班组名称获取班组id | ||
| 908 | + * @Date 2025/11/26 19:38 | ||
| 909 | + */ | ||
| 910 | + /** | ||
| 911 | + * 根据部门ID列表获取「部门名称→部门ID」的映射(含空值防护) | ||
| 912 | + */ | ||
| 913 | + private Map<String, Long> getDeptListAll(List<Long> deptIds) { | ||
| 914 | + Map<String, Long> deptNameMap = new HashMap<>(); | ||
| 915 | + | ||
| 916 | + // 1. 空值安全:避免 deptIds 为 null/空列表导致 NPE | ||
| 917 | + if (CollectionUtils.isEmpty(deptIds)) { | ||
| 918 | + return deptNameMap; | ||
| 919 | + } | ||
| 920 | + | ||
| 921 | + for (Long deptId : deptIds) { | ||
| 922 | + // 2. 跳过 null 的部门ID | ||
| 923 | + if (deptId == null) { | ||
| 924 | + continue; | ||
| 925 | + } | ||
| 926 | + | ||
| 927 | + // 3. 获取子部门列表(防护接口返回 null) | ||
| 928 | + List<DeptRespDTO> deptList = deptApi.getChildDeptList(deptId); | ||
| 929 | + if (CollectionUtils.isEmpty(deptList)) { | ||
| 930 | + continue; | ||
| 931 | + } | ||
| 932 | + | ||
| 933 | + // 4. 遍历子部门:过滤无效名称,避免 Map 出现 null/空字符串键 | ||
| 934 | + for (DeptRespDTO dept : deptList) { | ||
| 935 | + if (dept == null) { | ||
| 936 | + continue; | ||
| 937 | + } | ||
| 938 | + String deptName = dept.getName(); | ||
| 939 | + // 过滤 null/空白名称(避免 Map 键为 null 或空字符串) | ||
| 940 | + if (deptName == null || deptName.trim().isEmpty()) { | ||
| 941 | + continue; | ||
| 942 | + } | ||
| 943 | + // 若名称重复,后遍历的部门会覆盖先遍历的(可根据业务调整规则) | ||
| 944 | + deptNameMap.put(deptName.trim(), dept.getId()); | ||
| 945 | + } | ||
| 946 | + } | ||
| 947 | + | ||
| 948 | + return deptNameMap; | ||
| 949 | + } | ||
| 950 | + /** | ||
| 951 | + * @Author wangqian | ||
| 952 | + * @Description 根据班组名称获取班组id集合 | ||
| 953 | + * @Date 2025/11/26 20:07 | ||
| 954 | + */ | ||
| 955 | + private List<Long> getDeptList(Map<String, Long> teamIdMap, String teamId) { | ||
| 956 | + List<Long> list = new ArrayList<>(); | ||
| 957 | + | ||
| 958 | + // 1. 处理 teamId 为空的情况,直接返回空列表 | ||
| 959 | + if (StringUtils.isEmpty(teamId)) { | ||
| 960 | + return list; | ||
| 961 | + } | ||
| 962 | + | ||
| 963 | + // 2. 分割字符串,处理空元素 | ||
| 964 | + String[] teamArray = teamId.split(","); | ||
| 965 | + for (String teamName : teamArray) { | ||
| 966 | + // 3. 去除首尾空格,避免因空格导致匹配不到 | ||
| 967 | + String trimTeamName = teamName.trim(); | ||
| 968 | + if (StringUtils.isEmpty(trimTeamName)) { | ||
| 969 | + continue; // 跳过空字符串元素 | ||
| 970 | + } | ||
| 971 | + | ||
| 972 | + // 4. 从Map中获取ID,处理null值(避免添加null到列表) | ||
| 973 | + Long deptId = teamIdMap.get(trimTeamName); | ||
| 974 | + if (deptId != null) { | ||
| 975 | + list.add(deptId); // 直接添加Long类型,修正原String类型错误 | ||
| 976 | + } | ||
| 977 | + } | ||
| 978 | + | ||
| 979 | + return list; | ||
| 980 | + } | ||
| 981 | +} |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventory/MaterialInventoryServiceImpl.java
| @@ -10,6 +10,8 @@ import com.zteits.urbanops.module.garden.controller.admin.materialinventory.vo.M | @@ -10,6 +10,8 @@ import com.zteits.urbanops.module.garden.controller.admin.materialinventory.vo.M | ||
| 10 | import com.zteits.urbanops.module.garden.controller.admin.materialinventory.vo.MaterialInventorySaveReqVO; | 10 | import com.zteits.urbanops.module.garden.controller.admin.materialinventory.vo.MaterialInventorySaveReqVO; |
| 11 | import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; | 11 | import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; |
| 12 | import com.zteits.urbanops.module.garden.dal.mysql.materialinventory.MaterialInventoryMapper; | 12 | import com.zteits.urbanops.module.garden.dal.mysql.materialinventory.MaterialInventoryMapper; |
| 13 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 14 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 13 | import jakarta.annotation.Resource; | 15 | import jakarta.annotation.Resource; |
| 14 | import org.springframework.stereotype.Service; | 16 | import org.springframework.stereotype.Service; |
| 15 | import org.springframework.validation.annotation.Validated; | 17 | import org.springframework.validation.annotation.Validated; |
| @@ -19,6 +21,7 @@ import java.util.List; | @@ -19,6 +21,7 @@ import java.util.List; | ||
| 19 | import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | 21 | import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; |
| 20 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | 22 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 21 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | 23 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; |
| 24 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; | ||
| 22 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; | 25 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; |
| 23 | 26 | ||
| 24 | /** | 27 | /** |
| @@ -33,10 +36,19 @@ public class MaterialInventoryServiceImpl implements MaterialInventoryService { | @@ -33,10 +36,19 @@ public class MaterialInventoryServiceImpl implements MaterialInventoryService { | ||
| 33 | @Resource | 36 | @Resource |
| 34 | private MaterialInventoryMapper materialInventoryMapper; | 37 | private MaterialInventoryMapper materialInventoryMapper; |
| 35 | 38 | ||
| 39 | + @Resource | ||
| 40 | + private DeptApi deptApi; | ||
| 41 | + | ||
| 36 | @Override | 42 | @Override |
| 37 | public Long createMaterialInventory(MaterialInventorySaveReqVO createReqVO) { | 43 | public Long createMaterialInventory(MaterialInventorySaveReqVO createReqVO) { |
| 38 | // 插入 | 44 | // 插入 |
| 39 | MaterialInventoryDO materialInventory = BeanUtils.toBean(createReqVO, MaterialInventoryDO.class); | 45 | MaterialInventoryDO materialInventory = BeanUtils.toBean(createReqVO, MaterialInventoryDO.class); |
| 46 | + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); | ||
| 47 | + if(null == deptRespDTO){ | ||
| 48 | + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); | ||
| 49 | + } | ||
| 50 | + materialInventory.setBelongCompanyId(deptRespDTO.getId()); | ||
| 51 | + materialInventory.setBelongCompanyName(deptRespDTO.getName()); | ||
| 40 | materialInventoryMapper.insert(materialInventory); | 52 | materialInventoryMapper.insert(materialInventory); |
| 41 | 53 | ||
| 42 | // 返回 | 54 | // 返回 |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryin/MaterialInventoryInServiceImpl.java
| @@ -6,6 +6,8 @@ import com.zteits.urbanops.module.garden.dal.dataobject.material.MaterialDO; | @@ -6,6 +6,8 @@ import com.zteits.urbanops.module.garden.dal.dataobject.material.MaterialDO; | ||
| 6 | import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; | 6 | import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; |
| 7 | import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; | 7 | import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; |
| 8 | import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; | 8 | import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; |
| 9 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 10 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 9 | import org.springframework.stereotype.Service; | 11 | import org.springframework.stereotype.Service; |
| 10 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 11 | import org.springframework.validation.annotation.Validated; | 13 | import org.springframework.validation.annotation.Validated; |
| @@ -25,8 +27,7 @@ import static com.zteits.urbanops.framework.common.exception.util.ServiceExcepti | @@ -25,8 +27,7 @@ import static com.zteits.urbanops.framework.common.exception.util.ServiceExcepti | ||
| 25 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | 27 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; |
| 26 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertList; | 28 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertList; |
| 27 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.diffList; | 29 | import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.diffList; |
| 28 | -import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; | ||
| 29 | -import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getLoginUserDeptId; | 30 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; |
| 30 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; | 31 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; |
| 31 | 32 | ||
| 32 | /** | 33 | /** |
| @@ -47,6 +48,9 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | @@ -47,6 +48,9 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | ||
| 47 | @Resource | 48 | @Resource |
| 48 | private MaterialInventoryService materialInventoryService; | 49 | private MaterialInventoryService materialInventoryService; |
| 49 | 50 | ||
| 51 | + @Resource | ||
| 52 | + private DeptApi deptApi; | ||
| 53 | + | ||
| 50 | @Override | 54 | @Override |
| 51 | @Transactional | 55 | @Transactional |
| 52 | public Long createMaterialInventoryIn(MaterialInventoryInSaveReqVO createReqVO) { | 56 | public Long createMaterialInventoryIn(MaterialInventoryInSaveReqVO createReqVO) { |
| @@ -55,6 +59,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | @@ -55,6 +59,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | ||
| 55 | if(null == material){ | 59 | if(null == material){ |
| 56 | throw exception0(BAD_REQUEST.getCode(), "无效参数-materialId"); | 60 | throw exception0(BAD_REQUEST.getCode(), "无效参数-materialId"); |
| 57 | } | 61 | } |
| 62 | + DeptRespDTO deptRespDTO= deptApi.getDept(getCompanyId()); | ||
| 63 | + if(null == deptRespDTO){ | ||
| 64 | + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); | ||
| 65 | + } | ||
| 58 | MaterialInventoryInDO in = new MaterialInventoryInDO(); | 66 | MaterialInventoryInDO in = new MaterialInventoryInDO(); |
| 59 | in.setInventoryInNo(System.currentTimeMillis()+ RandomUtil.randomInt(10, 100000)+""); | 67 | in.setInventoryInNo(System.currentTimeMillis()+ RandomUtil.randomInt(10, 100000)+""); |
| 60 | in.setMaterialId(createReqVO.getMaterialId()); | 68 | in.setMaterialId(createReqVO.getMaterialId()); |
| @@ -66,8 +74,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | @@ -66,8 +74,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic | ||
| 66 | in.setTypeDetailId(material.getTypeDetailId()); | 74 | in.setTypeDetailId(material.getTypeDetailId()); |
| 67 | in.setTypeDetailName(material.getTypeDetailName()); | 75 | in.setTypeDetailName(material.getTypeDetailName()); |
| 68 | in.setSpecifications(material.getSpecifications()); | 76 | in.setSpecifications(material.getSpecifications()); |
| 69 | - in.setBelongCompanyId(material.getBelongCompanyId()); | ||
| 70 | - in.setBelongCompanyName(material.getBelongCompanyName()); | 77 | +// in.setBelongCompanyId(material.getBelongCompanyId()); |
| 78 | +// in.setBelongCompanyName(material.getBelongCompanyName()); | ||
| 79 | + in.setBelongCompanyId(deptRespDTO.getId()); | ||
| 80 | + in.setBelongCompanyName(deptRespDTO.getName()); | ||
| 71 | in.setInInventoryNum(createReqVO.getInInventoryNum()); | 81 | in.setInInventoryNum(createReqVO.getInInventoryNum()); |
| 72 | in.setMaterialPrice(createReqVO.getMaterialPrice()); | 82 | in.setMaterialPrice(createReqVO.getMaterialPrice()); |
| 73 | in.setUnitId(material.getUnitId()); | 83 | in.setUnitId(material.getUnitId()); |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryout/MaterialInventoryOutServiceImpl.java
| @@ -10,6 +10,8 @@ import com.zteits.urbanops.module.garden.dal.dataobject.materialinventoryout.Mat | @@ -10,6 +10,8 @@ import com.zteits.urbanops.module.garden.dal.dataobject.materialinventoryout.Mat | ||
| 10 | import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; | 10 | import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; |
| 11 | import com.zteits.urbanops.module.garden.dal.mysql.materialinventoryout.MaterialInventoryOutMapper; | 11 | import com.zteits.urbanops.module.garden.dal.mysql.materialinventoryout.MaterialInventoryOutMapper; |
| 12 | import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; | 12 | import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; |
| 13 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 14 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 13 | import jakarta.annotation.Resource; | 15 | import jakarta.annotation.Resource; |
| 14 | import org.springframework.stereotype.Service; | 16 | import org.springframework.stereotype.Service; |
| 15 | import org.springframework.validation.annotation.Validated; | 17 | import org.springframework.validation.annotation.Validated; |
| @@ -19,6 +21,7 @@ import java.util.List; | @@ -19,6 +21,7 @@ import java.util.List; | ||
| 19 | import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | 21 | import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; |
| 20 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | 22 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 21 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | 23 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; |
| 24 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; | ||
| 22 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; | 25 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; |
| 23 | 26 | ||
| 24 | /** | 27 | /** |
| @@ -37,6 +40,9 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | @@ -37,6 +40,9 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | ||
| 37 | 40 | ||
| 38 | @Resource | 41 | @Resource |
| 39 | private MaterialInventoryService materialInventoryService; | 42 | private MaterialInventoryService materialInventoryService; |
| 43 | + | ||
| 44 | + @Resource | ||
| 45 | + private DeptApi deptApi; | ||
| 40 | @Override | 46 | @Override |
| 41 | public Long createMaterialInventoryOut(MaterialInventoryOutSaveReqVO createReqVO) { | 47 | public Long createMaterialInventoryOut(MaterialInventoryOutSaveReqVO createReqVO) { |
| 42 | // 插入 | 48 | // 插入 |
| @@ -54,7 +60,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | @@ -54,7 +60,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | ||
| 54 | if(materialInventory.getCurrentNum() < out.getOutInventoryNum()){ | 60 | if(materialInventory.getCurrentNum() < out.getOutInventoryNum()){ |
| 55 | throw exception0(BAD_REQUEST.getCode(),"当前库存不足,无法出库"); | 61 | throw exception0(BAD_REQUEST.getCode(),"当前库存不足,无法出库"); |
| 56 | } | 62 | } |
| 57 | - | 63 | + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); |
| 64 | + if(null == deptRespDTO){ | ||
| 65 | + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); | ||
| 66 | + } | ||
| 58 | out.setMaterialName(material.getMaterialName()); | 67 | out.setMaterialName(material.getMaterialName()); |
| 59 | out.setMaterialTypeId(material.getMaterialTypeId()); | 68 | out.setMaterialTypeId(material.getMaterialTypeId()); |
| 60 | out.setMaterialTypeName(material.getMaterialTypeName()); | 69 | out.setMaterialTypeName(material.getMaterialTypeName()); |
| @@ -63,8 +72,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | @@ -63,8 +72,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ | ||
| 63 | out.setTypeDetailId(material.getTypeDetailId()); | 72 | out.setTypeDetailId(material.getTypeDetailId()); |
| 64 | out.setTypeDetailName(material.getTypeDetailName()); | 73 | out.setTypeDetailName(material.getTypeDetailName()); |
| 65 | out.setSpecifications(material.getSpecifications()); | 74 | out.setSpecifications(material.getSpecifications()); |
| 66 | - out.setBelongCompanyId(material.getBelongCompanyId()); | ||
| 67 | - out.setBelongCompanyName(material.getBelongCompanyName()); | 75 | +// out.setBelongCompanyId(material.getBelongCompanyId()); |
| 76 | +// out.setBelongCompanyName(material.getBelongCompanyName()); | ||
| 77 | + out.setBelongCompanyId(deptRespDTO.getId()); | ||
| 78 | + out.setBelongCompanyName(deptRespDTO.getName()); | ||
| 68 | out.setUnitId(material.getUnitId()); | 79 | out.setUnitId(material.getUnitId()); |
| 69 | out.setUnitName(material.getUnitName()); | 80 | out.setUnitName(material.getUnitName()); |
| 70 | materialInventoryOutMapper.insert(out); | 81 | materialInventoryOutMapper.insert(out); |
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/wasterecord/WasteRecordServiceImpl.java
| @@ -7,6 +7,8 @@ import com.zteits.urbanops.module.garden.controller.admin.wasterecord.vo.WasteRe | @@ -7,6 +7,8 @@ import com.zteits.urbanops.module.garden.controller.admin.wasterecord.vo.WasteRe | ||
| 7 | import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.TeamBizdomainRelVo; | 7 | import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.TeamBizdomainRelVo; |
| 8 | import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; | 8 | import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; |
| 9 | import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; | 9 | import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; |
| 10 | +import com.zteits.urbanops.module.system.api.dept.DeptApi; | ||
| 11 | +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; | ||
| 10 | import jakarta.annotation.Resource; | 12 | import jakarta.annotation.Resource; |
| 11 | import org.springframework.stereotype.Service; | 13 | import org.springframework.stereotype.Service; |
| 12 | import org.springframework.transaction.annotation.Transactional; | 14 | import org.springframework.transaction.annotation.Transactional; |
| @@ -15,7 +17,10 @@ import org.springframework.validation.annotation.Validated; | @@ -15,7 +17,10 @@ import org.springframework.validation.annotation.Validated; | ||
| 15 | import java.util.List; | 17 | import java.util.List; |
| 16 | import java.util.stream.Collectors; | 18 | import java.util.stream.Collectors; |
| 17 | 19 | ||
| 20 | +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; | ||
| 18 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; | 21 | import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 22 | +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; | ||
| 23 | +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; | ||
| 19 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.WASTE_RECORD_NOT_EXISTS; | 24 | import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.WASTE_RECORD_NOT_EXISTS; |
| 20 | 25 | ||
| 21 | /** | 26 | /** |
| @@ -30,11 +35,19 @@ public class WasteRecordServiceImpl implements WasteRecordService { | @@ -30,11 +35,19 @@ public class WasteRecordServiceImpl implements WasteRecordService { | ||
| 30 | @Resource | 35 | @Resource |
| 31 | private WasteRecordMapper wasteRecordMapper; | 36 | private WasteRecordMapper wasteRecordMapper; |
| 32 | 37 | ||
| 38 | + @Resource | ||
| 39 | + private DeptApi deptApi; | ||
| 33 | @Override | 40 | @Override |
| 34 | @Transactional | 41 | @Transactional |
| 35 | public Long createWasteRecord(WasteRecordSaveReqVO createReqVO) { | 42 | public Long createWasteRecord(WasteRecordSaveReqVO createReqVO) { |
| 43 | + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); | ||
| 44 | + if(null == deptRespDTO){ | ||
| 45 | + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); | ||
| 46 | + } | ||
| 36 | // 插入 | 47 | // 插入 |
| 37 | WasteRecordDO wasteRecord = BeanUtils.toBean(createReqVO, WasteRecordDO.class); | 48 | WasteRecordDO wasteRecord = BeanUtils.toBean(createReqVO, WasteRecordDO.class); |
| 49 | + wasteRecord.setCompanyId(String.valueOf(deptRespDTO.getId())); | ||
| 50 | + wasteRecord.setCompanyName(deptRespDTO.getName()); | ||
| 38 | List<Long> deptIds = createReqVO.getTeamIds().stream() | 51 | List<Long> deptIds = createReqVO.getTeamIds().stream() |
| 39 | .map(TeamBizdomainRelVo::getTeamId) | 52 | .map(TeamBizdomainRelVo::getTeamId) |
| 40 | .collect(Collectors.toList()); | 53 | .collect(Collectors.toList()); |