Commit 26d5a6727ceaa6ab385ed58e285e92d2013fb823

Authored by wangqian
1 parent b99550af

派单及人机材功能迁移

urbanops-framework/urbanops-spring-boot-starter-excel/src/main/java/com/zteits/urbanops/framework/excel/core/util/ExcelUtils.java
@@ -53,6 +53,7 @@ public class ExcelUtils { @@ -53,6 +53,7 @@ public class ExcelUtils {
53 return FastExcelFactory.read(file.getInputStream(), head, null) 53 return FastExcelFactory.read(file.getInputStream(), head, null)
54 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理 54 .autoCloseStream(false) // 不要自动关闭,交给 Servlet 自己处理
55 .headRowNumber(startIndex) 55 .headRowNumber(startIndex)
56 - .doReadAllSync(); 56 + .sheet(0)
  57 + .doReadSync();
57 } 58 }
58 } 59 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/controller/admin/costfee/CostFeeController.java
@@ -6,6 +6,7 @@ import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImp @@ -6,6 +6,7 @@ import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.WaterFeeImp
6 import com.zteits.urbanops.module.garden.enums.TemplateEnum; 6 import com.zteits.urbanops.module.garden.enums.TemplateEnum;
7 import com.zteits.urbanops.module.garden.service.costfee.CostFeeService; 7 import com.zteits.urbanops.module.garden.service.costfee.CostFeeService;
8 import io.swagger.v3.oas.annotations.Operation; 8 import io.swagger.v3.oas.annotations.Operation;
  9 +import io.swagger.v3.oas.annotations.tags.Tag;
9 import jakarta.servlet.http.HttpServletResponse; 10 import jakarta.servlet.http.HttpServletResponse;
10 import lombok.AllArgsConstructor; 11 import lombok.AllArgsConstructor;
11 import lombok.Data; 12 import lombok.Data;
@@ -32,6 +33,7 @@ import static com.zteits.urbanops.framework.common.pojo.CommonResult.success; @@ -32,6 +33,7 @@ import static com.zteits.urbanops.framework.common.pojo.CommonResult.success;
32 * 人机材公共处理控制器 33 * 人机材公共处理控制器
33 * 处理Excel导入预览、提交等功能 34 * 处理Excel导入预览、提交等功能
34 */ 35 */
  36 +@Tag(name = "管理后台 - 人机材批量导入")
35 @RestController 37 @RestController
36 @RequestMapping("/costfee") 38 @RequestMapping("/costfee")
37 @Slf4j 39 @Slf4j
@@ -100,6 +102,7 @@ public class CostFeeController{ @@ -100,6 +102,7 @@ public class CostFeeController{
100 * Excel文件上传预览 102 * Excel文件上传预览
101 */ 103 */
102 @PostMapping("/preview") 104 @PostMapping("/preview")
  105 + @Operation(summary = "文件上传")
103 public CommonResult previewExcel(@RequestParam("file") MultipartFile file, 106 public CommonResult previewExcel(@RequestParam("file") MultipartFile file,
104 @RequestParam("templateName") String templateName) { 107 @RequestParam("templateName") String templateName) {
105 // 基础参数校验 108 // 基础参数校验
@@ -145,6 +148,7 @@ public class CostFeeController{ @@ -145,6 +148,7 @@ public class CostFeeController{
145 * 分页获取预览数据 148 * 分页获取预览数据
146 */ 149 */
147 @GetMapping("/preview-data") 150 @GetMapping("/preview-data")
  151 + @Operation(summary = "批量上传分页预览")
148 public CommonResult getPreviewData(@RequestParam("batchId") String batchId, 152 public CommonResult getPreviewData(@RequestParam("batchId") String batchId,
149 @RequestParam("templateName") String templateName, 153 @RequestParam("templateName") String templateName,
150 @RequestParam(value = "page", defaultValue = "1") int page, 154 @RequestParam(value = "page", defaultValue = "1") int page,
@@ -186,6 +190,7 @@ public class CostFeeController{ @@ -186,6 +190,7 @@ public class CostFeeController{
186 * 提交导入数据到数据库 190 * 提交导入数据到数据库
187 */ 191 */
188 @PostMapping("/submit") 192 @PostMapping("/submit")
  193 + @Operation(summary = "批次保存")
189 public CommonResult submitImport(@RequestParam("batchId") String batchId, 194 public CommonResult submitImport(@RequestParam("batchId") String batchId,
190 @RequestParam("templateName") String templateName, 195 @RequestParam("templateName") String templateName,
191 @RequestParam("operator") String operator) { 196 @RequestParam("operator") String operator) {
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/departmentcost/DepartmentCostMapper.java
@@ -31,5 +31,12 @@ public interface DepartmentCostMapper extends BaseMapperX<DepartmentCostDO> { @@ -31,5 +31,12 @@ public interface DepartmentCostMapper extends BaseMapperX<DepartmentCostDO> {
31 .betweenIfPresent(DepartmentCostDO::getCreateTime, reqVO.getCreateTime()) 31 .betweenIfPresent(DepartmentCostDO::getCreateTime, reqVO.getCreateTime())
32 .orderByDesc(DepartmentCostDO::getId)); 32 .orderByDesc(DepartmentCostDO::getId));
33 } 33 }
  34 + /**
  35 + * 批量查询重复数据
  36 + *
  37 + * @param keyList 部门信息集合
  38 + * @return 结果
  39 + */
  40 + public List<Long> selectDuplicateIds(List<DepartmentCostDO> keyList);
34 41
35 -}  
36 \ No newline at end of file 42 \ No newline at end of file
  43 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/mechanicalcost/MechanicalCostMapper.java
@@ -39,4 +39,5 @@ public interface MechanicalCostMapper extends BaseMapperX&lt;MechanicalCostDO&gt; { @@ -39,4 +39,5 @@ public interface MechanicalCostMapper extends BaseMapperX&lt;MechanicalCostDO&gt; {
39 .orderByDesc(MechanicalCostDO::getId)); 39 .orderByDesc(MechanicalCostDO::getId));
40 } 40 }
41 41
42 -}  
43 \ No newline at end of file 42 \ No newline at end of file
  43 + public List<Long> selectDuplicateIds(List<MechanicalCostDO> keyList);
  44 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/personalcost/PersonalCostMapper.java
@@ -32,4 +32,13 @@ public interface PersonalCostMapper extends BaseMapperX&lt;PersonalCostDO&gt; { @@ -32,4 +32,13 @@ public interface PersonalCostMapper extends BaseMapperX&lt;PersonalCostDO&gt; {
32 .orderByDesc(PersonalCostDO::getId)); 32 .orderByDesc(PersonalCostDO::getId));
33 } 33 }
34 34
35 -}  
36 \ No newline at end of file 35 \ No newline at end of file
  36 +
  37 + /**
  38 + * 批量查询重复数据
  39 + *
  40 + * @param keyList 人员费用信息集合
  41 + * @return 结果
  42 + */
  43 + public List<Long> selectDuplicateIds(List<PersonalCostDO> keyList);
  44 +
  45 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/dal/mysql/waterfee/WaterFeeMapper.java
@@ -32,4 +32,12 @@ public interface WaterFeeMapper extends BaseMapperX&lt;WaterFeeDO&gt; { @@ -32,4 +32,12 @@ public interface WaterFeeMapper extends BaseMapperX&lt;WaterFeeDO&gt; {
32 .orderByDesc(WaterFeeDO::getId)); 32 .orderByDesc(WaterFeeDO::getId));
33 } 33 }
34 34
35 -}  
36 \ No newline at end of file 35 \ No newline at end of file
  36 +
  37 + /**
  38 + * 批量查询重复数据
  39 + *
  40 + * @param keyList 水费录入信息集合
  41 + * @return 结果
  42 + */
  43 + public List<Long> selectDuplicateIds(List<WaterFeeDO> keyList);
  44 +}
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/enums/TemplateEnum.java
@@ -7,6 +7,9 @@ import lombok.Getter; @@ -7,6 +7,9 @@ import lombok.Getter;
7 import java.util.HashMap; 7 import java.util.HashMap;
8 import java.util.Map; 8 import java.util.Map;
9 9
  10 +import static com.zteits.urbanops.framework.common.exception.enums.GlobalErrorCodeConstants.BAD_REQUEST;
  11 +import static com.zteits.urbanops.framework.common.exception.util.ServiceExceptionUtil.exception0;
  12 +
