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