Browse Source

同步打样到NPI

master
han\hanst 12 hours ago
parent
commit
07f1cd469f
  1. 28
      src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleTrackingServiceImpl.java

28
src/main/java/com/xujie/sys/modules/sampletracking/service/impl/SampleTrackingServiceImpl.java

@ -31,6 +31,12 @@ import java.util.Objects;
@Slf4j @Slf4j
@Service @Service
/**
* 样品追踪模块核心实现
* 1) 维护 tracking 表的增删改查
* 2) tracking 数据同步到 NPI 正式表项目/物料/打样
* 3) 同步后执行 ID 对齐保证 tracking NPI 使用同一主键便于后续跨表追溯
*/
public class SampleTrackingServiceImpl implements SampleTrackingService { public class SampleTrackingServiceImpl implements SampleTrackingService {
@Autowired @Autowired
@ -249,6 +255,8 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
throw new RuntimeException("当前项目物料下无可同步打样单: " + oldProjectPartId); throw new RuntimeException("当前项目物料下无可同步打样单: " + oldProjectPartId);
} }
List<Integer> syncedIds = new ArrayList<>(); List<Integer> syncedIds = new ArrayList<>();
// 批量同步时每轮都重新查一次列表
// 因为单条同步会触发 trackingId 对齐ID 可能变化使用最新列表可避免漏同步/重复同步
while (true) { while (true) {
Integer pendingTrackingId = null; Integer pendingTrackingId = null;
currentTrackingIds = sampleTrackingMapper.selectProofTrackingIdsByProjectPartId(syncedProjectPartId); currentTrackingIds = sampleTrackingMapper.selectProofTrackingIdsByProjectPartId(syncedProjectPartId);
@ -286,6 +294,8 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (!missingFields.isEmpty()) { if (!missingFields.isEmpty()) {
throw new RuntimeException("项目追踪记录缺少以下必填项,无法同步: " + String.join("、", missingFields)); throw new RuntimeException("项目追踪记录缺少以下必填项,无法同步: " + String.join("、", missingFields));
} }
// 优先按trackingId == npiId的目标查 NPI
// 若已存在则更新不存在则插入后续再统一做 tracking ID 对齐
Project existedNpiProject = projectMapper.selectById(trackingProjectId); Project existedNpiProject = projectMapper.selectById(trackingProjectId);
Integer npiProjectId = existedNpiProject == null ? null : existedNpiProject.getProjectId(); Integer npiProjectId = existedNpiProject == null ? null : existedNpiProject.getProjectId();
if (npiProjectId == null) { if (npiProjectId == null) {
@ -303,6 +313,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
trackingProject.setUpdateBy(operator); trackingProject.setUpdateBy(operator);
sampleTrackingMapper.updateNpiProjectFromTracking(trackingProject); sampleTrackingMapper.updateNpiProjectFromTracking(trackingProject);
} }
// 对齐 tracking 主键到 NPI 主键避免后续依赖方出现同业务不同ID的维护成本
alignTrackingProjectId(trackingProjectId, npiProjectId, operator); alignTrackingProjectId(trackingProjectId, npiProjectId, operator);
return npiProjectId; return npiProjectId;
} }
@ -317,7 +328,9 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (!missingPartFields.isEmpty()) { if (!missingPartFields.isEmpty()) {
throw new RuntimeException("项目物料追踪记录缺少以下必填项,无法同步: " + String.join("、", missingPartFields)); throw new RuntimeException("项目物料追踪记录缺少以下必填项,无法同步: " + String.join("、", missingPartFields));
} }
// 物料依赖项目主键先确保项目已完成同步并拿到最终 NPI 项目ID
Integer npiProjectId = syncProjectInfoInternal(trackingPart.getProjectId(), operator); Integer npiProjectId = syncProjectInfoInternal(trackingPart.getProjectId(), operator);
// 重新读取一次 tracking 数据避免并发修改导致使用旧快照覆盖新值
ProjectPartTrackingData latestPart = sampleTrackingMapper.selectProjectPartTrackingById(trackingProjectPartId); ProjectPartTrackingData latestPart = sampleTrackingMapper.selectProjectPartTrackingById(trackingProjectPartId);
if (latestPart == null) { if (latestPart == null) {
throw new RuntimeException("项目物料追踪记录不存在: " + trackingProjectPartId); throw new RuntimeException("项目物料追踪记录不存在: " + trackingProjectPartId);
@ -345,8 +358,10 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
latestPart.setUpdateBy(operator); latestPart.setUpdateBy(operator);
sampleTrackingMapper.updateNpiProjectPartFromTracking(latestPart); sampleTrackingMapper.updateNpiProjectPartFromTracking(latestPart);
} }
// 主键对齐放在最后执行保证下游打样记录引用到最终 projectPartId
alignTrackingProjectPartId(trackingProjectPartId, npiProjectPartId, operator); alignTrackingProjectPartId(trackingProjectPartId, npiProjectPartId, operator);
latestPart.setProjectPartId(npiProjectPartId); latestPart.setProjectPartId(npiProjectPartId);
// 邮件放在事务提交后触发避免邮件异常把本次同步回滚
registerProjectPartSyncMailAfterCommit(latestPart, operator); registerProjectPartSyncMailAfterCommit(latestPart, operator);
return npiProjectPartId; return npiProjectPartId;
} }
@ -380,6 +395,8 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
} }
Integer npiProofingId = null; Integer npiProofingId = null;
// 先按 trackingId 再按 proofingId
// 用于兼容历史数据中trackingId/proofingId已部分对齐的场景避免重复插入
ProofTrackingData existedByTrackingId = sampleTrackingMapper.selectNpiProofingById(trackingProofingId); ProofTrackingData existedByTrackingId = sampleTrackingMapper.selectNpiProofingById(trackingProofingId);
if (existedByTrackingId != null) { if (existedByTrackingId != null) {
npiProofingId = existedByTrackingId.getProofingId(); npiProofingId = existedByTrackingId.getProofingId();
@ -403,8 +420,10 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
latestTrackingProof.setUpdateBy(operator); latestTrackingProof.setUpdateBy(operator);
sampleTrackingMapper.updateNpiProofingFromTracking(latestTrackingProof); sampleTrackingMapper.updateNpiProofingFromTracking(latestTrackingProof);
} }
// 对齐打样 tracking 主键同时维护日志/文档关联表保证链路内所有 proofingId 一致
alignTrackingProofingId(trackingProofingId, npiProofingId, operator); alignTrackingProofingId(trackingProofingId, npiProofingId, operator);
sampleTrackingMapper.updateProofTrackingProofingIdByTrackingId(npiProofingId, npiProofingId, operator); sampleTrackingMapper.updateProofTrackingProofingIdByTrackingId(npiProofingId, npiProofingId, operator);
// EAM新增打样保持一致主记录落库后补齐打样文档清单初始化
ensureProofingDocumentAfterSync(npiProofingId, operator); ensureProofingDocumentAfterSync(npiProofingId, operator);
return npiProofingId; return npiProofingId;
} }
@ -413,6 +432,8 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (Objects.equals(oldTrackingProjectId, newTrackingProjectId)) { if (Objects.equals(oldTrackingProjectId, newTrackingProjectId)) {
return; return;
} }
// 若目标ID已被其他 tracking 记录占用先把占位记录整体迁移到新ID再回填当前记录
// 这样可以避免直接覆盖造成数据丢失并保证过程可追溯
Integer existedId = sampleTrackingMapper.existsProjectInfoTrackingId(newTrackingProjectId); Integer existedId = sampleTrackingMapper.existsProjectInfoTrackingId(newTrackingProjectId);
if (existedId != null && !Objects.equals(existedId, oldTrackingProjectId)) { if (existedId != null && !Objects.equals(existedId, oldTrackingProjectId)) {
Integer replacementId = sampleTrackingMapper.selectNextProjectInfoTrackingId(); Integer replacementId = sampleTrackingMapper.selectNextProjectInfoTrackingId();
@ -433,6 +454,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (Objects.equals(oldTrackingProjectPartId, newTrackingProjectPartId)) { if (Objects.equals(oldTrackingProjectPartId, newTrackingProjectPartId)) {
return; return;
} }
// 与项目ID对齐策略一致先腾挪占位记录再执行当前记录主键对齐
Integer existedId = sampleTrackingMapper.existsProjectPartTrackingId(newTrackingProjectPartId); Integer existedId = sampleTrackingMapper.existsProjectPartTrackingId(newTrackingProjectPartId);
if (existedId != null && !Objects.equals(existedId, oldTrackingProjectPartId)) { if (existedId != null && !Objects.equals(existedId, oldTrackingProjectPartId)) {
Integer replacementId = sampleTrackingMapper.selectNextProjectPartTrackingId(); Integer replacementId = sampleTrackingMapper.selectNextProjectPartTrackingId();
@ -451,6 +473,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (Objects.equals(oldTrackingProofingId, newTrackingProofingId)) { if (Objects.equals(oldTrackingProofingId, newTrackingProofingId)) {
return; return;
} }
// 打样ID冲突处理除主表外还要同步维护日志与文档表引用保证关联关系不断链
Integer existedId = sampleTrackingMapper.existsProofTrackingId(newTrackingProofingId); Integer existedId = sampleTrackingMapper.existsProofTrackingId(newTrackingProofingId);
if (existedId != null && !Objects.equals(existedId, oldTrackingProofingId)) { if (existedId != null && !Objects.equals(existedId, oldTrackingProofingId)) {
Integer replacementId = sampleTrackingMapper.selectNextProofTrackingId(); Integer replacementId = sampleTrackingMapper.selectNextProofTrackingId();
@ -474,6 +497,8 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (trackingProjectId == null || trackingProjectPartId == null) { if (trackingProjectId == null || trackingProjectPartId == null) {
throw new RuntimeException("打样记录缺少项目或项目物料ID,请先同步项目和物料"); throw new RuntimeException("打样记录缺少项目或项目物料ID,请先同步项目和物料");
} }
// 这里不是仅校验存在还要校验关系一致
// 防止脏数据把打样同步到错误的项目/物料下
ProjectInfoTrackingData trackingProject = sampleTrackingMapper.selectProjectInfoTrackingById(trackingProjectId); ProjectInfoTrackingData trackingProject = sampleTrackingMapper.selectProjectInfoTrackingById(trackingProjectId);
if (trackingProject == null) { if (trackingProject == null) {
throw new RuntimeException("项目追踪记录不存在: " + trackingProjectId); throw new RuntimeException("项目追踪记录不存在: " + trackingProjectId);
@ -533,6 +558,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
proofingDocumentData.setProjectPartId(syncedTracking.getProjectPartId()); proofingDocumentData.setProjectPartId(syncedTracking.getProjectPartId());
proofingDocumentData.setcProjectTypeDb(syncedTracking.getProjectCategory()); proofingDocumentData.setcProjectTypeDb(syncedTracking.getProjectCategory());
proofingDocumentData.setCreateBy(resolveOperator(syncedTracking.getCreateBy(), operator)); proofingDocumentData.setCreateBy(resolveOperator(syncedTracking.getCreateBy(), operator));
// 复用现有文档初始化服务避免在 sampleTracking 侧重复维护文档生成规则
proofingInformationService.proofingDocumentSave(proofingDocumentData); proofingInformationService.proofingDocumentSave(proofingDocumentData);
} }
@ -543,6 +569,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
try { try {
ProofingInformationData mailData = new ProofingInformationData(); ProofingInformationData mailData = new ProofingInformationData();
BeanUtils.copyProperties(syncedPart, mailData); BeanUtils.copyProperties(syncedPart, mailData);
// 邮件模板字段分散在项目/物料两张 tracking 物料优先项目兜底组装
ProjectInfoTrackingData trackingProject = sampleTrackingMapper.selectProjectInfoTrackingById(syncedPart.getProjectId()); ProjectInfoTrackingData trackingProject = sampleTrackingMapper.selectProjectInfoTrackingById(syncedPart.getProjectId());
if (trackingProject != null) { if (trackingProject != null) {
if (isBlank(mailData.getProjectNo())) { if (isBlank(mailData.getProjectNo())) {
@ -580,6 +607,7 @@ public class SampleTrackingServiceImpl implements SampleTrackingService {
if (syncedPart == null) { if (syncedPart == null) {
return; return;
} }
// 使用快照而不是原对象避免事务提交前后对象被其他逻辑修改
ProjectPartTrackingData mailSnapshot = new ProjectPartTrackingData(); ProjectPartTrackingData mailSnapshot = new ProjectPartTrackingData();
BeanUtils.copyProperties(syncedPart, mailSnapshot); BeanUtils.copyProperties(syncedPart, mailSnapshot);
if (TransactionSynchronizationManager.isSynchronizationActive()) { if (TransactionSynchronizationManager.isSynchronizationActive()) {

Loading…
Cancel
Save