10 /** 13 /**
11 * Excel导入模板枚举 14 * Excel导入模板枚举
12 * 存储模板编码、名称、起始行索引及对应的数据导入类 15 * 存储模板编码、名称、起始行索引及对应的数据导入类
@@ -29,35 +32,35 @@ public enum TemplateEnum { @@ -29,35 +32,35 @@ public enum TemplateEnum {
29 2, 32 2,
30 PersonalCostImport.class, 33 PersonalCostImport.class,
31 true // 新增默认值 34 true // 新增默认值
  35 + ),
  36 + COSTFEE_DEPARTMENT(
  37 + "costFee_department",
  38 + "综合费用导入模板.xlsx",
  39 + 2,
  40 + DepartmentCostImport.class,
  41 + true // 新增默认值
  42 + ),
  43 + COSTFEE_GARDENWASTE(
  44 + "costFee_gardenWaste",
  45 + "园林废弃物费用导入模板.xlsx",
  46 + 2,
  47 + GardenWasteRecordImport.class,
  48 + true // 新增默认值
  49 + ),
  50 + COSTFEE_MECHANICAL(
  51 + "costFee_mechanical",
  52 + "自有机械费用导入模板.xlsx",
  53 + 2,
  54 + MechanicalCostImport.class,
  55 + true // 新增默认值
  56 + ),
  57 + COSTFEE_RENTALMECHANICAL(
  58 + "costFee_rentalmechanical",
  59 + "租赁机械费用导入模板.xlsx",
  60 + 2,
  61 + RentalMechanicalCostImport.class,
  62 + true // 新增默认值
32 ); 63 );
33 -// COSTFEE_DEPARTMENT(  
34 -// "costFee_department",  
35 -// "综合费用导入模板.xlsx",  
36 -// 1,  
37 -// DepartmentCostImport.class,  
38 -// true // 新增默认值  
39 -// ),  
40 -// COSTFEE_GARDENWASTE(  
41 -// "costFee_gardenWaste",  
42 -// "园林废弃物费用导入模板.xlsx",  
43 -// 1,  
44 -// GardenWasteRecordImport.class,  
45 -// true // 新增默认值  
46 -// ),  
47 -// COSTFEE_MECHANICAL(  
48 -// "costFee_mechanical",  
49 -// "自有机械费用导入模板.xlsx",  
50 -// 1,  
51 -// MechanicalCostImport.class,  
52 -// true // 新增默认值  
53 -// ),  
54 -// COSTFEE_RENTALMECHANICAL(  
55 -// "costFee_rentalmechanical",  
56 -// "租赁机械费用导入模板.xlsx",  
57 -// 1,  
58 -// RentalMechanicalCostImport.class,  
59 -// true // 新增默认值  
60 -// );  
61 64
62 // 模板编码(前端传递的标识) 65 // 模板编码(前端传递的标识)
63 private final String code; 66 private final String code;
@@ -86,12 +89,12 @@ public enum TemplateEnum { @@ -86,12 +89,12 @@ public enum TemplateEnum {
86 public static TemplateEnum getByCode(String code) { 89 public static TemplateEnum getByCode(String code) {
87 // 空值校验 90 // 空值校验
88 if (code == null || code.trim().isEmpty()) { 91 if (code == null || code.trim().isEmpty()) {
89 - throw new RuntimeException("模板编码不能为空"); 92 + throw exception0(BAD_REQUEST.getCode(),"模板编码不能为空");
90 } 93 }
91 // 从缓存获取,避免循环遍历 94 // 从缓存获取,避免循环遍历
92 TemplateEnum template = CODE_MAP.get(code); 95 TemplateEnum template = CODE_MAP.get(code);
93 if (template == null) { 96 if (template == null) {
94 - throw new RuntimeException("无效的模板编码:" + code); 97 + throw exception0(BAD_REQUEST.getCode(),"无效的模板编码"+code);
95 } 98 }
96 return template; 99 return template;
97 } 100 }
urbanops-module-garden/src/main/java/com/zteits/urbanops/module/garden/service/costfee/CostFeeServiceImpl.java
@@ -5,6 +5,18 @@ import com.fasterxml.jackson.databind.ObjectMapper; @@ -5,6 +5,18 @@ import com.fasterxml.jackson.databind.ObjectMapper;
5 import com.zteits.urbanops.framework.security.core.LoginUser; 5 import com.zteits.urbanops.framework.security.core.LoginUser;
6 import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController; 6 import com.zteits.urbanops.module.garden.controller.admin.costfee.CostFeeController;
7 import com.zteits.urbanops.module.garden.controller.admin.costfee.vo.*; 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;
8 import com.zteits.urbanops.module.garden.enums.AssignTasksConstants; 20 import com.zteits.urbanops.module.garden.enums.AssignTasksConstants;
9 import com.zteits.urbanops.module.garden.enums.CostFeeConstants; 21 import com.zteits.urbanops.module.garden.enums.CostFeeConstants;
10 import com.zteits.urbanops.module.garden.enums.TemplateEnum; 22 import com.zteits.urbanops.module.garden.enums.TemplateEnum;
@@ -16,6 +28,7 @@ import jakarta.validation.ConstraintViolationException; @@ -16,6 +28,7 @@ import jakarta.validation.ConstraintViolationException;
16 import jakarta.validation.Validator; 28 import jakarta.validation.Validator;
17 import lombok.extern.slf4j.Slf4j; 29 import lombok.extern.slf4j.Slf4j;
18 import org.apache.commons.lang3.StringUtils; 30 import org.apache.commons.lang3.StringUtils;
  31 +import org.springframework.beans.BeanUtils;
19 import org.springframework.beans.factory.annotation.Autowired; 32 import org.springframework.beans.factory.annotation.Autowired;
20 import org.springframework.data.redis.core.BoundHashOperations; 33 import org.springframework.data.redis.core.BoundHashOperations;
21 import org.springframework.data.redis.core.RedisTemplate; 34 import org.springframework.data.redis.core.RedisTemplate;
@@ -24,8 +37,10 @@ import org.springframework.transaction.annotation.Transactional; @@ -24,8 +37,10 @@ import org.springframework.transaction.annotation.Transactional;
24 import org.springframework.util.CollectionUtils; 37 import org.springframework.util.CollectionUtils;
25 38
26 import java.io.IOException; 39 import java.io.IOException;
  40 +import java.time.LocalDate;
27 import java.util.*; 41 import java.util.*;
28 import java.util.concurrent.TimeUnit; 42 import java.util.concurrent.TimeUnit;
  43 +import java.util.function.Function;
29 import java.util.stream.Collectors; 44 import java.util.stream.Collectors;
30 import java.util.stream.IntStream; 45 import java.util.stream.IntStream;
31 46
@@ -59,6 +74,20 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -59,6 +74,20 @@ public class CostFeeServiceImpl implements CostFeeService {
59 @Resource 74 @Resource
60 private DeptApi deptApi; 75 private DeptApi deptApi;
61 76
  77 + @Autowired
  78 + private WaterFeeMapper waterFeeMapper;
  79 + @Autowired
  80 + private PersonalCostMapper personalCostMapper;
  81 + @Autowired
  82 + private DepartmentCostMapper departmentCostMapper;
  83 + @Autowired
  84 + private MechanicalCostMapper mechanicalCostMapper;
  85 + @Autowired
  86 + private RentalMechanicalCostMapper rentalMechanicalCostMapper;
  87 + @Autowired
  88 + private WasteRecordMapper wasteRecordMapper;
  89 +
  90 +
62 @Override 91 @Override
63 /** 92 /**
64 * 保存数据到Redis 93 * 保存数据到Redis
@@ -99,7 +128,7 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -99,7 +128,7 @@ public class CostFeeServiceImpl implements CostFeeService {
99 TemplateEnum template = TemplateEnum.getByCode(templateName); 128 TemplateEnum template = TemplateEnum.getByCode(templateName);
100 Class<?> importClass = template.getImportClass(); 129 Class<?> importClass = template.getImportClass();
101 if (importClass == null) { 130 if (importClass == null) {
102 - throw exception(COST_FEE_NOT_EXISTS_001); 131 + throw exception0(BAD_REQUEST.getCode(),"导入模板配置异常");
103 } 132 }
104 133
105 // 分页参数设置 134 // 分页参数设置
@@ -118,7 +147,7 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -118,7 +147,7 @@ public class CostFeeServiceImpl implements CostFeeService {
118 if (dataList == null || dataList.isEmpty()) { 147 if (dataList == null || dataList.isEmpty()) {
119 // 首次分页就无数据,抛出异常 148 // 首次分页就无数据,抛出异常
120 if (pageNum == 1) { 149 if (pageNum == 1) {
121 - throw new IllegalArgumentException("没有可导入的数据或数据已过期"); 150 + throw exception0(BAD_REQUEST.getCode(),"没有可导入的数据或数据已过期");
122 } 151 }
123 break; 152 break;
124 } 153 }
@@ -128,7 +157,7 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -128,7 +157,7 @@ public class CostFeeServiceImpl implements CostFeeService {
128 157
129 // 调用业务服务执行本页数据导入 158 // 调用业务服务执行本页数据导入
130 int currentPageCount = dataList.size(); 159 int currentPageCount = dataList.size();
131 - //insertData(template, dataList, operator); 160 + insertData(template, dataList, operator);
132 totalImported += currentPageCount; 161 totalImported += currentPageCount;
133 162
134 // 打印分页处理日志(可选) 163 // 打印分页处理日志(可选)
@@ -152,6 +181,243 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -152,6 +181,243 @@ public class CostFeeServiceImpl implements CostFeeService {
152 181
153 return totalImported; 182 return totalImported;
154 } 183 }
  184 + /**
  185 + * @Author wangqian
  186 + * @Description 保存数据
  187 + * @Date 2025/7/20 11:30
  188 + * @Param template
  189 + * @param dataList
  190 + * @param operator
  191 + * @Return void
  192 + */
  193 + private void insertData(TemplateEnum template, List<?> dataList, String operator) {
  194 + if (CollectionUtils.isEmpty(dataList)) {
  195 + log.info("数据列表为空,跳过插入操作");
  196 + return;
  197 + }
  198 + //单位集合
  199 + List<DeptRespDTO> companyList = deptApi.getSubDeptList();
  200 + // 1. 预加载部门映射(循环外加载,避免重复查询)
  201 + List<Long> deptIds = companyList.stream().map(DeptRespDTO::getId).collect(Collectors.toList());
  202 + Map<String, Long> nameToCompanyIdMap = CollectionUtils.isEmpty(companyList) ? Map.of()
  203 + : companyList.stream()
  204 + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整)
  205 + .collect(Collectors.toMap(
  206 + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格)
  207 + DeptRespDTO::getId, // key 映射:取 companyId
  208 + (oldValue, newValue) -> newValue
  209 + ));
  210 +
  211 +
  212 + // 模板类型校验
  213 + Class<?> importClass = template.getImportClass(); // 从枚举获取导入类,避免switch
  214 + // 根据不同模板类型执行不同的处理逻辑
  215 + if (WaterFeeImport.class.isAssignableFrom(importClass)) {
  216 + Map<Long, String> companyIdToNamedMap = CollectionUtils.isEmpty(companyList) ? Map.of()
  217 + : companyList.stream()
  218 + // 转 Map:key=companyId,value=companyName;key 冲突时保留最后一个(可调整)
  219 + .collect(Collectors.toMap(
  220 + DeptRespDTO::getId, // key 映射:取 companyId
  221 + dept -> dept.getName().trim(), // value 映射:取 companyName(去空格)
  222 + (oldValue, newValue) -> newValue
  223 + ));
  224 +
  225 + // 处理水费数据
  226 + List<WaterFeeDO> feeList = ((List<WaterFeeImport>) dataList).stream()
  227 + .map(importObj -> {
  228 + WaterFeeDO fee = new WaterFeeDO();
  229 + BeanUtils.copyProperties(importObj, fee);
  230 + fee.setCompanyId(String.valueOf(getLoginUserDeptId()));
  231 + if (getLoginUserDeptId() == 100) {
  232 + fee.setCompanyName("蓟城山水");
  233 + } else {
  234 + // 设置单位id
  235 + fee.setCompanyName(String.valueOf(companyIdToNamedMap.get(fee.getCompanyId())));
  236 + }
  237 + return fee;
  238 + })
  239 + .collect(Collectors.toMap(
  240 + // 生成唯一键:feeMonth + 分隔符 + meterCode(避免不同组合拼接后重复)
  241 + fee -> fee.getFeeMonth() + "_" + fee.getMeterCode(),
  242 + // 值为对象本身
  243 + Function.identity(),
  244 + // 当键重复时,保留第一个出现的对象(可根据需求改为保留最后一个:(existing, replacement) -> replacement)
  245 + (existing, replacement) -> replacement
  246 + ))
  247 + .values()
  248 + .stream()
  249 + .collect(Collectors.toList());
  250 +
  251 + if (!CollectionUtils.isEmpty(feeList)) {
  252 + //查询重复项,进行删除
  253 + List<Long> ids = waterFeeMapper.selectDuplicateIds(feeList);
  254 + if (!CollectionUtils.isEmpty(ids)) {
  255 + waterFeeMapper.deleteByIds(ids);
  256 + }
  257 + // 调用批量插入方法
  258 + waterFeeMapper.insertBatch(feeList);
  259 + }
  260 + log.info("成功插入 {} 条水费数据", feeList.size());
  261 + } else if (PersonalCostImport.class.isAssignableFrom(importClass)) {
  262 + //去重
  263 + dataList = personalFeeReplacement((List<PersonalCostImport>) dataList);
  264 + //获取班组对象
  265 + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds);
  266 +
  267 + // 3. 转换导入数据为PersonalCost列表(Stream优化+空指针防护)
  268 + List<PersonalCostImport> feeList = (List<PersonalCostImport>)dataList;
  269 + List<PersonalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size());
  270 + if (!CollectionUtils.isEmpty(feeList)) {
  271 + for (PersonalCostImport importObj : feeList) {
  272 + PersonalCostDO fee = new PersonalCostDO();
  273 + BeanUtils.copyProperties(importObj, fee);
  274 + // 设置单位id
  275 + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName())));
  276 + //设置班组id
  277 + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds()));
  278 + saveFeeList.add(fee);
  279 + }
  280 + }
  281 + // 4. 处理重复数据(删除+插入原子性)
  282 + if (!feeList.isEmpty()) {
  283 + // 4.1 查询并删除重复项
  284 +
  285 +// List<Long> duplicateIds = personalCostMapper.selectDuplicateIds(saveFeeList);
  286 +// if (!duplicateIds.isEmpty()) {
  287 +// personalCostMapper.deleteByIds(duplicateIds);
  288 +// }
  289 +
  290 + // 4.2 批量插入主表
  291 + personalCostMapper.insertBatch(saveFeeList);
  292 + }
  293 + log.info("成功插入 {} 人员费用数据", dataList.size());
  294 + } else if (DepartmentCostImport.class.isAssignableFrom(importClass)) {
  295 + //去重
  296 + dataList = deptFeeReplacement((List<DepartmentCostImport>)dataList);
  297 + List<DepartmentCostDO> feeList = ((List<DepartmentCostImport>) dataList).stream()
  298 + .map(importObj -> {
  299 + DepartmentCostDO fee = new DepartmentCostDO();
  300 + BeanUtils.copyProperties(importObj, fee);
  301 + // 设置单位id
  302 + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName())));
  303 + return fee;
  304 + })
  305 + .collect(Collectors.toList());
  306 + if (!CollectionUtils.isEmpty(feeList)) {
  307 + //查询重复项,进行删除
  308 + List<Long> ids = departmentCostMapper.selectDuplicateIds(feeList);
  309 + if (!CollectionUtils.isEmpty(ids)) {
  310 + departmentCostMapper.deleteByIds(ids);
  311 + }
  312 + // 调用批量插入方法
  313 + departmentCostMapper.insertBatch(feeList);
  314 + }
  315 + log.info("成功插入 {} 综合费用数据", feeList.size());
  316 + } else if (MechanicalCostImport.class.isAssignableFrom(importClass)) {
  317 + List<MechanicalCostImport> feeList = (List<MechanicalCostImport>) dataList;
  318 + List<MechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size());
  319 + if (!CollectionUtils.isEmpty(feeList)) {
  320 + //获取班组对象
  321 + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds);
  322 + for (MechanicalCostImport importObj : feeList) {
  323 + MechanicalCostDO fee = new MechanicalCostDO();
  324 + BeanUtils.copyProperties(importObj, fee);
  325 + // 设置单位id
  326 + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName())));
  327 + //设置班组id
  328 + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds()));
  329 + // 3.3 计算结束时间(空指针防护)
  330 + Optional.ofNullable(fee.getStartUseTime())
  331 + .ifPresent(startTime -> {
  332 + int useDuration = Optional.ofNullable(fee.getServiceLife())
  333 + .map(Math::toIntExact) // 处理可能的溢出
  334 + .orElse(0);
  335 + fee.setEndUseTime(this.getEndLocalDateForYears(startTime, useDuration));
  336 + });
  337 +
  338 + saveFeeList.add(fee);
  339 + }
  340 + }
  341 + // 4. 批量插入主表(空集合直接跳过)
  342 + if (!feeList.isEmpty()) {
  343 + //查询重复项,进行删除
  344 + List<Long> ids = mechanicalCostMapper.selectDuplicateIds(saveFeeList);
  345 + if (!CollectionUtils.isEmpty(ids)) {
  346 + mechanicalCostMapper.deleteByIds(ids);
  347 + }
  348 + mechanicalCostMapper.insertBatch(saveFeeList);
  349 + }
  350 + log.info("成功插入 {} 自有机械费用数据", dataList.size());
  351 + } else if (RentalMechanicalCostImport.class.isAssignableFrom(importClass)) {
  352 + List<RentalMechanicalCostImport> feeList = (List<RentalMechanicalCostImport>)dataList;
  353 + List<RentalMechanicalCostDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size());
  354 + if (!CollectionUtils.isEmpty(feeList)) {
  355 + //获取班组对象
  356 + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds);
  357 + for (RentalMechanicalCostImport importObj : feeList) {
  358 + RentalMechanicalCostDO fee = new RentalMechanicalCostDO();
  359 + BeanUtils.copyProperties(importObj, fee);
  360 + // 设置单位id
  361 + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName())));
  362 + //设置班组id
  363 + List<TeamBizdomainRelVo> teamRels = new ArrayList<>();
  364 + String teamIdsStr = StringUtils.defaultString(importObj.getTeamIds()).replace(",", ",");
  365 + String weightsStr = StringUtils.defaultString(importObj.getWeight()).replace(",", ",");
  366 +
  367 + String[] teamIds = teamIdsStr.split(",");
  368 + String[] weights = weightsStr.split(",");
  369 + for (int i = 0; i < teamIds.length; i++) {
  370 + TeamBizdomainRelVo rel = new TeamBizdomainRelVo();
  371 + rel.setTeamName(teamIds[i].trim());
  372 + // 提前处理部门ID映射(复用deptMap)
  373 + rel.setTeamId(Optional.ofNullable(teamIdMap.get(teamIds[i].trim())).map(String::valueOf).orElse(null));
  374 + rel.setWeight(Long.valueOf(weights[i].trim()));
  375 + teamRels.add(rel);
  376 + }
  377 + fee.setTeamIds(teamRels);
  378 +
  379 + // 3.3 计算结束时间(空指针防护)
  380 + Optional.ofNullable(fee.getStartUseTime())
  381 + .ifPresent(startTime -> {
  382 + int useDuration = Optional.ofNullable(fee.getUseDuration())
  383 + .map(Math::toIntExact) // 处理可能的溢出
  384 + .orElse(0);
  385 + fee.setEndUseTime(getEndLocalDateForYears(startTime, useDuration));
  386 + });
  387 +
  388 + saveFeeList.add(fee);
  389 + }
  390 + }
  391 + if (!feeList.isEmpty()) {
  392 + rentalMechanicalCostMapper.insertBatch(saveFeeList);
  393 + }
  394 + log.info("成功插入 {} 租赁机械费用数据", dataList.size());
  395 + } else if (GardenWasteRecordImport.class.isAssignableFrom(importClass)) {
  396 + //获取班组对象
  397 + List<GardenWasteRecordImport> feeList = (List<GardenWasteRecordImport>)dataList;
  398 + List<WasteRecordDO> saveFeeList = new ArrayList<>(CollectionUtils.isEmpty(feeList) ? 0 : feeList.size());
  399 + if (!CollectionUtils.isEmpty(feeList)) {
  400 + Map<String, Long> teamIdMap = this.getDeptListAll(deptIds);
  401 + for (GardenWasteRecordImport importObj : feeList) {
  402 + WasteRecordDO fee = new WasteRecordDO();
  403 + BeanUtils.copyProperties(importObj, fee);
  404 + // 设置单位id
  405 + fee.setCompanyId(String.valueOf(nameToCompanyIdMap.get(fee.getCompanyName())));
  406 + //设置班组id
  407 + fee.setDeptId(getDeptList(teamIdMap, importObj.getTeamIds()));
  408 +
  409 + saveFeeList.add(fee);
  410 + }
  411 + }
  412 + // 4. 批量插入主表(若主表插入失败,事务回滚)
  413 + if (!feeList.isEmpty()) {
  414 + wasteRecordMapper.insertBatch(saveFeeList);
  415 + }
  416 +
  417 + log.info("成功插入 {} 园林废弃物费用数据", dataList.size());
  418 + }
  419 +
  420 + }
