Browse Source

同步Alpha物料part

master
han\hanst 2 weeks ago
parent
commit
eba2afffb0
  1. 12
      src/main/java/com/xujie/sys/modules/part/mapper/PartInventorySyncMapper.java
  2. 11
      src/main/java/com/xujie/sys/modules/part/service/PartInventorySyncService.java
  3. 76
      src/main/java/com/xujie/sys/modules/part/service/impl/PartInventorySyncServiceImpl.java
  4. 47
      src/main/java/com/xujie/sys/modules/part/task/PartInventorySyncTask.java
  5. 4
      src/main/resources/application-dev.yml
  6. 194
      src/main/resources/mapper/part/PartInventorySyncMapper.xml

12
src/main/java/com/xujie/sys/modules/part/mapper/PartInventorySyncMapper.java

@ -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);
}

11
src/main/java/com/xujie/sys/modules/part/service/PartInventorySyncService.java

@ -0,0 +1,11 @@
package com.xujie.sys.modules.part.service;
public interface PartInventorySyncService {
/**
* MES 物料视图同步数据到 part
*
* @param batchSize 分批大小防止长事务导致锁升级
*/
void syncFromMesInventoryView(Integer batchSize);
}

76
src/main/java/com/xujie/sys/modules/part/service/impl/PartInventorySyncServiceImpl.java

@ -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;
}
}

47
src/main/java/com/xujie/sys/modules/part/task/PartInventorySyncTask.java

@ -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);
}
}
}

4
src/main/resources/application-dev.yml

@ -57,6 +57,10 @@ task:
sendEmail: 0 0 12 ? * MON-FRI #发送邮件
createQCInspection: 0 01 17 * * ? # 创建质量任务
enabled: false
partInventorySync:
enabled: false # part物料视图同步开关
cron: 0 0/30 * * * ? # 每30分钟执行一次
batchSize: 1000 # 分批处理条数,避免锁升级
#--------------------------------------------ERF审批邮件提醒配置-------------------------------------------------
erf:

194
src/main/resources/mapper/part/PartInventorySyncMapper.xml

@ -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, '') &lt;&gt; ISNULL(nv.part_description, '')
OR ISNULL(p.spec, '') &lt;&gt; ISNULL(nv.spec, '')
OR ISNULL(p.part_type, '') &lt;&gt; ISNULL(nv.part_type, '')
OR ISNULL(p.umid, '') &lt;&gt; ISNULL(nv.umid, '')
OR ISNULL(p.active, '') &lt;&gt; ISNULL(nv.active, '')
OR ISNULL(p.key_part, '') &lt;&gt; ISNULL(nv.key_part, '')
OR ISNULL(p.qty_box_roll, '') &lt;&gt; ISNULL(nv.qty_box_roll, '')
OR ISNULL(p.qty_roll, '') &lt;&gt; ISNULL(nv.qty_roll, '')
OR ISNULL(p.cinv_source_code, '') &lt;&gt; ISNULL(nv.cinvSourceCode, '')
OR ISNULL(p.sourceBu, '') &lt;&gt; ISNULL(nv.sourceBu, '')
OR ISNULL(p.control_mes, '') &lt;&gt; ISNULL(nv.control_mes, '')
OR ISNULL(CONVERT(decimal(18, 4), p.standard_box_qty), 0) &lt;&gt; ISNULL(nv.standard_box_qty, 0)
OR ISNULL(p.sku, '') &lt;&gt; ISNULL(nv.sku, '')
OR ISNULL(p.cinvcname, '') &lt;&gt; ISNULL(nv.cinvname, '')
OR ISNULL(p.part_desce_en, '') &lt;&gt; ISNULL(nv.part_desce_en, '')
OR ISNULL(p.DefaultWarehouseID, '') &lt;&gt; ISNULL(nv.cwhcode, '')
OR ISNULL(p.DefaultWarehouseName, '') &lt;&gt; ISNULL(nv.cwhname, '')
OR ISNULL(p.invdefinetype, '') &lt;&gt; ISNULL(nv.invdefinetype, '')
OR ISNULL(CONVERT(decimal(20, 6), p.standard_cost), 0) &lt;&gt; ISNULL(CONVERT(decimal(20, 6), nv.invrcost), 0)
OR ISNULL(CONVERT(decimal(18, 4), p.box_length), 0) &lt;&gt; ISNULL(nv.box_length, 0)
OR ISNULL(CONVERT(decimal(18, 4), p.box_width), 0) &lt;&gt; ISNULL(nv.box_width, 0)
OR ISNULL(CONVERT(decimal(18, 4), p.box_height), 0) &lt;&gt; 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>
Loading…
Cancel
Save