Commit f461431bb42fda0f05633996bd9f716c889b0249

Authored by 王富生
1 parent 96792960

巡查计划明细及巡查计划创建优化

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