155 421
156 /** 422 /**
157 * 从Redis获取分页数据 423 * 从Redis获取分页数据
@@ -309,6 +575,11 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -309,6 +575,11 @@ public class CostFeeServiceImpl implements CostFeeService {
309 // 抛出包含行号和所有错误的异常 575 // 抛出包含行号和所有错误的异常
310 // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); 576 // throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations);
311 } 577 }
  578 + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName());
  579 + //单位校验
  580 + validatecompany(rowNum, importBean.getCompanyName(), companyId, deptId);
  581 + //部门名称校验
  582 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds());
312 583
313 } catch (ConstraintViolationException ex) { 584 } catch (ConstraintViolationException ex) {
314 // 5. 包装异常,明确行号 + 真实校验错误( 585 // 5. 包装异常,明确行号 + 真实校验错误(
@@ -339,7 +610,11 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -339,7 +610,11 @@ public class CostFeeServiceImpl implements CostFeeService {
339 // 抛出包含行号和所有错误的异常 610 // 抛出包含行号和所有错误的异常
340 //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations); 611 //throw exception0(BAD_REQUEST.getCode(), errorMsg.toString().trim(), violations);
341 } 612 }
342 - 613 + Long companyId = nameToCompanyIdMap.get(importBean.getCompanyName());
  614 + //单位校验
  615 + validatecompany(rowNum, importBean.getCompanyName(), companyId, deptId);
  616 + //部门名称校验
  617 + validateTeams(rowNum, deptList.get(companyId), importBean.getTeamIds());
343 618
344 } catch (ConstraintViolationException ex) { 619 } catch (ConstraintViolationException ex) {
345 throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex); 620 throw exception0(BAD_REQUEST.getCode(), String.format("第%d条数据校验失败:%s", rowNum, ex.getMessage()), ex);
@@ -501,30 +776,32 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -501,30 +776,32 @@ public class CostFeeServiceImpl implements CostFeeService {
501 } 776 }
502 } 777 }
503 private void validateTeams(int rowNum, List<String> dept, String teamIds) { 778 private void validateTeams(int rowNum, List<String> dept, String teamIds) {
504 - String[] teamNames = teamIds.split(",");  
505 - Set<String> checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复  
506 -  
507 - for (String teamName : teamNames) {  
508 - String trimmedTeamName = teamName.trim(); // 处理前后空格 779 + if (StringUtils.isNotEmpty(teamIds)) {
  780 + String[] teamNames = teamIds.split(",");
  781 + Set<String> checkedTeams = new HashSet<>(); // 用于存储已校验的团队名称,检测重复
  782 +
  783 + for (String teamName : teamNames) {
  784 + String trimmedTeamName = teamName.trim(); // 处理前后空格
  785 +
  786 + // 重复校验:如果Set中已存在该团队名称,说明重复
  787 + if (checkedTeams.contains(trimmedTeamName)) {
  788 + throw exception0(BAD_REQUEST.getCode(), String.format(
  789 + "第%d条数据:%s名称重复",
  790 + rowNum, teamName
  791 + ));
  792 + }
509 793
510 - // 重复校验:如果Set中已存在该团队名称,说明重复  
511 - if (checkedTeams.contains(trimmedTeamName)) {  
512 - throw exception0(BAD_REQUEST.getCode(), String.format(  
513 - "第%d条数据:%s名称重复",  
514 - rowNum, teamName  
515 - ));  
516 - } 794 + // 存在性校验:检查团队是否在dept中存在
  795 + if (!dept.contains(trimmedTeamName)) {
  796 + throw exception0(BAD_REQUEST.getCode(), String.format(
  797 + "第%d条数据:%s名称不正确",
  798 + rowNum, teamName
  799 + ));
  800 + }
517 801
518 - // 存在性校验:检查团队是否在dept中存在  
519 - if (!dept.contains(trimmedTeamName)) {  
520 - throw exception0(BAD_REQUEST.getCode(), String.format(  
521 - "第%d条数据:%s名称不正确",  
522 - rowNum, teamName  
523 - )); 802 + // 将校验通过的团队名称加入Set,用于后续重复判断
  803 + checkedTeams.add(trimmedTeamName);
524 } 804 }
525 -  
526 - // 将校验通过的团队名称加入Set,用于后续重复判断  
527 - checkedTeams.add(trimmedTeamName);  
528 } 805 }
529 } 806 }
530 807
@@ -538,4 +815,167 @@ public class CostFeeServiceImpl implements CostFeeService { @@ -538,4 +815,167 @@ public class CostFeeServiceImpl implements CostFeeService {
538 } 815 }
539 return listMap; 816 return listMap;
540 } 817 }
  818 +
  819 + /**
  820 + * 按salaryMonth、expenseName、companyId三个字段去重
  821 + * @param costList 原始部门成本列表
  822 + * @return 去重后的列表
  823 + */
  824 + public List<DepartmentCostImport> deptFeeReplacement(List<DepartmentCostImport> costList) {
  825 + if (costList == null || costList.isEmpty()) {
  826 + return new ArrayList<>();
  827 + }
  828 +
  829 + // 1. 以三个字段的组合作为键(使用分隔符避免不同组合产生相同键)
  830 + // 2. 遇到重复键时保留第一条记录
  831 + Map<String, DepartmentCostImport> uniqueMap = costList.stream()
  832 + .collect(Collectors.toMap(
  833 + // 生成唯一键:salaryMonth|expenseName|companyId
  834 + cost -> buildUniqueKey(cost),
  835 + // 键不存在时存入当前对象
  836 + cost -> cost,
  837 + // 键已存在时保留最后一个
  838 + (existing, replacement) -> replacement
  839 + ));
  840 +
  841 + // 将去重后的结果转为List返回
  842 + return new ArrayList<>(uniqueMap.values());
  843 + }
  844 +
  845 + /**
  846 + * 构建三个字段的唯一组合键(处理null值)
  847 + */
  848 + private String buildUniqueKey(DepartmentCostImport cost) {
  849 + // 使用Objects.toString处理可能的null值,避免NullPointerException
  850 + return Objects.toString(cost.getSalaryMonth(), "") + "|" +
  851 + Objects.toString(cost.getExpenseName(), "") + "|" +
  852 + Objects.toString(cost.getCompanyName(), "");
  853 + }
  854 +
  855 + /**
  856 + * 按salaryMonth、name、companyId、teamIdList四个字段去重
  857 + * @param costList 原始个人成本列表
  858 + * @return 去重后的列表
  859 + */
  860 + public List<PersonalCostImport> personalFeeReplacement(List<PersonalCostImport> costList) {
  861 + if (costList == null || costList.isEmpty()) {
  862 + return new ArrayList<>();
  863 + }
  864 +
  865 + // 1. 以四个字段的组合作为键(特别处理集合类型的teamIdList)
  866 + // 2. 遇到重复键时保留第一条记录
  867 + Map<String, PersonalCostImport> uniqueMap = costList.stream()
  868 + .collect(Collectors.toMap(
  869 + // 生成唯一键:salaryMonth|name|companyId|teamIdListStr
  870 + this::buildUniqueKey,
  871 + // 键不存在时存入当前对象
  872 + cost -> cost,
  873 + // 键已存在时保留第一个对象(可改为replacement保留最后一个)
  874 + (existing, replacement) -> replacement
  875 + ));
  876 +
  877 + // 将去重后的结果转为List返回
  878 + return new ArrayList<>(uniqueMap.values());
  879 + }
  880 +
  881 + /**
  882 + * 构建四个字段的唯一组合键(处理null值和集合类型)
  883 + */
  884 + private String buildUniqueKey(PersonalCostImport cost) {
  885 + // 处理普通字段的null值
  886 + String salaryMonth = Objects.toString(cost.getSalaryMonth(), "");
  887 + String name = Objects.toString(cost.getName(), "");
  888 + String companyId = Objects.toString(cost.getCompanyName(), "");
  889 + String teamIds = Objects.toString(cost.getTeamIds(), "");
  890 + // 拼接唯一键
  891 + return salaryMonth + "|" + name + "|" + companyId + "|" + teamIds;
  892 + }
  893 +
  894 +
  895 + public static LocalDate getEndLocalDateForYears(LocalDate startDate, int years) {
  896 + if (startDate == null) {
  897 + throw new IllegalArgumentException("开始日期不能为null");
  898 + }
  899 + if (years < 0) {
  900 + throw new IllegalArgumentException("年数不能为负数");
  901 + }
  902 + return startDate.plusYears(years);
  903 + }
  904 +
  905 + /**
  906 + * @Author wangqian
  907 + * @Description 根据班组名称获取班组id
  908 + * @Date 2025/11/26 19:38
  909 + */
  910 + /**
  911 + * 根据部门ID列表获取「部门名称→部门ID」的映射(含空值防护)
  912 + */
  913 + private Map<String, Long> getDeptListAll(List<Long> deptIds) {
  914 + Map<String, Long> deptNameMap = new HashMap<>();
  915 +
  916 + // 1. 空值安全:避免 deptIds 为 null/空列表导致 NPE
  917 + if (CollectionUtils.isEmpty(deptIds)) {
  918 + return deptNameMap;
  919 + }
  920 +
  921 + for (Long deptId : deptIds) {
  922 + // 2. 跳过 null 的部门ID
  923 + if (deptId == null) {
  924 + continue;
  925 + }
  926 +
  927 + // 3. 获取子部门列表(防护接口返回 null)
  928 + List<DeptRespDTO> deptList = deptApi.getChildDeptList(deptId);
  929 + if (CollectionUtils.isEmpty(deptList)) {
  930 + continue;
  931 + }
  932 +
  933 + // 4. 遍历子部门:过滤无效名称,避免 Map 出现 null/空字符串键
  934 + for (DeptRespDTO dept : deptList) {
  935 + if (dept == null) {
  936 + continue;
  937 + }
  938 + String deptName = dept.getName();
  939 + // 过滤 null/空白名称(避免 Map 键为 null 或空字符串)
  940 + if (deptName == null || deptName.trim().isEmpty()) {
  941 + continue;
  942 + }
  943 + // 若名称重复,后遍历的部门会覆盖先遍历的(可根据业务调整规则)
  944 + deptNameMap.put(deptName.trim(), dept.getId());
  945 + }
  946 + }
  947 +
  948 + return deptNameMap;
  949 + }
  950 + /**
  951 + * @Author wangqian
  952 + * @Description 根据班组名称获取班组id集合
  953 + * @Date 2025/11/26 20:07
  954 + */
  955 + private List<String> getDeptList(Map<String, Long> teamIdMap, String teamId) {
  956 + List<String> list = new ArrayList<>();
  957 +
  958 + // 1. 处理 teamId 为空的情况,直接返回空列表
  959 + if (StringUtils.isEmpty(teamId)) {
  960 + return list;
  961 + }
  962 +
  963 + // 2. 分割字符串,处理空元素
  964 + String[] teamArray = teamId.split(",");
  965 + for (String teamName : teamArray) {
  966 + // 3. 去除首尾空格,避免因空格导致匹配不到
  967 + String trimTeamName = teamName.trim();
  968 + if (StringUtils.isEmpty(trimTeamName)) {
  969 + continue; // 跳过空字符串元素
  970 + }
  971 +
  972 + // 4. 从Map中获取ID,处理null值(避免添加null到列表)
  973 + Long deptId = teamIdMap.get(trimTeamName);
  974 + if (deptId != null) {
  975 + list.add(String.valueOf(deptId)); // 直接添加Long类型,修正原String类型错误
  976 + }
  977 + }
  978 +
  979 + return list;
  980 + }
