6 changed files with 344 additions and 0 deletions
-
12src/main/java/com/xujie/sys/modules/part/mapper/PartInventorySyncMapper.java
-
11src/main/java/com/xujie/sys/modules/part/service/PartInventorySyncService.java
-
76src/main/java/com/xujie/sys/modules/part/service/impl/PartInventorySyncServiceImpl.java
-
47src/main/java/com/xujie/sys/modules/part/task/PartInventorySyncTask.java
-
4src/main/resources/application-dev.yml
-
194src/main/resources/mapper/part/PartInventorySyncMapper.xml
@ -0,0 +1,12 @@ |
|||
package com.xujie.sys.modules.part.mapper; |
|||
|
|||
import org.apache.ibatis.annotations.Mapper; |
|||
import org.apache.ibatis.annotations.Param; |
|||
|
|||
@Mapper |
|||
public interface PartInventorySyncMapper { |
|||
|
|||
int updatePartFromMesInventoryView(@Param("batchSize") Integer batchSize); |
|||
|
|||
int insertPartFromMesInventoryView(@Param("batchSize") Integer batchSize); |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package com.xujie.sys.modules.part.service; |
|||
|
|||
public interface PartInventorySyncService { |
|||
|
|||
/** |
|||
* 从 MES 物料视图同步数据到 part 表 |
|||
* |
|||
* @param batchSize 分批大小,防止长事务导致锁升级 |
|||
*/ |
|||
void syncFromMesInventoryView(Integer batchSize); |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
package com.xujie.sys.modules.part.service.impl; |
|||
|
|||
import com.xujie.sys.modules.part.mapper.PartInventorySyncMapper; |
|||
import com.xujie.sys.modules.part.service.PartInventorySyncService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
@Service |
|||
@Slf4j |
|||
public class PartInventorySyncServiceImpl implements PartInventorySyncService { |
|||
|
|||
private static final int DEFAULT_BATCH_SIZE = 500; |
|||
private static final int MAX_LOOP_COUNT = 2000; |
|||
|
|||
@Autowired |
|||
private PartInventorySyncMapper partInventorySyncMapper; |
|||
|
|||
@Override |
|||
public void syncFromMesInventoryView(Integer batchSize) { |
|||
int safeBatchSize = (batchSize == null || batchSize <= 0) ? DEFAULT_BATCH_SIZE : batchSize; |
|||
|
|||
int totalUpdateCount = executeUpdateInBatches(safeBatchSize); |
|||
int totalInsertCount = executeInsertInBatches(safeBatchSize); |
|||
|
|||
if (totalUpdateCount > 0 || totalInsertCount > 0) { |
|||
log.info("MES物料视图同步完成:更新{}条,新增{}条,批次大小{}", totalUpdateCount, totalInsertCount, safeBatchSize); |
|||
} else { |
|||
log.debug("MES物料视图同步无变化,批次大小{}", safeBatchSize); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 分批更新,避免一次更新过多导致锁升级 |
|||
*/ |
|||
private int executeUpdateInBatches(int batchSize) { |
|||
int totalCount = 0; |
|||
boolean reachLoopLimit = true; |
|||
|
|||
for (int i = 0; i < MAX_LOOP_COUNT; i++) { |
|||
int currentCount = partInventorySyncMapper.updatePartFromMesInventoryView(batchSize); |
|||
totalCount += currentCount; |
|||
if (currentCount < batchSize) { |
|||
reachLoopLimit = false; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (reachLoopLimit) { |
|||
log.warn("MES物料视图更新达到最大批次限制{},请检查是否存在长时间大批量变化", MAX_LOOP_COUNT); |
|||
} |
|||
return totalCount; |
|||
} |
|||
|
|||
/** |
|||
* 分批新增,避免一次插入过多导致锁等待 |
|||
*/ |
|||
private int executeInsertInBatches(int batchSize) { |
|||
int totalCount = 0; |
|||
boolean reachLoopLimit = true; |
|||
|
|||
for (int i = 0; i < MAX_LOOP_COUNT; i++) { |
|||
int currentCount = partInventorySyncMapper.insertPartFromMesInventoryView(batchSize); |
|||
totalCount += currentCount; |
|||
if (currentCount < batchSize) { |
|||
reachLoopLimit = false; |
|||
break; |
|||
} |
|||
} |
|||
|
|||
if (reachLoopLimit) { |
|||
log.warn("MES物料视图新增达到最大批次限制{},请检查是否存在长时间大批量新增", MAX_LOOP_COUNT); |
|||
} |
|||
return totalCount; |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package com.xujie.sys.modules.part.task; |
|||
|
|||
import com.xujie.sys.modules.part.service.PartInventorySyncService; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.scheduling.annotation.Scheduled; |
|||
import org.springframework.stereotype.Component; |
|||
|
|||
@Component |
|||
@Slf4j |
|||
public class PartInventorySyncTask { |
|||
|
|||
@Autowired |
|||
private PartInventorySyncService partInventorySyncService; |
|||
|
|||
|
|||
/** |
|||
* part 物料同步任务开关 |
|||
*/ |
|||
@Value("${task.partInventorySync.enabled:false}") |
|||
private boolean partInventorySyncEnabled; |
|||
|
|||
/** |
|||
* 分批大小,避免锁升级 |
|||
*/ |
|||
@Value("${task.partInventorySync.batchSize:500}") |
|||
private Integer batchSize; |
|||
|
|||
/** |
|||
* 每半小时同步一次(默认) |
|||
*/ |
|||
@Scheduled(cron = "${task.partInventorySync.cron:0 0/30 * * * ?}") |
|||
public void syncPartFromMesInventoryView() { |
|||
if (!partInventorySyncEnabled) { |
|||
return; |
|||
} |
|||
|
|||
long startTime = System.currentTimeMillis(); |
|||
try { |
|||
partInventorySyncService.syncFromMesInventoryView(batchSize); |
|||
log.info("part物料视图同步任务执行完成,耗时{}ms", System.currentTimeMillis() - startTime); |
|||
} catch (Exception e) { |
|||
log.error("part物料视图同步任务执行失败", e); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,194 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
|||
<mapper namespace="com.xujie.sys.modules.part.mapper.PartInventorySyncMapper"> |
|||
|
|||
<!-- |
|||
分批更新 part 表,使用 TOP + ROWLOCK,减少锁升级概率 |
|||
--> |
|||
<update id="updatePartFromMesInventoryView"> |
|||
;WITH changed_part AS ( |
|||
SELECT TOP (#{batchSize}) |
|||
p.id, |
|||
nv.part_description, |
|||
nv.spec, |
|||
nv.part_type, |
|||
nv.umid, |
|||
nv.active, |
|||
nv.key_part, |
|||
nv.qty_box_roll, |
|||
nv.qty_roll, |
|||
nv.cinvSourceCode, |
|||
nv.sourceBu, |
|||
nv.control_mes, |
|||
nv.standard_box_qty, |
|||
nv.sku, |
|||
nv.cinvname, |
|||
nv.part_desce_en, |
|||
nv.cwhcode, |
|||
nv.cwhname, |
|||
nv.invdefinetype, |
|||
nv.invrcost, |
|||
nv.box_length, |
|||
nv.box_width, |
|||
nv.box_height |
|||
FROM part p WITH (ROWLOCK, READPAST) |
|||
INNER JOIN view_custdev_mes_inventory v WITH (NOLOCK) |
|||
ON p.site = ISNULL(v.site, '5') |
|||
AND p.part_no = v.part_no |
|||
CROSS APPLY ( |
|||
SELECT |
|||
CASE WHEN v.part_description IS NULL THEN NULL ELSE LEFT(v.part_description, 500) END AS part_description, |
|||
CASE WHEN v.spec IS NULL THEN NULL ELSE LEFT(v.spec, 300) END AS spec, |
|||
CASE WHEN v.part_type IS NULL THEN NULL ELSE LEFT(v.part_type, 100) END AS part_type, |
|||
CASE WHEN v.umid IS NULL THEN NULL ELSE LEFT(v.umid, 8) END AS umid, |
|||
CASE WHEN v.active IS NULL THEN NULL ELSE LEFT(v.active, 1) END AS active, |
|||
CASE WHEN v.key_part IS NULL THEN NULL ELSE LEFT(CONVERT(varchar(10), v.key_part), 1) END AS key_part, |
|||
CASE WHEN v.qty_box_roll IS NULL THEN NULL ELSE CONVERT(varchar(50), v.qty_box_roll) END AS qty_box_roll, |
|||
CASE WHEN v.qty_roll IS NULL THEN NULL ELSE CONVERT(varchar(50), v.qty_roll) END AS qty_roll, |
|||
CASE WHEN v.cinvSourceCode IS NULL THEN NULL ELSE LEFT(v.cinvSourceCode, 50) END AS cinvSourceCode, |
|||
CASE WHEN v.sourceBu IS NULL THEN NULL ELSE LEFT(v.sourceBu, 50) END AS sourceBu, |
|||
CASE WHEN v.control_mes IS NULL THEN NULL ELSE LEFT(v.control_mes, 1) END AS control_mes, |
|||
v.standard_box_qty AS standard_box_qty, |
|||
CASE |
|||
WHEN v.sku IS NULL OR UPPER(LTRIM(RTRIM(v.sku))) = 'NULL' THEN LEFT(v.part_no, 50) |
|||
ELSE LEFT(v.sku, 50) |
|||
END AS sku, |
|||
CASE WHEN v.cinvname IS NULL THEN NULL ELSE LEFT(v.cinvname, 50) END AS cinvname, |
|||
CASE WHEN v.part_desce_en IS NULL THEN NULL ELSE LEFT(v.part_desce_en, 1000) END AS part_desce_en, |
|||
CASE WHEN v.cwhcode IS NULL THEN NULL ELSE LEFT(v.cwhcode, 20) END AS cwhcode, |
|||
CASE WHEN v.cwhname IS NULL THEN NULL ELSE LEFT(v.cwhname, 50) END AS cwhname, |
|||
CASE WHEN v.invdefinetype IS NULL THEN NULL ELSE LEFT(v.invdefinetype, 100) END AS invdefinetype, |
|||
v.invrcost AS invrcost, |
|||
v.box_length AS box_length, |
|||
v.box_width AS box_width, |
|||
v.box_height AS box_height |
|||
) nv |
|||
WHERE ISNULL(p.part_desc, '') <> ISNULL(nv.part_description, '') |
|||
OR ISNULL(p.spec, '') <> ISNULL(nv.spec, '') |
|||
OR ISNULL(p.part_type, '') <> ISNULL(nv.part_type, '') |
|||
OR ISNULL(p.umid, '') <> ISNULL(nv.umid, '') |
|||
OR ISNULL(p.active, '') <> ISNULL(nv.active, '') |
|||
OR ISNULL(p.key_part, '') <> ISNULL(nv.key_part, '') |
|||
OR ISNULL(p.qty_box_roll, '') <> ISNULL(nv.qty_box_roll, '') |
|||
OR ISNULL(p.qty_roll, '') <> ISNULL(nv.qty_roll, '') |
|||
OR ISNULL(p.cinv_source_code, '') <> ISNULL(nv.cinvSourceCode, '') |
|||
OR ISNULL(p.sourceBu, '') <> ISNULL(nv.sourceBu, '') |
|||
OR ISNULL(p.control_mes, '') <> ISNULL(nv.control_mes, '') |
|||
OR ISNULL(CONVERT(decimal(18, 4), p.standard_box_qty), 0) <> ISNULL(nv.standard_box_qty, 0) |
|||
OR ISNULL(p.sku, '') <> ISNULL(nv.sku, '') |
|||
OR ISNULL(p.cinvcname, '') <> ISNULL(nv.cinvname, '') |
|||
OR ISNULL(p.part_desce_en, '') <> ISNULL(nv.part_desce_en, '') |
|||
OR ISNULL(p.DefaultWarehouseID, '') <> ISNULL(nv.cwhcode, '') |
|||
OR ISNULL(p.DefaultWarehouseName, '') <> ISNULL(nv.cwhname, '') |
|||
OR ISNULL(p.invdefinetype, '') <> ISNULL(nv.invdefinetype, '') |
|||
OR ISNULL(CONVERT(decimal(20, 6), p.standard_cost), 0) <> ISNULL(CONVERT(decimal(20, 6), nv.invrcost), 0) |
|||
OR ISNULL(CONVERT(decimal(18, 4), p.box_length), 0) <> ISNULL(nv.box_length, 0) |
|||
OR ISNULL(CONVERT(decimal(18, 4), p.box_width), 0) <> ISNULL(nv.box_width, 0) |
|||
OR ISNULL(CONVERT(decimal(18, 4), p.box_height), 0) <> ISNULL(nv.box_height, 0) |
|||
ORDER BY p.id |
|||
) |
|||
UPDATE p |
|||
SET p.part_desc = c.part_description, |
|||
p.spec = c.spec, |
|||
p.part_type = c.part_type, |
|||
p.umid = c.umid, |
|||
p.active = c.active, |
|||
p.key_part = c.key_part, |
|||
p.qty_box_roll = c.qty_box_roll, |
|||
p.qty_roll = c.qty_roll, |
|||
p.cinv_source_code = c.cinvSourceCode, |
|||
p.sourceBu = c.sourceBu, |
|||
p.control_mes = c.control_mes, |
|||
p.standard_box_qty = c.standard_box_qty, |
|||
p.sku = c.sku, |
|||
p.cinvcname = c.cinvname, |
|||
p.part_desce_en = c.part_desce_en, |
|||
p.DefaultWarehouseID = c.cwhcode, |
|||
p.DefaultWarehouseName = c.cwhname, |
|||
p.invdefinetype = c.invdefinetype, |
|||
p.standard_cost = c.invrcost, |
|||
p.box_length = c.box_length, |
|||
p.box_width = c.box_width, |
|||
p.box_height = c.box_height, |
|||
p.update_date = GETDATE(), |
|||
p.update_by = 'SYSTEM_SYNC' |
|||
FROM part p WITH (ROWLOCK, UPDLOCK, READPAST) |
|||
INNER JOIN changed_part c ON p.id = c.id |
|||
</update> |
|||
|
|||
<!-- |
|||
分批新增 part 表,避免一次写入过大 |
|||
--> |
|||
<insert id="insertPartFromMesInventoryView"> |
|||
INSERT INTO part ( |
|||
site, |
|||
part_no, |
|||
part_desc, |
|||
spec, |
|||
part_type, |
|||
umid, |
|||
active, |
|||
key_part, |
|||
qty_box_roll, |
|||
qty_roll, |
|||
cinv_source_code, |
|||
sourceBu, |
|||
control_mes, |
|||
standard_box_qty, |
|||
sku, |
|||
cinvcname, |
|||
part_desce_en, |
|||
DefaultWarehouseID, |
|||
DefaultWarehouseName, |
|||
invdefinetype, |
|||
standard_cost, |
|||
box_length, |
|||
box_width, |
|||
box_height, |
|||
created_by, |
|||
creation_date, |
|||
update_by, |
|||
update_date |
|||
) |
|||
SELECT TOP (#{batchSize}) |
|||
ISNULL(v.site, '5'), |
|||
v.part_no, |
|||
CASE WHEN v.part_description IS NULL THEN NULL ELSE LEFT(v.part_description, 500) END, |
|||
CASE WHEN v.spec IS NULL THEN NULL ELSE LEFT(v.spec, 300) END, |
|||
CASE WHEN v.part_type IS NULL THEN NULL ELSE LEFT(v.part_type, 100) END, |
|||
CASE WHEN v.umid IS NULL THEN NULL ELSE LEFT(v.umid, 8) END, |
|||
CASE WHEN v.active IS NULL THEN NULL ELSE LEFT(v.active, 1) END, |
|||
CASE WHEN v.key_part IS NULL THEN NULL ELSE LEFT(CONVERT(varchar(10), v.key_part), 1) END, |
|||
CASE WHEN v.qty_box_roll IS NULL THEN NULL ELSE CONVERT(varchar(50), v.qty_box_roll) END, |
|||
CASE WHEN v.qty_roll IS NULL THEN NULL ELSE CONVERT(varchar(50), v.qty_roll) END, |
|||
CASE WHEN v.cinvSourceCode IS NULL THEN NULL ELSE LEFT(v.cinvSourceCode, 50) END, |
|||
CASE WHEN v.sourceBu IS NULL THEN NULL ELSE LEFT(v.sourceBu, 50) END, |
|||
CASE WHEN v.control_mes IS NULL THEN NULL ELSE LEFT(v.control_mes, 1) END, |
|||
v.standard_box_qty, |
|||
CASE |
|||
WHEN v.sku IS NULL OR UPPER(LTRIM(RTRIM(v.sku))) = 'NULL' THEN LEFT(v.part_no, 50) |
|||
ELSE LEFT(v.sku, 50) |
|||
END, |
|||
CASE WHEN v.cinvname IS NULL THEN NULL ELSE LEFT(v.cinvname, 50) END, |
|||
CASE WHEN v.part_desce_en IS NULL THEN NULL ELSE LEFT(v.part_desce_en, 1000) END, |
|||
CASE WHEN v.cwhcode IS NULL THEN NULL ELSE LEFT(v.cwhcode, 20) END, |
|||
CASE WHEN v.cwhname IS NULL THEN NULL ELSE LEFT(v.cwhname, 50) END, |
|||
CASE WHEN v.invdefinetype IS NULL THEN NULL ELSE LEFT(v.invdefinetype, 100) END, |
|||
v.invrcost, |
|||
v.box_length, |
|||
v.box_width, |
|||
v.box_height, |
|||
'SYSTEM_SYNC', |
|||
GETDATE(), |
|||
'SYSTEM_SYNC', |
|||
GETDATE() |
|||
FROM view_custdev_mes_inventory v WITH (NOLOCK) |
|||
LEFT JOIN part p WITH (READPAST) |
|||
ON p.site = ISNULL(v.site, '5') |
|||
AND p.part_no = v.part_no |
|||
WHERE p.id IS NULL |
|||
AND v.part_no IS NOT NULL |
|||
AND v.part_no != '' |
|||
ORDER BY ISNULL(v.site, '5'), v.part_no |
|||
</insert> |
|||
</mapper> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue