Commit 26d5a6727ceaa6ab385ed58e285e92d2013fb823
1 parent
b99550af
派单及人机材功能迁移
Showing
12 changed files
with
571 additions
and
89 deletions
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<MechanicalCostDO> { | @@ -39,4 +39,5 @@ public interface MechanicalCostMapper extends BaseMapperX<MechanicalCostDO> { | ||
| 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<PersonalCostDO> { | @@ -32,4 +32,13 @@ public interface PersonalCostMapper extends BaseMapperX<PersonalCostDO> { | ||
| 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<WaterFeeDO> { | @@ -32,4 +32,12 @@ public interface WaterFeeMapper extends BaseMapperX<WaterFeeDO> { | ||
| 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> |