diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java index 24d7c31..c4ef24d 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java @@ -1,378 +1,378 @@ -//package com.zteits.urbanops.module.garden.controller.admin.costfee; -// -//import com.zteits.urbanops.framework.common.pojo.CommonResult; -//import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; -//import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport; -//import com.zteits.urbanops.module.garden.enums.TemplateEnum; -//import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; -//import io.swagger.v3.oas.annotations.Operation; -//import io.swagger.v3.oas.annotations.tags.Tag; -//import jakarta.servlet.http.HttpServletResponse; -//import lombok.AllArgsConstructor; -//import lombok.Data; -//import lombok.extern.slf4j.Slf4j; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.beans.factory.annotation.Value; -//import org.springframework.core.io.ClassPathResource; -//import org.springframework.util.CollectionUtils; -//import org.springframework.web.bind.annotation.*; -//import org.springframework.web.multipart.MultipartFile; -// -//import java.io.IOException; -//import java.io.InputStream; -//import java.io.OutputStream; -//import java.lang.reflect.Method; -//import java.net.URLEncoder; -//import java.nio.charset.StandardCharsets; -//import java.util.*; -// -//import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -//import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; -// -///** -// * 人机材公共处理控制器 -// * 处理Excel导入预览、提交等功能 -// */ -//@Tag(name = "管理后台 - 人机材批量导入") -//@RestController -//@RequestMapping("/costfee") -//@Slf4j -//public class CostFeeController{ -// -// @Autowired -// private CostFeeService costFeeService; -// -// // 静态资源服务器基础URL(从配置文件读取) -// @Value("${static.resource.server.url}") -// private String staticResourceServerUrl; -// -// @Value("${static.resource.server.enable}") -// private Boolean staticResourceServerenable; -// -// // Excel文件校验相关 -// private static final List ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm"); -// private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; -// private static final String EXCEL_MIME_TYPE_XLS = "application/vnd.ms-excel"; -// -// /** -// * 导入模板下载 -// */ -// @GetMapping("/get-import-template") -// @Operation(summary = "获得导入用户模板") -// public void importTemplate(HttpServletResponse response, -// @RequestParam("templateName") String templateName) throws IOException { -// // 参数校验 -// if (templateName == null || templateName.trim().isEmpty()) { -// response.sendError(HttpServletResponse.SC_BAD_REQUEST, "模板名称不能为空"); -// return; -// } -// -// // 模板类型校验 -// TemplateEnum template = TemplateEnum.getByCode(templateName); -// if (template == null) { -// response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未知的模板类型: " + templateName); -// return; -// } -// -// if (staticResourceServerenable) { -// // 构建完整的静态服务器URL(确保路径正确) -// String serverUrl = staticResourceServerUrl; -// if (serverUrl != null && !serverUrl.endsWith("/")) { -// serverUrl += "/"; // 确保URL以/结尾,避免拼接错误 -// } -// -// String encodedFileName = URLEncoder.encode(template.getName().split("\\.")[0], StandardCharsets.UTF_8.name()); -// -// String templateFileName = encodedFileName + "." + template.getName().split("\\.")[1]; -// -// String templateUrl = serverUrl + templateFileName; // 包含templates目录 -// -// // 方式1:重定向到静态资源服务器 -// response.sendRedirect(templateUrl); -// } else { -// String templateFileName = templateName + "." + template.getName().split("\\.")[1]; -// -// // 写入模板文件到响应 -// writeTemplateToResponse(response, template.getName(), templateFileName); -// } -// } -// -// -// /** -// * Excel文件上传预览 -// */ -// @PostMapping("/preview") -// @Operation(summary = "文件上传") -// public CommonResult previewExcel(@RequestParam("file") MultipartFile file, -// @RequestParam("templateName") String templateName) { -// // 基础参数校验 -// if (templateName == null || templateName.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); -// -// } -// // 模板类型校验 -// TemplateEnum template = TemplateEnum.getByCode(templateName); -// if (template == null) { -// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); -// } -// String suffix = template.getName().split("\\.")[1]; -// // 文件有效性校验 -// if (!isValidExcelFile(file)) { -// return CommonResult.error(BAD_REQUEST, "请上传有效的Excel文件"); -// } -// //部门信息缓存,校验正确性使用 -// -// -// // 解析Excel并获取数据 -// ImportResult result = parseExcelFile(file, template); -// -// // 需预览的数据存入Redis并返回批次ID -// if (result.isNeedPreview()) { -// if (CollectionUtils.isEmpty(result.getDataList())) { -// return CommonResult.error(BAD_REQUEST, "导入的Excel文件不能为空"); -// -// } -// String batchId = costFeeService.saveDataToRedis(result.getDataList(), templateName); -// Map resultMap = new HashMap<>(2); -// resultMap.put("batchId", batchId); -// resultMap.put("totalCount", result.getDataList().size()); -// resultMap.put("templateName",templateName); -// return success(resultMap); -// } -// // 无需预览的直接返回数据 -// return success(result.getDataList()); -// } -// -// -// /** -// * 分页获取预览数据 -// */ -// @GetMapping("/preview-data") -// @Operation(summary = "批量上传分页预览") -// public CommonResult getPreviewData(@RequestParam("batchId") String batchId, -// @RequestParam("templateName") String templateName, -// @RequestParam(value = "page", defaultValue = "1") int page, -// @RequestParam(value = "size", defaultValue = "100000") int size) { -// -// -// // 参数校验 -// if (batchId == null || batchId.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); -// } -// if (templateName == null || templateName.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); -// -// } -// if (TemplateEnum.getByCode(templateName) == null) { -// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); -// -// } -// try { -// //if (page < 1 || size < 1 || size > 100) { // 限制每页最大100条,避免过大 -// // return AjaxResult.error("分页参数无效(page≥1,1≤size≤100)"); -// //} -// -// // 模板类型校验 -// TemplateEnum template = TemplateEnum.getByCode(templateName); -// Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch -// -// // 从Redis获取分页数据 -// PageResult pageResult = costFeeService.getPagedDataFromRedis(batchId, templateName, page, size, importClass); -// return CommonResult.success(pageResult); -// } catch (Exception e) { -// log.error("获取预览数据失败", e); -// throw new RuntimeException(e); -// } -// } -// -// -// /** -// * 提交导入数据到数据库 -// */ -// @PostMapping("/submit") -// @Operation(summary = "批次保存") -// public CommonResult submitImport(@RequestParam("batchId") String batchId, -// @RequestParam("templateName") String templateName, -// @RequestParam("operator") String operator) { -// // 参数校验 -// if (batchId == null || batchId.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); -// } -// if (templateName == null || templateName.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); -// -// } -// if (TemplateEnum.getByCode(templateName) == null) { -// return CommonResult.error(BAD_REQUEST, "未知的模板类型"); -// -// } -// if (operator == null || operator.trim().isEmpty()) { -// return CommonResult.error(BAD_REQUEST, "操作人不能为空"); -// } -// int count = costFeeService.submitImport(batchId, templateName, operator); -// return CommonResult.success("导入成功,共" + count + "条数据"); -// } -// -// /** -// * 写入模板文件到响应流 -// */ -// private void writeTemplateToResponse(HttpServletResponse response, String filename, String templatePath) throws IOException { -// // 设置响应头 -// response.setContentType(EXCEL_MIME_TYPE); -// response.setCharacterEncoding("UTF-8"); -// String encodedFileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20"); -// response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); -// response.setHeader("Cache-Control", "no-store"); // 禁止缓存 -// -// // 读取模板文件并写入响应 -// try (InputStream in = new ClassPathResource("template/" + templatePath).getInputStream(); -// OutputStream out = response.getOutputStream()) { -// -// byte[] buffer = new byte[4096]; -// int bytesRead; -// while ((bytesRead = in.read(buffer)) != -1) { -// out.write(buffer, 0, bytesRead); -// } -// out.flush(); -// } -// } -// -// -// /** -// * 校验Excel文件有效性(后缀+MIME类型) -// */ -// private boolean isValidExcelFile(MultipartFile file) { -// if (file == null || file.isEmpty()) { -// return false; -// } -// String fileName = file.getOriginalFilename(); -// if (fileName == null) { -// return false; -// } -// -// // 校验文件后缀 -// boolean validSuffix = ALLOWED_EXCEL_SUFFIX.stream() -// .anyMatch(suffix -> fileName.toLowerCase().endsWith(suffix)); -// if (!validSuffix) { -// return false; -// } -// -// // 4. 校验MIME类型(兼容可能带参数的MIME类型,如charset) -// String contentType = file.getContentType(); -// if (contentType == null) { -// return false; -// } -// // 处理MIME类型可能带参数的情况(如"application/xxx;charset=UTF-8") -// String baseContentType = contentType.split(";")[0].trim(); -// // 3. 匹配预设的Excel MIME类型(.xlsx/.xlsm或.xls) -// return EXCEL_MIME_TYPE.equals(baseContentType) -// || baseContentType.startsWith(EXCEL_MIME_TYPE_XLS); -// } -// -// -// /** -// * 解析Excel文件并返回导入结果 -// */ -// @SuppressWarnings("unchecked") -// private ImportResult parseExcelFile(MultipartFile file, TemplateEnum template){ -// // 获取模板对应的导入类 -// Class importClass = (Class) template.getImportClass(); -// List dataList = null; -// try { -// // 解析Excel(从指定行开始读取) -// dataList = ExcelUtils.read(file, importClass, template.getStartIndex()); -// if (dataList == null) { -// dataList = Collections.emptyList(); -// } -// // 关键:移除最后一行(以“说明”开头的行) -// removeLastLineIfStartWithExplain(dataList, importClass); -// // 基础数据校验(非空、格式等) -// costFeeService.validateImportData(dataList, template); -// -// } catch (IOException e) { -// log.error(e.getMessage()); -// } -// return new ImportResult<>(dataList, template.isNeedPreview()); -// } -// /** -// * 移除列表最后一行(若该行第一列内容以“说明”开头) -// * @param dataList Excel解析后的实体类列表 -// * @param clazz 实体类字节码(用于反射获取第一列字段值) -// * @param 泛型:Excel对应的实体类 -// */ -// private void removeLastLineIfStartWithExplain(List dataList, Class clazz) { -// // 1. 跳过空列表/元素数<1的情况,避免索引越界 -// if (CollectionUtils.isEmpty(dataList)) { -// return; -// } -// -// // 2. 获取最后一个元素(正确索引:size()-1) -// T lastItem = dataList.get(dataList.size() - 1); -// if (Objects.isNull(lastItem)) { -// return; -// } -// -// // 3. 反射获取「第一列字段的值」(核心:Excel列对应实体类的字段,假设第一列字段名是 firstColumn,需根据实际调整) -// String firstColumnValue = getFirstColumnValue(lastItem, clazz); -// if (Objects.isNull(firstColumnValue)) { -// return; -// } -// -// // 4. 匹配“说明”(trim() 避免空格干扰,如“ 说明:xxx”) -// if (firstColumnValue.trim().startsWith("说明")) { -// // 移除最后一个元素(正确索引:size()-1) -// dataList.remove(dataList.size() - 1); -// } -// } -// -// /** -// * 反射获取实体类的「第一列字段值」(需根据你的Excel列配置调整字段名) -// * @param item 实体类对象(Excel一行数据) -// * @param clazz 实体类字节码 -// * @return 第一列的字符串值(null 表示无值) -// */ -// private String getFirstColumnValue(T item, Class clazz) { -// try { -// String firstColumnFieldName = ""; -// if (WaterFeeImport.class.isAssignableFrom(clazz)) { -// firstColumnFieldName = "feeMonth"; -// } else { -// return null; -// } -// -// // 反射获取 getter 方法() -// String getterMethodName = "get" + firstColumnFieldName.substring(0, 1).toUpperCase() + firstColumnFieldName.substring(1); -// Method getterMethod = clazz.getDeclaredMethod(getterMethodName); -// -// // 执行 getter 方法获取字段值 -// Object value = getterMethod.invoke(item); -// return value == null ? null : value.toString(); -// } catch (Exception e) { -// // 反射失败(字段名错误、无getter方法),打印日志不抛异常,避免影响整体流程 -// log.warn("获取Excel第一列字段值失败,实体类:{}", clazz.getName(), e); -// return null; -// } -// } -// -// /** -// * 导入结果封装 -// */ -// @Data -// @AllArgsConstructor -// private static class ImportResult { -// private List dataList; -// private boolean needPreview; -// } -// -// /** -// * 分页结果封装 -// */ -// @Data -// @AllArgsConstructor -// public static class PageResult { -// private int page; -// private int size; -// private int total; -// private List data; -// } -//} +package com.zteits.urbanops.module.garden.controller.admin.costfee; + +import com.zteits.urbanops.framework.common.pojo.CommonResult; +import com.zteits.urbanops.framework.excel.core.util.ExcelUtils; +import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImport; +import com.zteits.urbanops.module.garden.enums.TemplateEnum; +import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletResponse; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.CollectionUtils; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.lang.reflect.Method; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; + +/** + * 人机材公共处理控制器 + * 处理Excel导入预览、提交等功能 + */ +@Tag(name = "管理后台 - 人机材批量导入") +@RestController +@RequestMapping("/costfee") +@Slf4j +public class CostFeeController{ + + @Autowired + private CostFeeService costFeeService; + + // 静态资源服务器基础URL(从配置文件读取) + @Value("${static.resource.server.url}") + private String staticResourceServerUrl; + + @Value("${static.resource.server.enable}") + private Boolean staticResourceServerenable; + + // Excel文件校验相关 + private static final List ALLOWED_EXCEL_SUFFIX = Arrays.asList(".xlsx", ".xlsm"); + private static final String EXCEL_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; + private static final String EXCEL_MIME_TYPE_XLS = "application/vnd.ms-excel"; + + /** + * 导入模板下载 + */ + @GetMapping("/get-import-template") + @Operation(summary = "获得导入用户模板") + public void importTemplate(HttpServletResponse response, + @RequestParam("templateName") String templateName) throws IOException { + // 参数校验 + if (templateName == null || templateName.trim().isEmpty()) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "模板名称不能为空"); + return; + } + + // 模板类型校验 + TemplateEnum template = TemplateEnum.getByCode(templateName); + if (template == null) { + response.sendError(HttpServletResponse.SC_BAD_REQUEST, "未知的模板类型: " + templateName); + return; + } + + if (staticResourceServerenable) { + // 构建完整的静态服务器URL(确保路径正确) + String serverUrl = staticResourceServerUrl; + if (serverUrl != null && !serverUrl.endsWith("/")) { + serverUrl += "/"; // 确保URL以/结尾,避免拼接错误 + } + + String encodedFileName = URLEncoder.encode(template.getName().split("\\.")[0], StandardCharsets.UTF_8.name()); + + String templateFileName = encodedFileName + "." + template.getName().split("\\.")[1]; + + String templateUrl = serverUrl + templateFileName; // 包含templates目录 + + // 方式1:重定向到静态资源服务器 + response.sendRedirect(templateUrl); + } else { + String templateFileName = templateName + "." + template.getName().split("\\.")[1]; + + // 写入模板文件到响应 + writeTemplateToResponse(response, template.getName(), templateFileName); + } + } + + + /** + * Excel文件上传预览 + */ + @PostMapping("/preview") + @Operation(summary = "文件上传") + public CommonResult previewExcel(@RequestParam("file") MultipartFile file, + @RequestParam("templateName") String templateName) { + // 基础参数校验 + if (templateName == null || templateName.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); + + } + // 模板类型校验 + TemplateEnum template = TemplateEnum.getByCode(templateName); + if (template == null) { + return CommonResult.error(BAD_REQUEST, "未知的模板类型"); + } + String suffix = template.getName().split("\\.")[1]; + // 文件有效性校验 + if (!isValidExcelFile(file)) { + return CommonResult.error(BAD_REQUEST, "请上传有效的Excel文件"); + } + //部门信息缓存,校验正确性使用 + + + // 解析Excel并获取数据 + ImportResult result = parseExcelFile(file, template); + + // 需预览的数据存入Redis并返回批次ID + if (result.isNeedPreview()) { + if (CollectionUtils.isEmpty(result.getDataList())) { + return CommonResult.error(BAD_REQUEST, "导入的Excel文件不能为空"); + + } + String batchId = costFeeService.saveDataToRedis(result.getDataList(), templateName); + Map resultMap = new HashMap<>(2); + resultMap.put("batchId", batchId); + resultMap.put("totalCount", result.getDataList().size()); + resultMap.put("templateName",templateName); + return success(resultMap); + } + // 无需预览的直接返回数据 + return success(result.getDataList()); + } + + + /** + * 分页获取预览数据 + */ + @GetMapping("/preview-data") + @Operation(summary = "批量上传分页预览") + public CommonResult getPreviewData(@RequestParam("batchId") String batchId, + @RequestParam("templateName") String templateName, + @RequestParam(value = "page", defaultValue = "1") int page, + @RequestParam(value = "size", defaultValue = "100000") int size) { + + + // 参数校验 + if (batchId == null || batchId.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); + } + if (templateName == null || templateName.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); + + } + if (TemplateEnum.getByCode(templateName) == null) { + return CommonResult.error(BAD_REQUEST, "未知的模板类型"); + + } + try { + //if (page < 1 || size < 1 || size > 100) { // 限制每页最大100条,避免过大 + // return AjaxResult.error("分页参数无效(page≥1,1≤size≤100)"); + //} + + // 模板类型校验 + TemplateEnum template = TemplateEnum.getByCode(templateName); + Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch + + // 从Redis获取分页数据 + PageResult pageResult = costFeeService.getPagedDataFromRedis(batchId, templateName, page, size, importClass); + return CommonResult.success(pageResult); + } catch (Exception e) { + log.error("获取预览数据失败", e); + throw new RuntimeException(e); + } + } + + + /** + * 提交导入数据到数据库 + */ + @PostMapping("/submit") + @Operation(summary = "批次保存") + public CommonResult submitImport(@RequestParam("batchId") String batchId, + @RequestParam("templateName") String templateName, + @RequestParam("operator") String operator) { + // 参数校验 + if (batchId == null || batchId.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "批次ID不能为空"); + } + if (templateName == null || templateName.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "模板名称不能为空"); + + } + if (TemplateEnum.getByCode(templateName) == null) { + return CommonResult.error(BAD_REQUEST, "未知的模板类型"); + + } + if (operator == null || operator.trim().isEmpty()) { + return CommonResult.error(BAD_REQUEST, "操作人不能为空"); + } + int count = costFeeService.submitImport(batchId, templateName, operator); + return CommonResult.success("导入成功,共" + count + "条数据"); + } + + /** + * 写入模板文件到响应流 + */ + private void writeTemplateToResponse(HttpServletResponse response, String filename, String templatePath) throws IOException { + // 设置响应头 + response.setContentType(EXCEL_MIME_TYPE); + response.setCharacterEncoding("UTF-8"); + String encodedFileName = URLEncoder.encode(filename, "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName); + response.setHeader("Cache-Control", "no-store"); // 禁止缓存 + + // 读取模板文件并写入响应 + try (InputStream in = new ClassPathResource("template/" + templatePath).getInputStream(); + OutputStream out = response.getOutputStream()) { + + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) != -1) { + out.write(buffer, 0, bytesRead); + } + out.flush(); + } + } + + + /** + * 校验Excel文件有效性(后缀+MIME类型) + */ + private boolean isValidExcelFile(MultipartFile file) { + if (file == null || file.isEmpty()) { + return false; + } + String fileName = file.getOriginalFilename(); + if (fileName == null) { + return false; + } + + // 校验文件后缀 + boolean validSuffix = ALLOWED_EXCEL_SUFFIX.stream() + .anyMatch(suffix -> fileName.toLowerCase().endsWith(suffix)); + if (!validSuffix) { + return false; + } + + // 4. 校验MIME类型(兼容可能带参数的MIME类型,如charset) + String contentType = file.getContentType(); + if (contentType == null) { + return false; + } + // 处理MIME类型可能带参数的情况(如"application/xxx;charset=UTF-8") + String baseContentType = contentType.split(";")[0].trim(); + // 3. 匹配预设的Excel MIME类型(.xlsx/.xlsm或.xls) + return EXCEL_MIME_TYPE.equals(baseContentType) + || baseContentType.startsWith(EXCEL_MIME_TYPE_XLS); + } + + + /** + * 解析Excel文件并返回导入结果 + */ + @SuppressWarnings("unchecked") + private ImportResult parseExcelFile(MultipartFile file, TemplateEnum template){ + // 获取模板对应的导入类 + Class importClass = (Class) template.getImportClass(); + List dataList = null; + try { + // 解析Excel(从指定行开始读取) + dataList = ExcelUtils.read(file, importClass, template.getStartIndex()); + if (dataList == null) { + dataList = Collections.emptyList(); + } + // 关键:移除最后一行(以“说明”开头的行) + removeLastLineIfStartWithExplain(dataList, importClass); + // 基础数据校验(非空、格式等) + costFeeService.validateImportData(dataList, template); + + } catch (IOException e) { + log.error(e.getMessage()); + } + return new ImportResult<>(dataList, template.isNeedPreview()); + } + /** + * 移除列表最后一行(若该行第一列内容以“说明”开头) + * @param dataList Excel解析后的实体类列表 + * @param clazz 实体类字节码(用于反射获取第一列字段值) + * @param 泛型:Excel对应的实体类 + */ + private void removeLastLineIfStartWithExplain(List dataList, Class clazz) { + // 1. 跳过空列表/元素数<1的情况,避免索引越界 + if (CollectionUtils.isEmpty(dataList)) { + return; + } + + // 2. 获取最后一个元素(正确索引:size()-1) + T lastItem = dataList.get(dataList.size() - 1); + if (Objects.isNull(lastItem)) { + return; + } + + // 3. 反射获取「第一列字段的值」(核心:Excel列对应实体类的字段,假设第一列字段名是 firstColumn,需根据实际调整) + String firstColumnValue = getFirstColumnValue(lastItem, clazz); + if (Objects.isNull(firstColumnValue)) { + return; + } + + // 4. 匹配“说明”(trim() 避免空格干扰,如“ 说明:xxx”) + if (firstColumnValue.trim().startsWith("说明")) { + // 移除最后一个元素(正确索引:size()-1) + dataList.remove(dataList.size() - 1); + } + } + + /** + * 反射获取实体类的「第一列字段值」(需根据你的Excel列配置调整字段名) + * @param item 实体类对象(Excel一行数据) + * @param clazz 实体类字节码 + * @return 第一列的字符串值(null 表示无值) + */ + private String getFirstColumnValue(T item, Class clazz) { + try { + String firstColumnFieldName = ""; + if (WaterFeeImport.class.isAssignableFrom(clazz)) { + firstColumnFieldName = "feeMonth"; + } else { + return null; + } + + // 反射获取 getter 方法() + String getterMethodName = "get" + firstColumnFieldName.substring(0, 1).toUpperCase() + firstColumnFieldName.substring(1); + Method getterMethod = clazz.getDeclaredMethod(getterMethodName); + + // 执行 getter 方法获取字段值 + Object value = getterMethod.invoke(item); + return value == null ? null : value.toString(); + } catch (Exception e) { + // 反射失败(字段名错误、无getter方法),打印日志不抛异常,避免影响整体流程 + log.warn("获取Excel第一列字段值失败,实体类:{}", clazz.getName(), e); + return null; + } + } + + /** + * 导入结果封装 + */ + @Data + @AllArgsConstructor + private static class ImportResult { + private List dataList; + private boolean needPreview; + } + + /** + * 分页结果封装 + */ + @Data + @AllArgsConstructor + public static class PageResult { + private int page; + private int size; + private int total; + private List data; + } +} diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/wasterecord/vo/WasteRecordSaveReqVO.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/wasterecord/vo/WasteRecordSaveReqVO.java index 3948ab7..54cee9a 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/wasterecord/vo/WasteRecordSaveReqVO.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/wasterecord/vo/WasteRecordSaveReqVO.java @@ -43,11 +43,11 @@ public class WasteRecordSaveReqVO { private BigDecimal totalCost; @Schema(description = "归属公司", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") - @NotEmpty(message = "归属公司不能为空") + //@NotEmpty(message = "归属公司不能为空") private String companyName; @Schema(description = "归属公司ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "3314") - @NotEmpty(message = "归属公司ID不能为空") + //@NotEmpty(message = "归属公司ID不能为空") private String companyId; @Schema(description = "部门id", example = "26656") diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeService.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeService.java index cd6cf6e..f106863 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeService.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeService.java @@ -1,64 +1,64 @@ -//package com.zteits.urbanops.module.garden.service.costfee; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; -//import com.zteits.urbanops.module.garden.enums.TemplateEnum; -// -//import java.io.IOException; -//import java.util.List; -//import java.util.Map; -// -///** -// * @Classname ICostFeeService -// * @Description 模板导入公共接口 -// * @Date 2025/7/20 15:10 -// * @Created by wangqian -// */ -//public interface CostFeeService { -// -// /** -// * @Author wangqian -// * @Description 导入数据保存redis -// * @Date 2025/7/20 15:36 -// * @Param dataList -// * @param templateName -// * @Return java.lang.String -// */ -// public String saveDataToRedis(List dataList, String templateName); -// -// /** -// * @Author wangqian -// * @Description 导入数据保存到数据库 -// * @Date 2025/7/20 15:36 -// * @Param batchId -// * @param templateName -// * @param operator -// * @Return int -// */ -// public int submitImport(String batchId, String templateName, String operator); -// -// /** -// * @Author wangqian -// * @Description 预览分页查询 -// * @Date 2025/7/20 15:36 -// * @Param batchId -// * @param templateName -// * @param page -// * @param size -// * @param clazz -// * @Return com.servicemanager.project.costfee.controller.CostFeeController.PageResult -// */ -// public CostFeeController.PageResult getPagedDataFromRedis(String batchId, String templateName, -// int page, int size, Class clazz) throws IOException; -// -// /** -// * @Author wangqian -// * @Description 导入内容校验 -// * @Date 2025/11/24 8:40 -// * @Param dataList -// * @param template -// * @Return void -// */ -// public void validateImportData(List dataList, TemplateEnum template); -// -//} +package com.zteits.urbanops.module.garden.service.costfee; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; +import com.zteits.urbanops.module.garden.enums.TemplateEnum; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * @Classname ICostFeeService + * @Description 模板导入公共接口 + * @Date 2025/7/20 15:10 + * @Created by wangqian + */ +public interface CostFeeService { + + /** + * @Author wangqian + * @Description 导入数据保存redis + * @Date 2025/7/20 15:36 + * @Param dataList + * @param templateName + * @Return java.lang.String + */ + public String saveDataToRedis(List dataList, String templateName); + + /** + * @Author wangqian + * @Description 导入数据保存到数据库 + * @Date 2025/7/20 15:36 + * @Param batchId + * @param templateName + * @param operator + * @Return int + */ + public int submitImport(String batchId, String templateName, String operator); + + /** + * @Author wangqian + * @Description 预览分页查询 + * @Date 2025/7/20 15:36 + * @Param batchId + * @param templateName + * @param page + * @param size + * @param clazz + * @Return com.servicemanager.project.costfee.controller.CostFeeController.PageResult + */ + public CostFeeController.PageResult getPagedDataFromRedis(String batchId, String templateName, + int page, int size, Class clazz) throws IOException; + + /** + * @Author wangqian + * @Description 导入内容校验 + * @Date 2025/11/24 8:40 + * @Param dataList + * @param template + * @Return void + */ + public void validateImportData(List dataList, TemplateEnum template); + +} diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java index 2746e06..5a1dcfa 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java +++ b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java @@ -1,982 +1,981 @@ -//package com.zteits.urbanops.module.garden.service.costfee; -// -//import com.fasterxml.jackson.core.JsonProcessingException; -//import com.fasterxml.jackson.databind.ObjectMapper; -//import com.zteits.urbanops.framework.security.core.LoginUser; -//import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; -//import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.*; -//import com.zteits.urbanops.module.garden.dal.dataobject.departmentcost.DepartmentCostDO; -//import com.zteits.urbanops.module.garden.dal.dataobject.mechanicalcost.MechanicalCostDO; -//import com.zteits.urbanops.module.garden.dal.dataobject.personalcost.PersonalCostDO; -//import com.zteits.urbanops.module.garden.dal.dataobject.rentalmechanicalcost.RentalMechanicalCostDO; -//import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; -//import com.zteits.urbanops.module.garden.dal.dataobject.waterfee.WaterFeeDO; -//import com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper; -//import com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper; -//import com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper; -//import com.zteits.urbanops.module.garden.dal.mysql.rentalmechanicalcost.RentalMechanicalCostMapper; -//import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; -//import com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper; -//import com.zteits.urbanops.module.garden.enums.AssignTasksConstants; -//import com.zteits.urbanops.module.garden.enums.CostFeeConstants; -//import com.zteits.urbanops.module.garden.enums.TemplateEnum; -//import com.zteits.urbanops.module.system.api.dept.DeptApi; -//import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; -//import jakarta.annotation.Resource; -//import jakarta.validation.ConstraintViolation; -//import jakarta.validation.ConstraintViolationException; -//import jakarta.validation.Validator; -//import lombok.extern.slf4j.Slf4j; -//import org.apache.commons.lang3.StringUtils; -//import org.springframework.beans.BeanUtils; -//import org.springframework.beans.factory.annotation.Autowired; -//import org.springframework.data.redis.core.BoundHashOperations; -//import org.springframework.data.redis.core.RedisTemplate; -//import org.springframework.stereotype.Service; -//import org.springframework.transaction.annotation.Transactional; -//import org.springframework.util.CollectionUtils; -// -//import java.io.IOException; -//import java.time.LocalDate; -//import java.util.*; -//import java.util.concurrent.TimeUnit; -//import java.util.function.Function; -//import java.util.stream.Collectors; -//import java.util.stream.IntStream; -// -//import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; -//import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; -//import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; -//import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; -//import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.COST_FEE_NOT_EXISTS_001; -// -///** -// * @Classname CostFeeService -// * @Description 模板导入公共实现 -// * @Date 2025/7/20 15:12 -// * @Created by wangqian -// */ -//@Service -//@Slf4j -//public class CostFeeServiceImpl implements CostFeeService { -// -// // Redis相关配置 -// private static final String REDIS_KEY_PREFIX = "excel:import:"; -// private static final long EXPIRE_TIME = 30; // 预览数据过期时间:30分钟 -// private static final TimeUnit EXPIRE_UNIT = TimeUnit.MINUTES; -// @Autowired -// private RedisTemplate redisTemplate; -// @Autowired -// private ObjectMapper objectMapper; -// @Resource -// private Validator validator; -// @Resource -// private DeptApi deptApi; -// -// @Autowired -// private WaterFeeMapper waterFeeMapper; -// @Autowired -// private PersonalCostMapper personalCostMapper; -// @Autowired -// private DepartmentCostMapper departmentCostMapper; -// @Autowired -// private MechanicalCostMapper mechanicalCostMapper; -// @Autowired -// private RentalMechanicalCostMapper rentalMechanicalCostMapper; -// @Autowired -// private WasteRecordMapper wasteRecordMapper; -// -// -// @Override -// /** -// * 保存数据到Redis -// */ -// public String saveDataToRedis(List dataList, String templateName){ -// String batchId = UUID.randomUUID().toString().replace("-", ""); -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; -// -// // 存储总记录数 -// redisTemplate.opsForValue().set( -// redisKey + ":total", -// String.valueOf(dataList.size()), -// EXPIRE_TIME, -// EXPIRE_UNIT -// ); -// -// // 使用Hash存储每条数据(key:索引,value:JSON字符串) -// BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); -// try { -// for (int i = 0; i < dataList.size(); i++) { -// String jsonData = objectMapper.writeValueAsString(dataList.get(i)); -// hashOps.put(String.valueOf(i), jsonData); -// } -// } catch (JsonProcessingException e) { -// log.error(e.getMessage()); -// } -// // 设置Hash过期时间 -// hashOps.expire(EXPIRE_TIME, EXPIRE_UNIT); -// -// log.info("数据保存到Redis成功,批次ID:{},记录数:{}", batchId, dataList.size()); -// return batchId; -// } -// -// @Override -// @Transactional -// public int submitImport(String batchId, String templateName, String operator){ -// // 模板类型校验 -// TemplateEnum template = TemplateEnum.getByCode(templateName); -// Class importClass = template.getImportClass(); -// if (importClass == null) { -// throw exception0(BAD_REQUEST.getCode(),"导入模板配置异常"); -// } -// -// // 分页参数设置 -// int pageNum = 1; -// final int pageSize = 100000; // 每页处理条数,可根据实际情况调整 -// int totalImported = 0; // 总导入数量累计 -// -// try { -// while (true) { -// // 分页从Redis获取数据 -// CostFeeController.PageResult pageResult = getPagedDataFromRedis( -// batchId, templateName, pageNum, pageSize, importClass); -// List dataList = pageResult.getData(); -// -// // 如果当前页无数据,说明已处理完毕 -// if (dataList == null || dataList.isEmpty()) { -// // 首次分页就无数据,抛出异常 -// if (pageNum == 1) { -// throw exception0(BAD_REQUEST.getCode(),"没有可导入的数据或数据已过期"); -// } -// break; +package com.zteits.urbanops.module.garden.service.costfee; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; +import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.*; +import com.zteits.urbanops.module.garden.dal.dataobject.departmentcost.DepartmentCostDO; +import com.zteits.urbanops.module.garden.dal.dataobject.mechanicalcost.MechanicalCostDO; +import com.zteits.urbanops.module.garden.dal.dataobject.personalcost.PersonalCostDO; +import com.zteits.urbanops.module.garden.dal.dataobject.rentalmechanicalcost.RentalMechanicalCostDO; +import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; +import com.zteits.urbanops.module.garden.dal.dataobject.waterfee.WaterFeeDO; +import com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper; +import com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper; +import com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper; +import com.zteits.urbanops.module.garden.dal.mysql.rentalmechanicalcost.RentalMechanicalCostMapper; +import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; +import com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper; +import com.zteits.urbanops.module.garden.enums.AssignTasksConstants; +import com.zteits.urbanops.module.garden.enums.CostFeeConstants; +import com.zteits.urbanops.module.garden.enums.TemplateEnum; +import com.zteits.urbanops.module.system.api.dept.DeptApi; +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; +import jakarta.annotation.Resource; +import jakarta.validation.ConstraintViolation; +import jakarta.validation.ConstraintViolationException; +import jakarta.validation.Validator; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.BoundHashOperations; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.io.IOException; +import java.time.LocalDate; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; +import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.COST_FEE_NOT_EXISTS_001; + +/** + * @Classname CostFeeService + * @Description 模板导入公共实现 + * @Date 2025/7/20 15:12 + * @Created by wangqian + */ +@Service +@Slf4j +public class CostFeeServiceImpl implements CostFeeService { + + // Redis相关配置 + private static final String REDIS_KEY_PREFIX = "excel:import:"; + private static final long EXPIRE_TIME = 30; // 预览数据过期时间:30分钟 + private static final TimeUnit EXPIRE_UNIT = TimeUnit.MINUTES; + @Autowired + private RedisTemplate redisTemplate; + @Autowired + private ObjectMapper objectMapper; + @Resource + private Validator validator; + @Resource + private DeptApi deptApi; + + @Autowired + private WaterFeeMapper waterFeeMapper; + @Autowired + private PersonalCostMapper personalCostMapper; + @Autowired + private DepartmentCostMapper departmentCostMapper; + @Autowired + private MechanicalCostMapper mechanicalCostMapper; + @Autowired + private RentalMechanicalCostMapper rentalMechanicalCostMapper; + @Autowired + private WasteRecordMapper wasteRecordMapper; + + + @Override + /** + * 保存数据到Redis + */ + public String saveDataToRedis(List dataList, String templateName){ + String batchId = UUID.randomUUID().toString().replace("-", ""); + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; + + // 存储总记录数 + redisTemplate.opsForValue().set( + redisKey + ":total", + String.valueOf(dataList.size()), + EXPIRE_TIME, + EXPIRE_UNIT + ); + + // 使用Hash存储每条数据(key:索引,value:JSON字符串) + BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); + try { + for (int i = 0; i < dataList.size(); i++) { + String jsonData = objectMapper.writeValueAsString(dataList.get(i)); + hashOps.put(String.valueOf(i), jsonData); + } + } catch (JsonProcessingException e) { + log.error(e.getMessage()); + } + // 设置Hash过期时间 + hashOps.expire(EXPIRE_TIME, EXPIRE_UNIT); + + log.info("数据保存到Redis成功,批次ID:{},记录数:{}", batchId, dataList.size()); + return batchId; + } + + @Override + @Transactional + public int submitImport(String batchId, String templateName, String operator){ + // 模板类型校验 + TemplateEnum template = TemplateEnum.getByCode(templateName); + Class importClass = template.getImportClass(); + if (importClass == null) { + throw exception0(BAD_REQUEST.getCode(),"导入模板配置异常"); + } + + // 分页参数设置 + int pageNum = 1; + final int pageSize = 100000; // 每页处理条数,可根据实际情况调整 + int totalImported = 0; // 总导入数量累计 + + try { + while (true) { + // 分页从Redis获取数据 + CostFeeController.PageResult pageResult = getPagedDataFromRedis( + batchId, templateName, pageNum, pageSize, importClass); + List dataList = pageResult.getData(); + + // 如果当前页无数据,说明已处理完毕 + if (dataList == null || dataList.isEmpty()) { + // 首次分页就无数据,抛出异常 + if (pageNum == 1) { + throw exception0(BAD_REQUEST.getCode(),"没有可导入的数据或数据已过期"); + } + break; + } + + // 业务校验(每页单独校验) + //validateBusinessData(dataList, template); + + // 调用业务服务执行本页数据导入 + int currentPageCount = dataList.size(); + insertData(template, dataList, operator); + totalImported += currentPageCount; + + // 打印分页处理日志(可选) + log.info("批次[{}]第{}页导入完成,本页{}条,累计{}条", + batchId, pageNum, currentPageCount, totalImported); + + // 准备下一页 + pageNum++; + + // 如果当前页数据小于页大小,说明已到最后一页 + if (dataList.size() < pageSize) { + break; + } + } + } catch (IOException e) { + log.error(e.getMessage()); + } finally { + // 无论导入成功与否,都清理Redis临时数据 + deleteRedisTempData(batchId, templateName); + } + + return totalImported; + } + /** + * @Author wangqian + * @Description 保存数据 + * @Date 2025/7/20 11:30 + * @Param template + * @param dataList + * @param operator + * @Return void + */ + private void insertData(TemplateEnum template, List dataList, String operator) { + if (CollectionUtils.isEmpty(dataList)) { + log.info("数据列表为空,跳过插入操作"); + return; + } + //单位集合 + List companyList = deptApi.getSubDeptList(); + // 1. 预加载部门映射(循环外加载,避免重复查询) + List deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); + Map nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) ? Map.of() + : companyList.stream() + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) + .collect(Collectors.toMap( + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) + DeptRespDTO::getId, // key 映射:取 companyId + (oldValue, newValue) -> newValue + )); + + + // 模板类型校验 + Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch + // 根据不同模板类型执行不同的处理逻辑 + if (WaterFeeImport.class.isAssignableFrom(importClass)) { + Map companyIdToNamedMap = CollectionUtils.isEmpty(companyList) ? Map.of() + : companyList.stream() + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) + .collect(Collectors.toMap( + DeptRespDTO::getId, // key 映射:取 companyId + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) + (oldValue, newValue) -> newValue + )); + + // 处理水费数据 + List feeList = ((List) dataList).stream() + .map(importObj -> { + WaterFeeDO fee = new WaterFeeDO(); + BeanUtils.copyProperties(importObj, fee); + fee.setCompanyId(String.valueOf(getCompanyId())); + if (getCompanyId() == 100) { + fee.setCompanyName("蓟城山水"); + } else { + // 设置单位id + fee.setCompanyName(String.valueOf(companyIdToNamedMap.get(fee.getCompanyId()))); + } + return fee; + }) + .collect(Collectors.toMap( + // 生成唯一键:feeMonth + 分隔符 + meterCode(避免不同组合拼接后重复) + fee -> fee.getFeeMonth() + "_" + fee.getMeterCode(), + // 值为对象本身 + Function.identity(), + // 当键重复时,保留第一个出现的对象(可根据需求改为保留最后一个:(existing, replacement) -> replacement) + (existing, replacement) -> replacement + )) + .values() + .stream() + .collect(Collectors.toList()); + + if (!CollectionUtils.isEmpty(feeList)) { + //查询重复项,进行删除 + List ids = waterFeeMapper.selectDuplicateIds(feeList); + if (!CollectionUtils.isEmpty(ids)) { + waterFeeMapper.deleteByIds(ids); + } + // 调用批量插入方法 + waterFeeMapper.insertBatch(feeList); + } + log.info("成功插入 {} 条水费数据", feeList.size()); + } else if (PersonalCostImport.class.isAssignableFrom(importClass)) { + //去重 + dataList = personalFeeReplacement((List) dataList); + //获取班组对象 + Map teamIdMap = this.getDeptListAll(deptIds); + + // 3. 转换导入数据为PersonalCost列表(Stream优化+空指针防护) + List feeList = (List)dataList; + List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); + if (!CollectionUtils.isEmpty(feeList)) { + for (PersonalCostImport importObj : feeList) { + PersonalCostDO fee = new PersonalCostDO(); + BeanUtils.copyProperties(importObj, fee); + // 设置单位id + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); + //设置班组id + //fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); + saveFeeList.add(fee); + } + } + // 4. 处理重复数据(删除+插入原子性) + if (!feeList.isEmpty()) { + // 4.1 查询并删除重复项 + +// List duplicateIds = personalCostMapper.selectDuplicateIds(saveFeeList); +// if (!duplicateIds.isEmpty()) { +// personalCostMapper.deleteByIds(duplicateIds); // } -// -// // 业务校验(每页单独校验) -// //validateBusinessData(dataList, template); -// -// // 调用业务服务执行本页数据导入 -// int currentPageCount = dataList.size(); -// insertData(template, dataList, operator); -// totalImported += currentPageCount; -// -// // 打印分页处理日志(可选) -// log.info("批次[{}]第{}页导入完成,本页{}条,累计{}条", -// batchId, pageNum, currentPageCount, totalImported); -// -// // 准备下一页 -// pageNum++; -// -// // 如果当前页数据小于页大小,说明已到最后一页 -// if (dataList.size() < pageSize) { -// break; -// } -// } -// } catch (IOException e) { -// log.error(e.getMessage()); -// } finally { -// // 无论导入成功与否,都清理Redis临时数据 -// deleteRedisTempData(batchId, templateName); -// } -// -// return totalImported; -// } -// /** -// * @Author wangqian -// * @Description 保存数据 -// * @Date 2025/7/20 11:30 -// * @Param template -// * @param dataList -// * @param operator -// * @Return void -// */ -// private void insertData(TemplateEnum template, List dataList, String operator) { -// if (CollectionUtils.isEmpty(dataList)) { -// log.info("数据列表为空,跳过插入操作"); -// return; -// } -// //单位集合 -// List companyList = deptApi.getSubDeptList(); -// // 1. 预加载部门映射(循环外加载,避免重复查询) -// List deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); -// Map nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) ? Map.of() -// : companyList.stream() -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) -// .collect(Collectors.toMap( -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) -// DeptRespDTO::getId, // key 映射:取 companyId -// (oldValue, newValue) -> newValue -// )); -// -// -// // 模板类型校验 -// Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch -// // 根据不同模板类型执行不同的处理逻辑 -// if (WaterFeeImport.class.isAssignableFrom(importClass)) { -// Map companyIdToNamedMap = CollectionUtils.isEmpty(companyList) ? Map.of() -// : companyList.stream() -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) -// .collect(Collectors.toMap( -// DeptRespDTO::getId, // key 映射:取 companyId -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) -// (oldValue, newValue) -> newValue -// )); -// -// // 处理水费数据 -// List feeList = ((List) dataList).stream() -// .map(importObj -> { -// WaterFeeDO fee = new WaterFeeDO(); -// BeanUtils.copyProperties(importObj, fee); -// fee.setCompanyId(String.valueOf(getCompanyId())); -// if (getCompanyId() == 100) { -// fee.setCompanyName("蓟城山水"); -// } else { -// // 设置单位id -// fee.setCompanyName(String.valueOf(companyIdToNamedMap.get(fee.getCompanyId()))); -// } -// return fee; -// }) -// .collect(Collectors.toMap( -// // 生成唯一键:feeMonth + 分隔符 + meterCode(避免不同组合拼接后重复) -// fee -> fee.getFeeMonth() + "_" + fee.getMeterCode(), -// // 值为对象本身 -// Function.identity(), -// // 当键重复时,保留第一个出现的对象(可根据需求改为保留最后一个:(existing, replacement) -> replacement) -// (existing, replacement) -> replacement -// )) -// .values() -// .stream() -// .collect(Collectors.toList()); -// -// if (!CollectionUtils.isEmpty(feeList)) { -// //查询重复项,进行删除 -// List ids = waterFeeMapper.selectDuplicateIds(feeList); -// if (!CollectionUtils.isEmpty(ids)) { -// waterFeeMapper.deleteByIds(ids); -// } -// // 调用批量插入方法 -// waterFeeMapper.insertBatch(feeList); -// } -// log.info("成功插入 {} 条水费数据", feeList.size()); -// } else if (PersonalCostImport.class.isAssignableFrom(importClass)) { -// //去重 -// dataList = personalFeeReplacement((List) dataList); -// //获取班组对象 -// Map teamIdMap = this.getDeptListAll(deptIds); -// -// // 3. 转换导入数据为PersonalCost列表(Stream优化+空指针防护) -// List feeList = (List)dataList; -// List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); -// if (!CollectionUtils.isEmpty(feeList)) { -// for (PersonalCostImport importObj : feeList) { -// PersonalCostDO fee = new PersonalCostDO(); -// BeanUtils.copyProperties(importObj, fee); -// // 设置单位id -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); -// //设置班组id -// //fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); -// saveFeeList.add(fee); -// } -// } -// // 4. 处理重复数据(删除+插入原子性) -// if (!feeList.isEmpty()) { -// // 4.1 查询并删除重复项 -// -//// List duplicateIds = personalCostMapper.selectDuplicateIds(saveFeeList); -//// if (!duplicateIds.isEmpty()) { -//// personalCostMapper.deleteByIds(duplicateIds); -//// } -// -// // 4.2 批量插入主表 -// personalCostMapper.insertBatch(saveFeeList); -// } -// log.info("成功插入 {} 人员费用数据", dataList.size()); -// } else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { -// //去重 -// dataList = deptFeeReplacement((List)dataList); -// List feeList = ((List) dataList).stream() -// .map(importObj -> { -// DepartmentCostDO fee = new DepartmentCostDO(); -// BeanUtils.copyProperties(importObj, fee); -// // 设置单位id -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); -// return fee; -// }) -// .collect(Collectors.toList()); -// if (!CollectionUtils.isEmpty(feeList)) { -// //查询重复项,进行删除 -// List ids = departmentCostMapper.selectDuplicateIds(feeList); -// if (!CollectionUtils.isEmpty(ids)) { -// departmentCostMapper.deleteByIds(ids); -// } -// // 调用批量插入方法 -// departmentCostMapper.insertBatch(feeList); -// } -// log.info("成功插入 {} 综合费用数据", feeList.size()); -// } else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { -// List feeList = (List) dataList; -// List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); -// if (!CollectionUtils.isEmpty(feeList)) { -// //获取班组对象 -// Map teamIdMap = this.getDeptListAll(deptIds); -// for (MechanicalCostImport importObj : feeList) { -// MechanicalCostDO fee = new MechanicalCostDO(); -// BeanUtils.copyProperties(importObj, fee); -// // 设置单位id -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); -// //设置班组id -// fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); -// // 3.3 计算结束时间(空指针防护) -// Optional.ofNullable(fee.getStartUseTime()) -// .ifPresent(startTime -> { -// int useDuration = Optional.ofNullable(fee.getServiceLife()) -// .map(Math::toIntExact) // 处理可能的溢出 -// .orElse(0); -// fee.setEndUseTime(this.getEndLocalDateForYears(startTime, useDuration)); -// }); -// -// saveFeeList.add(fee); -// } -// } -// // 4. 批量插入主表(空集合直接跳过) -// if (!feeList.isEmpty()) { -// //查询重复项,进行删除 -// List ids = mechanicalCostMapper.selectDuplicateIds(saveFeeList); -// if (!CollectionUtils.isEmpty(ids)) { -// mechanicalCostMapper.deleteByIds(ids); -// } -// mechanicalCostMapper.insertBatch(saveFeeList); -// } -// log.info("成功插入 {} 自有机械费用数据", dataList.size()); -// } else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { -// List feeList = (List)dataList; -// List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); -// if (!CollectionUtils.isEmpty(feeList)) { -// //获取班组对象 -// Map teamIdMap = this.getDeptListAll(deptIds); -// for (RentalMechanicalCostImport importObj : feeList) { -// RentalMechanicalCostDO fee = new RentalMechanicalCostDO(); -// BeanUtils.copyProperties(importObj, fee); -// // 设置单位id -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); -// //设置班组id -// List teamRels = new ArrayList<>(); -// String teamIdsStr = StringUtils.defaultString(importObj.getTeamIds()).replace(",", ","); -// String weightsStr = StringUtils.defaultString(importObj.getWeight()).replace(",", ","); -// -// String[] teamIds = teamIdsStr.split(","); -// String[] weights = weightsStr.split(","); -// for (int i = 0; i < teamIds.length; i++) { -// TeamBizdomainRelVo rel = new TeamBizdomainRelVo(); -// rel.setTeamName(teamIds[i].trim()); -// // 提前处理部门ID映射(复用deptMap) -// rel.setTeamId(Optional.ofNullable(teamIdMap.get(teamIds[i].trim())).map(String::valueOf).orElse(null)); -// rel.setWeight(Long.valueOf(weights[i].trim())); -// teamRels.add(rel); -// } -// fee.setTeamIds(teamRels); -// -// // 3.3 计算结束时间(空指针防护) -// Optional.ofNullable(fee.getStartUseTime()) -// .ifPresent(startTime -> { -// int useDuration = Optional.ofNullable(fee.getUseDuration()) -// .map(Math::toIntExact) // 处理可能的溢出 -// .orElse(0); -// fee.setEndUseTime(getEndLocalDateForYears(startTime, useDuration)); -// }); -// -// saveFeeList.add(fee); -// } -// } -// if (!feeList.isEmpty()) { -// rentalMechanicalCostMapper.insertBatch(saveFeeList); -// } -// log.info("成功插入 {} 租赁机械费用数据", dataList.size()); -// } else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { -// //获取班组对象 -// List feeList = (List)dataList; -// List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); -// if (!CollectionUtils.isEmpty(feeList)) { -// Map teamIdMap = this.getDeptListAll(deptIds); -// for (GardenWasteRecordImport importObj : feeList) { -// WasteRecordDO fee = new WasteRecordDO(); -// BeanUtils.copyProperties(importObj, fee); -// // 设置单位id -// fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); -// //设置班组id -// fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); -// -// saveFeeList.add(fee); -// } -// } -// // 4. 批量插入主表(若主表插入失败,事务回滚) -// if (!feeList.isEmpty()) { -// wasteRecordMapper.insertBatch(saveFeeList); -// } -// -// log.info("成功插入 {} 园林废弃物费用数据", dataList.size()); -// } -// -// } -// -// /** -// * 从Redis获取分页数据 -// */ -// @Override -// public CostFeeController.PageResult < T > getPagedDataFromRedis(String batchId, String templateName, -// int page, int size, Class clazz) throws IOException { -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; -// -// // 获取总记录数 -// String totalStr = redisTemplate.opsForValue().get(redisKey + ":total"); -// int total = totalStr != null ? Integer.parseInt(totalStr) : 0; -// -// // 计算分页范围 -// int start = (page - 1) * size; -// int end = Math.min(start + size - 1, total - 1); -// -// // 分页查询数据 -// List pageData = new ArrayList<>(); -// if (start <= end && total > 0) { -// BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); -// -// // 批量获取指定范围的索引对应的数据 -// List indexes = IntStream.rangeClosed(start, end) -// .mapToObj(String::valueOf) -// .collect(Collectors.toList()); -// List jsonList = hashOps.multiGet(indexes); -// -// // 反序列化为对象 -// for (String json : jsonList) { -// if (json != null) { -// pageData.add(objectMapper.readValue(json, clazz)); -// } -// } -// } -// -// return new CostFeeController.PageResult<>(page, size, total, pageData); -// } -// -// -// /** -// * 从Redis获取全部数据 -// */ -// private List < T > getAllDataFromRedis(String batchId, String templateName, Class < T > clazz) throws -// IOException { -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; -// BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); -// -// // 获取所有数据的JSON字符串 -// List jsonList = hashOps.values(); -// List dataList = new ArrayList<>(jsonList.size()); -// -// // 反序列化为对象 -// for (String json : jsonList) { -// if (json != null) { -// dataList.add(objectMapper.readValue(json, clazz)); -// } -// } -// -// return dataList; -// } -// -// -// /** -// * 删除Redis临时数据 -// */ -// private void deleteRedisTempData (String batchId, String templateName){ -// String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; -// redisTemplate.delete(redisKey + ":total"); -// redisTemplate.delete(redisKey + ":items"); -// log.info("Redis临时数据已删除,批次ID:{}", batchId); -// } -// /** -// * @Author wangqian -// * @Description 导入内容校验 -// * @Date 2025/11/24 8:40 -// * @Param dataList -// * @param template -// * @Return void -// */ -// public void validateImportData(List dataList, TemplateEnum template) { -// if (CollectionUtils.isEmpty(dataList)) { -// return; // 空列表无需校验 -// } -// //登录用户单位 -// Long belongCompanyId = getCompanyId(); -// //单位集合 -// List companyList = deptApi.getSubDeptList(); -// List deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); -// Map nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) -// ? Map.of() // 空列表返回空不可变 Map(避免 null) -// : companyList.stream() -// // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) -// .collect(Collectors.toMap( -// dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) -// DeptRespDTO::getId, // key 映射:取 companyId -// (oldValue, newValue) -> newValue -// )); -// //班组集合 -// Map> deptList = this.getDetpList(deptIds); -// // 模板类型校验 -// Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch -// // 根据不同模板类型执行不同的处理逻辑 -// if (WaterFeeImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// WaterFeeImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// // StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// //errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+ violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); -// } -// -// } catch (ConstraintViolationException ex) { -// // 5. 包装异常,明确行号 + 真实校验错误( -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// else if (PersonalCostImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// PersonalCostImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// //StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// // errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+ violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); -// } -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); -// //单位校验 -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); -// //部门名称校验 -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); -// -// } catch (ConstraintViolationException ex) { -// // 5. 包装异常,明确行号 + 真实校验错误( -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// GardenWasteRecordImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// //StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// //errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+ violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); -// } -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); -// //单位校验 -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); -// //部门名称校验 -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); -// -// } catch (ConstraintViolationException ex) { -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// MechanicalCostImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// //StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// //errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+ violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); -// } -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); -// //单位校验 -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); -// //部门名称校验 -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); -// -// //车辆校验 -// if (CostFeeConstants.MECH_TYPE.equals(importBean.getMechType())) { -// if (StringUtils.isEmpty(importBean.getLicensePlate())) { -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:车牌号不能为空" -// ), rowNum); -// } -// if (StringUtils.isEmpty(importBean.getVehicleType())) { -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:车辆类型不能为空" -// ), rowNum); -// -// } -// if (StringUtils.isEmpty(importBean.getVehicleCategory())) { -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第%d条数据:车辆类别不能为空" -// ), rowNum); -// } -// } -// -// } catch (ConstraintViolationException ex) { -// // 5. 包装异常,明确行号 + 真实校验错误( -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// RentalMechanicalCostImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// // StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// // errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); -// } -// // 处理可能的空值 -// String teamIdsStr = StringUtils.defaultString(importBean.getTeamIds()); -// String weightsStr = StringUtils.defaultString(importBean.getWeight()); -// -// // 统一替换中文逗号并分割 -// String[] teamIds = teamIdsStr.replace(",", ",").split(","); -// String[] weights = weightsStr.replace(",", ",").split(","); -// -// // 校验数组长度是否一致 -// if (teamIds.length != weights.length) { -// throw exception0(BAD_REQUEST.getCode(), String.format( -// "第%d条数据:权重值和所选班组数量不符(班组:%d个,权重:%d个)", -// rowNum, teamIds.length, weights.length -// )); -// } -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); -// //单位校验 -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); -// //部门名称校验 -// validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); -// -// } catch (ConstraintViolationException ex) { -// // 5. 包装异常,明确行号 + 真实校验错误( -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { -// -// List importList = (List) dataList; -// // 2. 遍历,逐个创建 -// for (int i = 0; i < importList.size(); i++) { -// int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) -// DepartmentCostImport importBean = importList.get(i); -// -// try { -// // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) -// Set> violations = validator.validate(importBean); -// if (!violations.isEmpty()) { -// // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) -// //StringBuilder errorMsg = new StringBuilder(); -// for (ConstraintViolation violation : violations) { -// //errorMsg.append(violation.getMessage()).append(";"); -// throw exception0(BAD_REQUEST.getCode() ,String.format( -// "第[{}]条数据:"+violation.getMessage() -// ), rowNum); -// } -// // 抛出包含行号和所有错误的异常 -// //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); -// } -// -// Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); -// //单位校验 -// validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); -// } catch (ConstraintViolationException ex) { -// // 5. 包装异常,明确行号 + 真实校验错误( -// throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); -// } -// } -// } -// } -// -// private void validatecompany(int rowNum, String companyName, Long companyId, Long belongCompanyId){ -// // 校验数组长度是否一致 -// if (companyId == null) { -// throw exception0(BAD_REQUEST.getCode(), String.format( -// "第%d条数据:单位名称不正确(%s)", -// rowNum, companyName -// )); -// } -// // 校验导入单位 -// if (belongCompanyId != 100 && !belongCompanyId.equals(companyId)) { -// throw exception0(BAD_REQUEST.getCode(), String.format( -// "第%d条数据:导入单位%s与当前操作员单位不一致", -// rowNum, companyName -// )); -// } -// } -// private void validateTeams(int rowNum, List dept, String teamIds) { -// if (StringUtils.isNotEmpty(teamIds)) { -// String[] teamNames = teamIds.split(","); -// Set checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复 -// -// for (String teamName : teamNames) { -// String trimmedTeamName = teamName.trim(); // 处理前后空格 -// -// // 重复校验:如果Set中已存在该团队名称,说明重复 -// if (checkedTeams.contains(trimmedTeamName)) { -// throw exception0(BAD_REQUEST.getCode(), String.format( -// "第%d条数据:%s名称重复", -// rowNum, teamName -// )); -// } -// -// // 存在性校验:检查团队是否在dept中存在 -// if (!dept.contains(trimmedTeamName)) { -// throw exception0(BAD_REQUEST.getCode(), String.format( -// "第%d条数据:%s名称不正确", -// rowNum, teamName -// )); -// } -// -// // 将校验通过的团队名称加入Set,用于后续重复判断 -// checkedTeams.add(trimmedTeamName); -// } -// } -// } -// -// //查询单位下的班组信息 -// private Map> getDetpList (List deptIds) { -// Map> listMap = new HashMap<>(); -// for (Long depId : deptIds) { -// List deptList = deptApi.getChildDeptList(depId); -// List names = deptList.stream().map(DeptRespDTO::getName).collect(Collectors.toList()); -// listMap.put(depId,names); -// } -// return listMap; -// } -// -// /** -// * 按salaryMonth、expenseName、companyId三个字段去重 -// * @param costList 原始部门成本列表 -// * @return 去重后的列表 -// */ -// public List deptFeeReplacement(List costList) { -// if (costList == null || costList.isEmpty()) { -// return new ArrayList<>(); -// } -// -// // 1. 以三个字段的组合作为键(使用分隔符避免不同组合产生相同键) -// // 2. 遇到重复键时保留第一条记录 -// Map uniqueMap = costList.stream() -// .collect(Collectors.toMap( -// // 生成唯一键:salaryMonth|expenseName|companyId -// cost -> buildUniqueKey(cost), -// // 键不存在时存入当前对象 -// cost -> cost, -// // 键已存在时保留最后一个 -// (existing, replacement) -> replacement -// )); -// -// // 将去重后的结果转为List返回 -// return new ArrayList<>(uniqueMap.values()); -// } -// -// /** -// * 构建三个字段的唯一组合键(处理null值) -// */ -// private String buildUniqueKey(DepartmentCostImport cost) { -// // 使用Objects.toString处理可能的null值,避免NullPointerException -// return Objects.toString(cost.getSalaryMonth(), "") + "|" + -// Objects.toString(cost.getExpenseName(), "") + "|" + -// Objects.toString(cost.getCompanyName(), ""); -// } -// -// /** -// * 按salaryMonth、name、companyId、teamIdList四个字段去重 -// * @param costList 原始个人成本列表 -// * @return 去重后的列表 -// */ -// public List personalFeeReplacement(List costList) { -// if (costList == null || costList.isEmpty()) { -// return new ArrayList<>(); -// } -// -// // 1. 以四个字段的组合作为键(特别处理集合类型的teamIdList) -// // 2. 遇到重复键时保留第一条记录 -// Map uniqueMap = costList.stream() -// .collect(Collectors.toMap( -// // 生成唯一键:salaryMonth|name|companyId|teamIdListStr -// this::buildUniqueKey, -// // 键不存在时存入当前对象 -// cost -> cost, -// // 键已存在时保留第一个对象(可改为replacement保留最后一个) -// (existing, replacement) -> replacement -// )); -// -// // 将去重后的结果转为List返回 -// return new ArrayList<>(uniqueMap.values()); -// } -// -// /** -// * 构建四个字段的唯一组合键(处理null值和集合类型) -// */ -// private String buildUniqueKey(PersonalCostImport cost) { -// // 处理普通字段的null值 -// String salaryMonth = Objects.toString(cost.getSalaryMonth(), ""); -// String name = Objects.toString(cost.getName(), ""); -// String companyId = Objects.toString(cost.getCompanyName(), ""); -// String teamIds = Objects.toString(cost.getTeamIds(), ""); -// // 拼接唯一键 -// return salaryMonth + "|" + name + "|" + companyId + "|" + teamIds; -// } -// -// -// public static LocalDate getEndLocalDateForYears(LocalDate startDate, int years) { -// if (startDate == null) { -// throw new IllegalArgumentException("开始日期不能为null"); -// } -// if (years < 0) { -// throw new IllegalArgumentException("年数不能为负数"); -// } -// return startDate.plusYears(years); -// } -// -// /** -// * @Author wangqian -// * @Description 根据班组名称获取班组id -// * @Date 2025/11/26 19:38 -// */ -// /** -// * 根据部门ID列表获取「部门名称→部门ID」的映射(含空值防护) -// */ -// private Map getDeptListAll(List deptIds) { -// Map deptNameMap = new HashMap<>(); -// -// // 1. 空值安全:避免 deptIds 为 null/空列表导致 NPE -// if (CollectionUtils.isEmpty(deptIds)) { -// return deptNameMap; -// } -// -// for (Long deptId : deptIds) { -// // 2. 跳过 null 的部门ID -// if (deptId == null) { -// continue; -// } -// -// // 3. 获取子部门列表(防护接口返回 null) -// List deptList = deptApi.getChildDeptList(deptId); -// if (CollectionUtils.isEmpty(deptList)) { -// continue; -// } -// -// // 4. 遍历子部门:过滤无效名称,避免 Map 出现 null/空字符串键 -// for (DeptRespDTO dept : deptList) { -// if (dept == null) { -// continue; -// } -// String deptName = dept.getName(); -// // 过滤 null/空白名称(避免 Map 键为 null 或空字符串) -// if (deptName == null || deptName.trim().isEmpty()) { -// continue; -// } -// // 若名称重复,后遍历的部门会覆盖先遍历的(可根据业务调整规则) -// deptNameMap.put(deptName.trim(), dept.getId()); -// } -// } -// -// return deptNameMap; -// } -// /** -// * @Author wangqian -// * @Description 根据班组名称获取班组id集合 -// * @Date 2025/11/26 20:07 -// */ -// private List getDeptList(Map teamIdMap, String teamId) { -// List list = new ArrayList<>(); -// -// // 1. 处理 teamId 为空的情况,直接返回空列表 -// if (StringUtils.isEmpty(teamId)) { -// return list; -// } -// -// // 2. 分割字符串,处理空元素 -// String[] teamArray = teamId.split(","); -// for (String teamName : teamArray) { -// // 3. 去除首尾空格,避免因空格导致匹配不到 -// String trimTeamName = teamName.trim(); -// if (StringUtils.isEmpty(trimTeamName)) { -// continue; // 跳过空字符串元素 -// } -// -// // 4. 从Map中获取ID,处理null值(避免添加null到列表) -// Long deptId = teamIdMap.get(trimTeamName); -// if (deptId != null) { -// list.add(String.valueOf(deptId)); // 直接添加Long类型,修正原String类型错误 -// } -// } -// -// return list; -// } -//} + + // 4.2 批量插入主表 + personalCostMapper.insertBatch(saveFeeList); + } + log.info("成功插入 {} 人员费用数据", dataList.size()); + } else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { + //去重 + dataList = deptFeeReplacement((List)dataList); + List feeList = ((List) dataList).stream() + .map(importObj -> { + DepartmentCostDO fee = new DepartmentCostDO(); + BeanUtils.copyProperties(importObj, fee); + // 设置单位id + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); + return fee; + }) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(feeList)) { + //查询重复项,进行删除 + List ids = departmentCostMapper.selectDuplicateIds(feeList); + if (!CollectionUtils.isEmpty(ids)) { + departmentCostMapper.deleteByIds(ids); + } + // 调用批量插入方法 + departmentCostMapper.insertBatch(feeList); + } + log.info("成功插入 {} 综合费用数据", feeList.size()); + } else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { + List feeList = (List) dataList; + List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); + if (!CollectionUtils.isEmpty(feeList)) { + //获取班组对象 + Map teamIdMap = this.getDeptListAll(deptIds); + for (MechanicalCostImport importObj : feeList) { + MechanicalCostDO fee = new MechanicalCostDO(); + BeanUtils.copyProperties(importObj, fee); + // 设置单位id + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); + //设置班组id + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); + // 3.3 计算结束时间(空指针防护) + Optional.ofNullable(fee.getStartUseTime()) + .ifPresent(startTime -> { + int useDuration = Optional.ofNullable(fee.getServiceLife()) + .map(Math::toIntExact) // 处理可能的溢出 + .orElse(0); + fee.setEndUseTime(this.getEndLocalDateForYears(startTime, useDuration)); + }); + + saveFeeList.add(fee); + } + } + // 4. 批量插入主表(空集合直接跳过) + if (!feeList.isEmpty()) { + //查询重复项,进行删除 + List ids = mechanicalCostMapper.selectDuplicateIds(saveFeeList); + if (!CollectionUtils.isEmpty(ids)) { + mechanicalCostMapper.deleteByIds(ids); + } + mechanicalCostMapper.insertBatch(saveFeeList); + } + log.info("成功插入 {} 自有机械费用数据", dataList.size()); + } else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { + List feeList = (List)dataList; + List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); + if (!CollectionUtils.isEmpty(feeList)) { + //获取班组对象 + Map teamIdMap = this.getDeptListAll(deptIds); + for (RentalMechanicalCostImport importObj : feeList) { + RentalMechanicalCostDO fee = new RentalMechanicalCostDO(); + BeanUtils.copyProperties(importObj, fee); + // 设置单位id + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); + //设置班组id + List teamRels = new ArrayList<>(); + String teamIdsStr = StringUtils.defaultString(importObj.getTeamIds()).replace(",", ","); + String weightsStr = StringUtils.defaultString(importObj.getWeight()).replace(",", ","); + + String[] teamIds = teamIdsStr.split(","); + String[] weights = weightsStr.split(","); + for (int i = 0; i < teamIds.length; i++) { + TeamBizdomainRelVo rel = new TeamBizdomainRelVo(); + rel.setTeamName(teamIds[i].trim()); + // 提前处理部门ID映射(复用deptMap) + rel.setTeamId(Optional.ofNullable(teamIdMap.get(teamIds[i].trim())).map(Long::valueOf).orElse(null)); + rel.setWeight(Long.valueOf(weights[i].trim())); + teamRels.add(rel); + } + fee.setTeamIds(teamRels); + + // 3.3 计算结束时间(空指针防护) + Optional.ofNullable(fee.getStartUseTime()) + .ifPresent(startTime -> { + int useDuration = Optional.ofNullable(fee.getUseDuration()) + .map(Math::toIntExact) // 处理可能的溢出 + .orElse(0); + fee.setEndUseTime(getEndLocalDateForYears(startTime, useDuration)); + }); + + saveFeeList.add(fee); + } + } + if (!feeList.isEmpty()) { + rentalMechanicalCostMapper.insertBatch(saveFeeList); + } + log.info("成功插入 {} 租赁机械费用数据", dataList.size()); + } else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { + //获取班组对象 + List feeList = (List)dataList; + List saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size()); + if (!CollectionUtils.isEmpty(feeList)) { + Map teamIdMap = this.getDeptListAll(deptIds); + for (GardenWasteRecordImport importObj : feeList) { + WasteRecordDO fee = new WasteRecordDO(); + BeanUtils.copyProperties(importObj, fee); + // 设置单位id + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName()))); + //设置班组id + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds())); + + saveFeeList.add(fee); + } + } + // 4. 批量插入主表(若主表插入失败,事务回滚) + if (!feeList.isEmpty()) { + wasteRecordMapper.insertBatch(saveFeeList); + } + + log.info("成功插入 {} 园林废弃物费用数据", dataList.size()); + } + + } + + /** + * 从Redis获取分页数据 + */ + @Override + public CostFeeController.PageResult < T > getPagedDataFromRedis(String batchId, String templateName, + int page, int size, Class clazz) throws IOException { + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; + + // 获取总记录数 + String totalStr = redisTemplate.opsForValue().get(redisKey + ":total"); + int total = totalStr != null ? Integer.parseInt(totalStr) : 0; + + // 计算分页范围 + int start = (page - 1) * size; + int end = Math.min(start + size - 1, total - 1); + + // 分页查询数据 + List pageData = new ArrayList<>(); + if (start <= end && total > 0) { + BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); + + // 批量获取指定范围的索引对应的数据 + List indexes = IntStream.rangeClosed(start, end) + .mapToObj(String::valueOf) + .collect(Collectors.toList()); + List jsonList = hashOps.multiGet(indexes); + + // 反序列化为对象 + for (String json : jsonList) { + if (json != null) { + pageData.add(objectMapper.readValue(json, clazz)); + } + } + } + + return new CostFeeController.PageResult<>(page, size, total, pageData); + } + + + /** + * 从Redis获取全部数据 + */ + private List < T > getAllDataFromRedis(String batchId, String templateName, Class < T > clazz) throws + IOException { + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; + BoundHashOperations hashOps = redisTemplate.boundHashOps(redisKey + ":items"); + + // 获取所有数据的JSON字符串 + List jsonList = hashOps.values(); + List dataList = new ArrayList<>(jsonList.size()); + + // 反序列化为对象 + for (String json : jsonList) { + if (json != null) { + dataList.add(objectMapper.readValue(json, clazz)); + } + } + + return dataList; + } + + + /** + * 删除Redis临时数据 + */ + private void deleteRedisTempData (String batchId, String templateName){ + String redisKey = REDIS_KEY_PREFIX + templateName + ":" + batchId; + redisTemplate.delete(redisKey + ":total"); + redisTemplate.delete(redisKey + ":items"); + log.info("Redis临时数据已删除,批次ID:{}", batchId); + } + /** + * @Author wangqian + * @Description 导入内容校验 + * @Date 2025/11/24 8:40 + * @Param dataList + * @param template + * @Return void + */ + public void validateImportData(List dataList, TemplateEnum template) { + if (CollectionUtils.isEmpty(dataList)) { + return; // 空列表无需校验 + } + //登录用户单位 + Long belongCompanyId = getCompanyId(); + //单位集合 + List companyList = deptApi.getSubDeptList(); + List deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList()); + Map nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) + ? Map.of() // 空列表返回空不可变 Map(避免 null) + : companyList.stream() + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整) + .collect(Collectors.toMap( + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格) + DeptRespDTO::getId, // key 映射:取 companyId + (oldValue, newValue) -> newValue + )); + //班组集合 + Map> deptList = this.getDetpList(deptIds); + // 模板类型校验 + Class importClass = template.getImportClass(); // 从枚举获取导入类,避免switch + // 根据不同模板类型执行不同的处理逻辑 + if (WaterFeeImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + WaterFeeImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + // StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + //errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+ violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); + } + + } catch (ConstraintViolationException ex) { + // 5. 包装异常,明确行号 + 真实校验错误( + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + else if (PersonalCostImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + PersonalCostImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + //StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + // errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+ violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); + } + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); + //单位校验 + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); + //部门名称校验 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); + + } catch (ConstraintViolationException ex) { + // 5. 包装异常,明确行号 + 真实校验错误( + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + GardenWasteRecordImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + //StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + //errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+ violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); + } + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); + //单位校验 + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); + //部门名称校验 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); + + } catch (ConstraintViolationException ex) { + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + else if (MechanicalCostImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + MechanicalCostImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + //StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + //errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+ violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); + } + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); + //单位校验 + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); + //部门名称校验 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); + + //车辆校验 + if (CostFeeConstants.MECH_TYPE.equals(importBean.getMechType())) { + if (StringUtils.isEmpty(importBean.getLicensePlate())) { + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:车牌号不能为空" + ), rowNum); + } + if (StringUtils.isEmpty(importBean.getVehicleType())) { + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:车辆类型不能为空" + ), rowNum); + + } + if (StringUtils.isEmpty(importBean.getVehicleCategory())) { + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第%d条数据:车辆类别不能为空" + ), rowNum); + } + } + + } catch (ConstraintViolationException ex) { + // 5. 包装异常,明确行号 + 真实校验错误( + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + RentalMechanicalCostImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + // StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + // errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); + } + // 处理可能的空值 + String teamIdsStr = StringUtils.defaultString(importBean.getTeamIds()); + String weightsStr = StringUtils.defaultString(importBean.getWeight()); + + // 统一替换中文逗号并分割 + String[] teamIds = teamIdsStr.replace(",", ",").split(","); + String[] weights = weightsStr.replace(",", ",").split(","); + + // 校验数组长度是否一致 + if (teamIds.length != weights.length) { + throw exception0(BAD_REQUEST.getCode(), String.format( + "第%d条数据:权重值和所选班组数量不符(班组:%d个,权重:%d个)", + rowNum, teamIds.length, weights.length + )); + } + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); + //单位校验 + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); + //部门名称校验 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds()); + + } catch (ConstraintViolationException ex) { + // 5. 包装异常,明确行号 + 真实校验错误( + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + else if (DepartmentCostImport.class.isAssignableFrom(importClass)) { + + List importList = (List) dataList; + // 2. 遍历,逐个创建 + for (int i = 0; i < importList.size(); i++) { + int rowNum = i + 1; // 第i条数据 → 第rowNum行(对应Excel导入的行号逻辑) + DepartmentCostImport importBean = importList.get(i); + + try { + // 2.1 执行校验(替换原 ValidationUtils,直接用 Validator 校验,确保抛出 ConstraintViolationException) + Set> violations = validator.validate(importBean); + if (!violations.isEmpty()) { + // 收集所有校验错误信息(比如“用水量不能为空”“格式应为两位小数”) + //StringBuilder errorMsg = new StringBuilder(); + for (ConstraintViolation violation : violations) { + //errorMsg.append(violation.getMessage()).append(";"); + throw exception0(BAD_REQUEST.getCode() ,String.format( + "第[{}]条数据:"+violation.getMessage() + ), rowNum); + } + // 抛出包含行号和所有错误的异常 + //throw new ConstraintViolationException(errorMsg.toString().trim(), violations); + } + + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName()); + //单位校验 + validatecompany(rowNum, importBean.getCompanyName(), companyId, belongCompanyId); + } catch (ConstraintViolationException ex) { + // 5. 包装异常,明确行号 + 真实校验错误( + throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); + } + } + } + } + + private void validatecompany(int rowNum, String companyName, Long companyId, Long belongCompanyId){ + // 校验数组长度是否一致 + if (companyId == null) { + throw exception0(BAD_REQUEST.getCode(), String.format( + "第%d条数据:单位名称不正确(%s)", + rowNum, companyName + )); + } + // 校验导入单位 + if (belongCompanyId != 100 && !belongCompanyId.equals(companyId)) { + throw exception0(BAD_REQUEST.getCode(), String.format( + "第%d条数据:导入单位%s与当前操作员单位不一致", + rowNum, companyName + )); + } + } + private void validateTeams(int rowNum, List dept, String teamIds) { + if (StringUtils.isNotEmpty(teamIds)) { + String[] teamNames = teamIds.split(","); + Set checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复 + + for (String teamName : teamNames) { + String trimmedTeamName = teamName.trim(); // 处理前后空格 + + // 重复校验:如果Set中已存在该团队名称,说明重复 + if (checkedTeams.contains(trimmedTeamName)) { + throw exception0(BAD_REQUEST.getCode(), String.format( + "第%d条数据:%s名称重复", + rowNum, teamName + )); + } + + // 存在性校验:检查团队是否在dept中存在 + if (!dept.contains(trimmedTeamName)) { + throw exception0(BAD_REQUEST.getCode(), String.format( + "第%d条数据:%s名称不正确", + rowNum, teamName + )); + } + + // 将校验通过的团队名称加入Set,用于后续重复判断 + checkedTeams.add(trimmedTeamName); + } + } + } + + //查询单位下的班组信息 + private Map> getDetpList (List deptIds) { + Map> listMap = new HashMap<>(); + for (Long depId : deptIds) { + List deptList = deptApi.getChildDeptList(depId); + List names = deptList.stream().map(DeptRespDTO::getName).collect(Collectors.toList()); + listMap.put(depId,names); + } + return listMap; + } + + /** + * 按salaryMonth、expenseName、companyId三个字段去重 + * @param costList 原始部门成本列表 + * @return 去重后的列表 + */ + public List deptFeeReplacement(List costList) { + if (costList == null || costList.isEmpty()) { + return new ArrayList<>(); + } + + // 1. 以三个字段的组合作为键(使用分隔符避免不同组合产生相同键) + // 2. 遇到重复键时保留第一条记录 + Map uniqueMap = costList.stream() + .collect(Collectors.toMap( + // 生成唯一键:salaryMonth|expenseName|companyId + cost -> buildUniqueKey(cost), + // 键不存在时存入当前对象 + cost -> cost, + // 键已存在时保留最后一个 + (existing, replacement) -> replacement + )); + + // 将去重后的结果转为List返回 + return new ArrayList<>(uniqueMap.values()); + } + + /** + * 构建三个字段的唯一组合键(处理null值) + */ + private String buildUniqueKey(DepartmentCostImport cost) { + // 使用Objects.toString处理可能的null值,避免NullPointerException + return Objects.toString(cost.getSalaryMonth(), "") + "|" + + Objects.toString(cost.getExpenseName(), "") + "|" + + Objects.toString(cost.getCompanyName(), ""); + } + + /** + * 按salaryMonth、name、companyId、teamIdList四个字段去重 + * @param costList 原始个人成本列表 + * @return 去重后的列表 + */ + public List personalFeeReplacement(List costList) { + if (costList == null || costList.isEmpty()) { + return new ArrayList<>(); + } + + // 1. 以四个字段的组合作为键(特别处理集合类型的teamIdList) + // 2. 遇到重复键时保留第一条记录 + Map uniqueMap = costList.stream() + .collect(Collectors.toMap( + // 生成唯一键:salaryMonth|name|companyId|teamIdListStr + this::buildUniqueKey, + // 键不存在时存入当前对象 + cost -> cost, + // 键已存在时保留第一个对象(可改为replacement保留最后一个) + (existing, replacement) -> replacement + )); + + // 将去重后的结果转为List返回 + return new ArrayList<>(uniqueMap.values()); + } + + /** + * 构建四个字段的唯一组合键(处理null值和集合类型) + */ + private String buildUniqueKey(PersonalCostImport cost) { + // 处理普通字段的null值 + String salaryMonth = Objects.toString(cost.getSalaryMonth(), ""); + String name = Objects.toString(cost.getName(), ""); + String companyId = Objects.toString(cost.getCompanyName(), ""); + String teamIds = Objects.toString(cost.getTeamIds(), ""); + // 拼接唯一键 + return salaryMonth + "|" + name + "|" + companyId + "|" + teamIds; + } + + + public static LocalDate getEndLocalDateForYears(LocalDate startDate, int years) { + if (startDate == null) { + throw new IllegalArgumentException("开始日期不能为null"); + } + if (years < 0) { + throw new IllegalArgumentException("年数不能为负数"); + } + return startDate.plusYears(years); + } + + /** + * @Author wangqian + * @Description 根据班组名称获取班组id + * @Date 2025/11/26 19:38 + */ + /** + * 根据部门ID列表获取「部门名称→部门ID」的映射(含空值防护) + */ + private Map getDeptListAll(List deptIds) { + Map deptNameMap = new HashMap<>(); + + // 1. 空值安全:避免 deptIds 为 null/空列表导致 NPE + if (CollectionUtils.isEmpty(deptIds)) { + return deptNameMap; + } + + for (Long deptId : deptIds) { + // 2. 跳过 null 的部门ID + if (deptId == null) { + continue; + } + + // 3. 获取子部门列表(防护接口返回 null) + List deptList = deptApi.getChildDeptList(deptId); + if (CollectionUtils.isEmpty(deptList)) { + continue; + } + + // 4. 遍历子部门:过滤无效名称,避免 Map 出现 null/空字符串键 + for (DeptRespDTO dept : deptList) { + if (dept == null) { + continue; + } + String deptName = dept.getName(); + // 过滤 null/空白名称(避免 Map 键为 null 或空字符串) + if (deptName == null || deptName.trim().isEmpty()) { + continue; + } + // 若名称重复,后遍历的部门会覆盖先遍历的(可根据业务调整规则) + deptNameMap.put(deptName.trim(), dept.getId()); + } + } + + return deptNameMap; + } + /** + * @Author wangqian + * @Description 根据班组名称获取班组id集合 + * @Date 2025/11/26 20:07 + */ + private List getDeptList(Map teamIdMap, String teamId) { + List list = new ArrayList<>(); + + // 1. 处理 teamId 为空的情况,直接返回空列表 + if (StringUtils.isEmpty(teamId)) { + return list; + } + + // 2. 分割字符串,处理空元素 + String[] teamArray = teamId.split(","); + for (String teamName : teamArray) { + // 3. 去除首尾空格,避免因空格导致匹配不到 + String trimTeamName = teamName.trim(); + if (StringUtils.isEmpty(trimTeamName)) { + continue; // 跳过空字符串元素 + } + + // 4. 从Map中获取ID,处理null值(避免添加null到列表) + Long deptId = teamIdMap.get(trimTeamName); + if (deptId != null) { + list.add(deptId); // 直接添加Long类型,修正原String类型错误 + } + } + + return list; + } +} diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventory/MaterialInventoryServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventory/MaterialInventoryServiceImpl.java index 05d87b4..9667d84 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventory/MaterialInventoryServiceImpl.java +++ b/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 import com.zteits.urbanops.module.garden.controller.admin.materialinventory.vo.MaterialInventorySaveReqVO; import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; import com.zteits.urbanops.module.garden.dal.mysql.materialinventory.MaterialInventoryMapper; +import com.zteits.urbanops.module.system.api.dept.DeptApi; +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -19,6 +21,7 @@ import java.util.List; import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; /** @@ -33,10 +36,19 @@ public class MaterialInventoryServiceImpl implements MaterialInventoryService { @Resource private MaterialInventoryMapper materialInventoryMapper; + @Resource + private DeptApi deptApi; + @Override public Long createMaterialInventory(MaterialInventorySaveReqVO createReqVO) { // 插入 MaterialInventoryDO materialInventory = BeanUtils.toBean(createReqVO, MaterialInventoryDO.class); + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); + if(null == deptRespDTO){ + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); + } + materialInventory.setBelongCompanyId(deptRespDTO.getId()); + materialInventory.setBelongCompanyName(deptRespDTO.getName()); materialInventoryMapper.insert(materialInventory); // 返回 diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryin/MaterialInventoryInServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryin/MaterialInventoryInServiceImpl.java index bedf767..42ffc65 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryin/MaterialInventoryInServiceImpl.java +++ b/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; import com.zteits.urbanops.module.garden.dal.dataobject.materialinventory.MaterialInventoryDO; import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; +import com.zteits.urbanops.module.system.api.dept.DeptApi; +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; import org.springframework.stereotype.Service; import jakarta.annotation.Resource; import org.springframework.validation.annotation.Validated; @@ -25,8 +27,7 @@ import static com.zteits.urbanops.framework.common.exception.util.ServiceExcepti import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.convertList; import static com.zteits.urbanops.framework.common.util.collection.CollectionUtils.diffList; -import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getLoginUser; -import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getLoginUserDeptId; +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.*; import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; /** @@ -47,6 +48,9 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic @Resource private MaterialInventoryService materialInventoryService; + @Resource + private DeptApi deptApi; + @Override @Transactional public Long createMaterialInventoryIn(MaterialInventoryInSaveReqVO createReqVO) { @@ -55,6 +59,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic if(null == material){ throw exception0(BAD_REQUEST.getCode(), "无效参数-materialId"); } + DeptRespDTO deptRespDTO= deptApi.getDept(getCompanyId()); + if(null == deptRespDTO){ + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); + } MaterialInventoryInDO in = new MaterialInventoryInDO(); in.setInventoryInNo(System.currentTimeMillis()+ RandomUtil.randomInt(10, 100000)+""); in.setMaterialId(createReqVO.getMaterialId()); @@ -66,8 +74,10 @@ public class MaterialInventoryInServiceImpl implements MaterialInventoryInServic in.setTypeDetailId(material.getTypeDetailId()); in.setTypeDetailName(material.getTypeDetailName()); in.setSpecifications(material.getSpecifications()); - in.setBelongCompanyId(material.getBelongCompanyId()); - in.setBelongCompanyName(material.getBelongCompanyName()); +// in.setBelongCompanyId(material.getBelongCompanyId()); +// in.setBelongCompanyName(material.getBelongCompanyName()); + in.setBelongCompanyId(deptRespDTO.getId()); + in.setBelongCompanyName(deptRespDTO.getName()); in.setInInventoryNum(createReqVO.getInInventoryNum()); in.setMaterialPrice(createReqVO.getMaterialPrice()); in.setUnitId(material.getUnitId()); diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryout/MaterialInventoryOutServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryout/MaterialInventoryOutServiceImpl.java index 85aafba..e347c07 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/materialinventoryout/MaterialInventoryOutServiceImpl.java +++ b/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 import com.zteits.urbanops.module.garden.dal.mysql.material.MaterialMapper; import com.zteits.urbanops.module.garden.dal.mysql.materialinventoryout.MaterialInventoryOutMapper; import com.zteits.urbanops.module.garden.service.materialinventory.MaterialInventoryService; +import com.zteits.urbanops.module.system.api.dept.DeptApi; +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; @@ -19,6 +21,7 @@ import java.util.List; import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.*; /** @@ -37,6 +40,9 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ @Resource private MaterialInventoryService materialInventoryService; + + @Resource + private DeptApi deptApi; @Override public Long createMaterialInventoryOut(MaterialInventoryOutSaveReqVO createReqVO) { // 插入 @@ -54,7 +60,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ if(materialInventory.getCurrentNum() < out.getOutInventoryNum()){ throw exception0(BAD_REQUEST.getCode(),"当前库存不足,无法出库"); } - + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); + if(null == deptRespDTO){ + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); + } out.setMaterialName(material.getMaterialName()); out.setMaterialTypeId(material.getMaterialTypeId()); out.setMaterialTypeName(material.getMaterialTypeName()); @@ -63,8 +72,10 @@ public class MaterialInventoryOutServiceImpl implements MaterialInventoryOutServ out.setTypeDetailId(material.getTypeDetailId()); out.setTypeDetailName(material.getTypeDetailName()); out.setSpecifications(material.getSpecifications()); - out.setBelongCompanyId(material.getBelongCompanyId()); - out.setBelongCompanyName(material.getBelongCompanyName()); +// out.setBelongCompanyId(material.getBelongCompanyId()); +// out.setBelongCompanyName(material.getBelongCompanyName()); + out.setBelongCompanyId(deptRespDTO.getId()); + out.setBelongCompanyName(deptRespDTO.getName()); out.setUnitId(material.getUnitId()); out.setUnitName(material.getUnitName()); materialInventoryOutMapper.insert(out); diff --git a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/wasterecord/WasteRecordServiceImpl.java b/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/wasterecord/WasteRecordServiceImpl.java index 7046942..05b0aef 100644 --- a/urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/wasterecord/WasteRecordServiceImpl.java +++ b/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 import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.TeamBizdomainRelVo; import com.zteits.urbanops.module.garden.dal.dataobject.wasterecord.WasteRecordDO; import com.zteits.urbanops.module.garden.dal.mysql.wasterecord.WasteRecordMapper; +import com.zteits.urbanops.module.system.api.dept.DeptApi; +import com.zteits.urbanops.module.system.api.dept.dto.DeptRespDTO; import jakarta.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -15,7 +17,10 @@ import org.springframework.validation.annotation.Validated; import java.util.List; import java.util.stream.Collectors; +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST; import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception; +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0; +import static com.zteits.urbanops.framework.security.core.util.SecurityFrameworkUtils.getCompanyId; import static com.zteits.urbanops.module.garden.enums.ErrorCodeConstants.WASTE_RECORD_NOT_EXISTS; /** @@ -30,11 +35,19 @@ public class WasteRecordServiceImpl implements WasteRecordService { @Resource private WasteRecordMapper wasteRecordMapper; + @Resource + private DeptApi deptApi; @Override @Transactional public Long createWasteRecord(WasteRecordSaveReqVO createReqVO) { + DeptRespDTO deptRespDTO = deptApi.getDept(getCompanyId()); + if(null == deptRespDTO){ + throw exception0(BAD_REQUEST.getCode(), "无效参数-登录人所属单位为空"); + } // 插入 WasteRecordDO wasteRecord = BeanUtils.toBean(createReqVO, WasteRecordDO.class); + wasteRecord.setCompanyId(String.valueOf(deptRespDTO.getId())); + wasteRecord.setCompanyName(deptRespDTO.getName()); List deptIds = createReqVO.getTeamIds().stream() .map(TeamBizdomainRelVo::getTeamId) .collect(Collectors.toList());