diff --git a/src/main/java/com/zg/project/information/mapper/PcdeDetailMapper.java b/src/main/java/com/zg/project/information/mapper/PcdeDetailMapper.java index d3e4417..8faaf02 100644 --- a/src/main/java/com/zg/project/information/mapper/PcdeDetailMapper.java +++ b/src/main/java/com/zg/project/information/mapper/PcdeDetailMapper.java @@ -99,5 +99,18 @@ public interface PcdeDetailMapper String selectEncodedIdByPcodeAndWarehouse(@Param("pcode") String pcode, @Param("warehouseCode") String warehouseCode); + /** + * 新增前查重 + * 同一仓库、同一场景、同一库位编码,不允许重复 + */ + public PcdeDetail selectByWarehouseSceneAndPcode(PcdeDetail pcdeDetail); + /** + * 修改前查重(排除自己) + */ + public PcdeDetail selectRepeatByWarehouseSceneAndPcode(PcdeDetail pcdeDetail); + /** + * 批量查已存在 + */ + List selectExistsForImport(@Param("list") List list); } diff --git a/src/main/java/com/zg/project/information/service/impl/PcdeDetailServiceImpl.java b/src/main/java/com/zg/project/information/service/impl/PcdeDetailServiceImpl.java index 28da61d..c3838c6 100644 --- a/src/main/java/com/zg/project/information/service/impl/PcdeDetailServiceImpl.java +++ b/src/main/java/com/zg/project/information/service/impl/PcdeDetailServiceImpl.java @@ -61,6 +61,15 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService */ @Override public int insertPcdeDetail(PcdeDetail pcdeDetail) { + pcdeDetail.setWarehouseCode(pcdeDetail.getWarehouseCode().trim()); + pcdeDetail.setScene(pcdeDetail.getScene().trim()); + pcdeDetail.setPcode(pcdeDetail.getPcode().trim()); + // 查重:同一仓库、同一场景、同一库位编码 + PcdeDetail exist = pcdeDetailMapper.selectByWarehouseSceneAndPcode(pcdeDetail); + if (exist != null) + { + throw new RuntimeException("当前仓库、当前场景下已存在该库位编码,请勿重复新增"); + } // 原始 locationCode String locationCode = pcdeDetail.getPcode(); @@ -86,6 +95,16 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService @Override public int updatePcdeDetail(PcdeDetail pcdeDetail) { + pcdeDetail.setWarehouseCode(pcdeDetail.getWarehouseCode().trim()); + pcdeDetail.setScene(pcdeDetail.getScene().trim()); + pcdeDetail.setPcode(pcdeDetail.getPcode().trim()); + + // 查重:排除自己 + PcdeDetail exist = pcdeDetailMapper.selectRepeatByWarehouseSceneAndPcode(pcdeDetail); + if (exist != null) + { + throw new RuntimeException("当前仓库、当前场景下已存在该库位编码,请勿重复修改"); + } return pcdeDetailMapper.updatePcdeDetail(pcdeDetail); } @@ -155,49 +174,96 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService final Date now = DateUtils.getNowDate(); - // 1) 清洗 + 本地生成 encodedId(逐字符→Unicode→HEX大写→拼接) + // 1. 清洗数据 List cleaned = new ArrayList<>(pcdeList.size()); + + // 文件内去重:同一仓库+同一场景+同一库位编码 + Set importKeySet = new HashSet<>(); + int innerRepeatCount = 0; + for (PcdeDetail d : pcdeList) { - if (d == null) continue; - String p = d.getPcode() == null ? null : d.getPcode().trim(); - if (StringUtils.isEmpty(p)) { - // 跳过空 pcode + if (d == null) { continue; } - d.setPcode(p); - d.setEncodedId(toHexStringFromPcode(p)); - // 审计字段(与你实体保持一致:createdBy/createdAt/updatedBy/updatedAt) + String warehouseCode = d.getWarehouseCode() == null ? null : d.getWarehouseCode().trim(); + String scene = d.getScene() == null ? null : d.getScene().trim(); + String pcode = d.getPcode() == null ? null : d.getPcode().trim(); + + if (StringUtils.isEmpty(warehouseCode) || StringUtils.isEmpty(scene) || StringUtils.isEmpty(pcode)) { + continue; + } + + String repeatKey = warehouseCode + "_" + scene + "_" + pcode; + if (importKeySet.contains(repeatKey)) { + innerRepeatCount++; + continue; + } + importKeySet.add(repeatKey); + + d.setWarehouseCode(warehouseCode); + d.setScene(scene); + d.setPcode(pcode); + d.setEncodedId(toHexStringFromPcode(pcode)); + d.setCreatedBy(operName); d.setCreatedAt(now); d.setUpdatedBy(operName); d.setUpdatedAt(now); - // 业务兜底 - if (d.getIsDelete() == null) d.setIsDelete("0"); + if (d.getIsDelete() == null) { + d.setIsDelete("0"); + } cleaned.add(d); } if (cleaned.isEmpty()) { - throw new ServiceException("有效的库位编号为空,请检查导入文件!"); + throw new ServiceException("有效的导入数据为空,请检查导入文件!"); } - // 2) 批量写库(分片,避免 SQL 过大;配合 uk_pcode + INSERT IGNORE 实现幂等忽略) + // 2. 批量查数据库已存在数据 + List existList = pcdeDetailMapper.selectExistsForImport(cleaned); + + Set dbExistKeySet = new HashSet<>(); + if (!CollectionUtils.isEmpty(existList)) { + for (PcdeDetail item : existList) { + String key = item.getWarehouseCode() + "_" + item.getScene() + "_" + item.getPcode(); + dbExistKeySet.add(key); + } + } + + // 3. 过滤数据库中已存在的数据 + List needInsertList = new ArrayList<>(); + int dbRepeatCount = 0; + + for (PcdeDetail d : cleaned) { + String key = d.getWarehouseCode() + "_" + d.getScene() + "_" + d.getPcode(); + if (dbExistKeySet.contains(key)) { + dbRepeatCount++; + continue; + } + needInsertList.add(d); + } + + if (needInsertList.isEmpty()) { + return String.format("导入完成:共读取 %d 条,文件内重复 %d 条,数据库已存在 %d 条,本次成功导入 0 条。", + pcdeList.size(), innerRepeatCount, dbRepeatCount); + } + + // 4. 分批插入 final int batchSize = 1000; - int totalTried = cleaned.size(); int totalInserted = 0; - for (int i = 0; i < cleaned.size(); i += batchSize) { - int end = Math.min(i + batchSize, cleaned.size()); - List slice = cleaned.subList(i, end); + for (int i = 0; i < needInsertList.size(); i += batchSize) { + int end = Math.min(i + batchSize, needInsertList.size()); + List slice = needInsertList.subList(i, end); int affected = pcdeDetailMapper.batchInsertIgnore(slice); totalInserted += affected; } - int skipped = totalTried - totalInserted; - return String.format("导入完成:共读取 %d 条,成功导入 %d 条,跳过(可能重复) %d 条。", - totalTried, totalInserted, skipped); + return String.format("导入完成:共读取 %d 条,文件内重复 %d 条,数据库已存在 %d 条,成功导入 %d 条。", + pcdeList.size(), innerRepeatCount, dbRepeatCount, totalInserted); } /** 将 pcode 按字符转大写十六进制(与新增接口保持一致) */ diff --git a/src/main/resources/mybatis/information/PcdeDetailMapper.xml b/src/main/resources/mybatis/information/PcdeDetailMapper.xml index 31dc6c2..ac1a8ad 100644 --- a/src/main/resources/mybatis/information/PcdeDetailMapper.xml +++ b/src/main/resources/mybatis/information/PcdeDetailMapper.xml @@ -382,5 +382,84 @@ #{id} + + + + +