541 } 981 }
urbanops-module-garden/src/main/resources/mapper/departmentcost/DepartmentCostMapper.xml
@@ -2,11 +2,13 @@ @@ -2,11 +2,13 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper"> 3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.departmentcost.DepartmentCostMapper">
4 4
5 - <!--  
6 - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。  
7 - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。  
8 - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。  
9 - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/  
10 - --> 5 + <select id="selectDuplicateIds" resultType="java.lang.Long">
  6 + SELECT id
  7 + FROM td_department_cost
  8 + WHERE
  9 + <foreach collection="keyList" item="key" separator="OR">
  10 + (salary_month = #{key.salaryMonth} AND expense_name = #{key.expenseName} and company_id=#{key.companyId})
  11 + </foreach>
  12 + </select>
11 13
12 -</mapper>  
13 \ No newline at end of file 14 \ No newline at end of file
  15 +</mapper>
urbanops-module-garden/src/main/resources/mapper/personalcost/PersonalCostMapper.xml
@@ -2,11 +2,12 @@ @@ -2,11 +2,12 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper"> 3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.personalcost.PersonalCostMapper">
4 4
5 - <!--  
6 - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。  
7 - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。  
8 - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。  
9 - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/  
10 - -->  
11 -  
12 -</mapper>  
13 \ No newline at end of file 5 \ No newline at end of file
  6 + <select id="selectDuplicateIds" resultType="java.lang.Long">
  7 + SELECT id
  8 + FROM td_personal_cost
  9 + WHERE
  10 + <foreach collection="keyList" item="key" separator="OR">
  11 + (salary_month = #{key.salaryMonth} AND name = #{key.name})
  12 + </foreach>
  13 + </select>
  14 +</mapper>
urbanops-module-garden/src/main/resources/mapper/waterfee/WaterFeeMapper.xml
@@ -2,11 +2,14 @@ @@ -2,11 +2,14 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper"> 3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.waterfee.WaterFeeMapper">
4 4
5 - <!--  
6 - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。  
7 - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。  
8 - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。  
9 - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/  
10 - -->  
11 5
12 -</mapper>  
13 \ No newline at end of file 6 \ No newline at end of file
  7 + <select id="selectDuplicateIds" resultType="java.lang.Long">
  8 + SELECT id
  9 + FROM td_water_fee
  10 + WHERE
  11 + <foreach collection="keyList" item="key" separator="OR">
  12 + (fee_month = #{key.feeMonth} AND meter_code = #{key.meterCode})
  13 + </foreach>
  14 + </select>
  15 +
  16 +</mapper>
urbanops-module-gloabl/src/main/resources/mapper/mechanicalcost/MechanicalCostMapper.xml
@@ -2,11 +2,13 @@ @@ -2,11 +2,13 @@
2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> 2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper"> 3 <mapper namespace="com.zteits.urbanops.module.garden.dal.mysql.mechanicalcost.MechanicalCostMapper">
4 4
5 - <!--  
6 - 一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。  
7 - 无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。  
8 - 代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。  
9 - 文档可见:https://www.iocoder.cn/MyBatis/x-plugins/  
10 - --> 5 + <select id="selectDuplicateIds" resultType="java.lang.Long">
  6 + SELECT id
  7 + FROM td_mechanical_cost
  8 + WHERE
  9 + <foreach collection="keyList" item="key" separator="OR">
  10 + mech_code =#{key.mechCode}
  11 + </foreach>
  12 + </select>
11 13
12 -</mapper>  
13 \ No newline at end of file 14 \ No newline at end of file
  15 +</mapper>