库位修改以及批量导入 不能重复的校验
This commit is contained in:
@@ -99,5 +99,18 @@ public interface PcdeDetailMapper
|
|||||||
|
|
||||||
String selectEncodedIdByPcodeAndWarehouse(@Param("pcode") String pcode,
|
String selectEncodedIdByPcodeAndWarehouse(@Param("pcode") String pcode,
|
||||||
@Param("warehouseCode") String warehouseCode);
|
@Param("warehouseCode") String warehouseCode);
|
||||||
|
/**
|
||||||
|
* 新增前查重
|
||||||
|
* 同一仓库、同一场景、同一库位编码,不允许重复
|
||||||
|
*/
|
||||||
|
public PcdeDetail selectByWarehouseSceneAndPcode(PcdeDetail pcdeDetail);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 修改前查重(排除自己)
|
||||||
|
*/
|
||||||
|
public PcdeDetail selectRepeatByWarehouseSceneAndPcode(PcdeDetail pcdeDetail);
|
||||||
|
/**
|
||||||
|
* 批量查已存在
|
||||||
|
*/
|
||||||
|
List<PcdeDetail> selectExistsForImport(@Param("list") List<PcdeDetail> list);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,15 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int insertPcdeDetail(PcdeDetail pcdeDetail) {
|
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
|
// 原始 locationCode
|
||||||
String locationCode = pcdeDetail.getPcode();
|
String locationCode = pcdeDetail.getPcode();
|
||||||
|
|
||||||
@@ -86,6 +95,16 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService
|
|||||||
@Override
|
@Override
|
||||||
public int updatePcdeDetail(PcdeDetail pcdeDetail)
|
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);
|
return pcdeDetailMapper.updatePcdeDetail(pcdeDetail);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,49 +174,96 @@ public class PcdeDetailServiceImpl implements IPcdeDetailService
|
|||||||
|
|
||||||
final Date now = DateUtils.getNowDate();
|
final Date now = DateUtils.getNowDate();
|
||||||
|
|
||||||
// 1) 清洗 + 本地生成 encodedId(逐字符→Unicode→HEX大写→拼接)
|
// 1. 清洗数据
|
||||||
List<PcdeDetail> cleaned = new ArrayList<>(pcdeList.size());
|
List<PcdeDetail> cleaned = new ArrayList<>(pcdeList.size());
|
||||||
|
|
||||||
|
// 文件内去重:同一仓库+同一场景+同一库位编码
|
||||||
|
Set<String> importKeySet = new HashSet<>();
|
||||||
|
int innerRepeatCount = 0;
|
||||||
|
|
||||||
for (PcdeDetail d : pcdeList) {
|
for (PcdeDetail d : pcdeList) {
|
||||||
if (d == null) continue;
|
if (d == null) {
|
||||||
String p = d.getPcode() == null ? null : d.getPcode().trim();
|
|
||||||
if (StringUtils.isEmpty(p)) {
|
|
||||||
// 跳过空 pcode
|
|
||||||
continue;
|
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.setCreatedBy(operName);
|
||||||
d.setCreatedAt(now);
|
d.setCreatedAt(now);
|
||||||
d.setUpdatedBy(operName);
|
d.setUpdatedBy(operName);
|
||||||
d.setUpdatedAt(now);
|
d.setUpdatedAt(now);
|
||||||
|
|
||||||
// 业务兜底
|
if (d.getIsDelete() == null) {
|
||||||
if (d.getIsDelete() == null) d.setIsDelete("0");
|
d.setIsDelete("0");
|
||||||
|
}
|
||||||
|
|
||||||
cleaned.add(d);
|
cleaned.add(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cleaned.isEmpty()) {
|
if (cleaned.isEmpty()) {
|
||||||
throw new ServiceException("有效的库位编号为空,请检查导入文件!");
|
throw new ServiceException("有效的导入数据为空,请检查导入文件!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2) 批量写库(分片,避免 SQL 过大;配合 uk_pcode + INSERT IGNORE 实现幂等忽略)
|
// 2. 批量查数据库已存在数据
|
||||||
|
List<PcdeDetail> existList = pcdeDetailMapper.selectExistsForImport(cleaned);
|
||||||
|
|
||||||
|
Set<String> dbExistKeySet = new HashSet<>();
|
||||||
|
if (!CollectionUtils.isEmpty(existList)) {
|
||||||
|
for (PcdeDetail item : existList) {
|
||||||
|
String key = item.getWarehouseCode() + "_" + item.getScene() + "_" + item.getPcode();
|
||||||
|
dbExistKeySet.add(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 过滤数据库中已存在的数据
|
||||||
|
List<PcdeDetail> 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;
|
final int batchSize = 1000;
|
||||||
int totalTried = cleaned.size();
|
|
||||||
int totalInserted = 0;
|
int totalInserted = 0;
|
||||||
|
|
||||||
for (int i = 0; i < cleaned.size(); i += batchSize) {
|
for (int i = 0; i < needInsertList.size(); i += batchSize) {
|
||||||
int end = Math.min(i + batchSize, cleaned.size());
|
int end = Math.min(i + batchSize, needInsertList.size());
|
||||||
List<PcdeDetail> slice = cleaned.subList(i, end);
|
List<PcdeDetail> slice = needInsertList.subList(i, end);
|
||||||
int affected = pcdeDetailMapper.batchInsertIgnore(slice);
|
int affected = pcdeDetailMapper.batchInsertIgnore(slice);
|
||||||
totalInserted += affected;
|
totalInserted += affected;
|
||||||
}
|
}
|
||||||
|
|
||||||
int skipped = totalTried - totalInserted;
|
return String.format("导入完成:共读取 %d 条,文件内重复 %d 条,数据库已存在 %d 条,成功导入 %d 条。",
|
||||||
return String.format("导入完成:共读取 %d 条,成功导入 %d 条,跳过(可能重复) %d 条。",
|
pcdeList.size(), innerRepeatCount, dbRepeatCount, totalInserted);
|
||||||
totalTried, totalInserted, skipped);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 将 pcode 按字符转大写十六进制(与新增接口保持一致) */
|
/** 将 pcode 按字符转大写十六进制(与新增接口保持一致) */
|
||||||
|
|||||||
@@ -382,5 +382,84 @@
|
|||||||
#{id}
|
#{id}
|
||||||
</foreach>
|
</foreach>
|
||||||
</delete>
|
</delete>
|
||||||
|
<!-- 新增前查重 -->
|
||||||
|
<select id="selectByWarehouseSceneAndPcode" parameterType="PcdeDetail" resultMap="PcdeDetailResult">
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
pcode,
|
||||||
|
scene,
|
||||||
|
parent_warehouse_code,
|
||||||
|
parent_warehouse_name,
|
||||||
|
warehouse_code,
|
||||||
|
warehouse_name,
|
||||||
|
encoded_id,
|
||||||
|
tag,
|
||||||
|
remark,
|
||||||
|
is_delete,
|
||||||
|
created_by,
|
||||||
|
created_at,
|
||||||
|
updated_by,
|
||||||
|
updated_at
|
||||||
|
FROM pcde_detail
|
||||||
|
WHERE is_delete = 0
|
||||||
|
AND warehouse_code = #{warehouseCode}
|
||||||
|
AND scene = #{scene}
|
||||||
|
AND pcode = #{pcode}
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<!-- 修改前查重(排除当前id) -->
|
||||||
|
<select id="selectRepeatByWarehouseSceneAndPcode" parameterType="PcdeDetail" resultMap="PcdeDetailResult">
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
pcode,
|
||||||
|
scene,
|
||||||
|
parent_warehouse_code,
|
||||||
|
parent_warehouse_name,
|
||||||
|
warehouse_code,
|
||||||
|
warehouse_name,
|
||||||
|
encoded_id,
|
||||||
|
tag,
|
||||||
|
remark,
|
||||||
|
is_delete,
|
||||||
|
created_by,
|
||||||
|
created_at,
|
||||||
|
updated_by,
|
||||||
|
updated_at
|
||||||
|
FROM pcde_detail
|
||||||
|
WHERE is_delete = 0
|
||||||
|
AND warehouse_code = #{warehouseCode}
|
||||||
|
AND scene = #{scene}
|
||||||
|
AND pcode = #{pcode}
|
||||||
|
AND id != #{id}
|
||||||
|
LIMIT 1
|
||||||
|
</select>
|
||||||
|
<select id="selectExistsForImport" resultMap="PcdeDetailResult">
|
||||||
|
SELECT
|
||||||
|
id,
|
||||||
|
pcode,
|
||||||
|
scene,
|
||||||
|
parent_warehouse_code,
|
||||||
|
parent_warehouse_name,
|
||||||
|
warehouse_code,
|
||||||
|
warehouse_name,
|
||||||
|
encoded_id,
|
||||||
|
tag,
|
||||||
|
remark,
|
||||||
|
is_delete,
|
||||||
|
created_by,
|
||||||
|
created_at,
|
||||||
|
updated_by,
|
||||||
|
updated_at
|
||||||
|
FROM pcde_detail
|
||||||
|
WHERE is_delete = '0'
|
||||||
|
AND
|
||||||
|
<foreach collection="list" item="item" separator=" OR " open="(" close=")">
|
||||||
|
(
|
||||||
|
warehouse_code = #{item.warehouseCode}
|
||||||
|
AND scene = #{item.scene}
|
||||||
|
AND pcode = #{item.pcode}
|
||||||
|
)
|
||||||
|
</foreach>
|
||||||
|
</select>
|
||||||
</mapper>
|
</mapper>
|
||||||
|
|||||||
Reference in New Issue
Block a user