|
|
|
@ -756,16 +756,41 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description 推送盘点单到WCS(首次推送,推前10个栈板)- rqrq |
|
|
|
* @Description 推送盘点单到WCS(首次推送)- rqrq |
|
|
|
* |
|
|
|
* <p><b>业务逻辑:</b></p> |
|
|
|
* <p><b>业务逻辑(重要!请仔细阅读):</b></p> |
|
|
|
* <pre> |
|
|
|
* 【循环盘点(CYCLE)】: |
|
|
|
* 1. 首次推送1个任务,包含10个栈板 |
|
|
|
* 2. 任务完成后自动推送下一批10个栈板 |
|
|
|
* 3. 始终保持最多1个任务在流转 |
|
|
|
* |
|
|
|
* 【手工盘点(MANUAL)】: |
|
|
|
* 1. 首次推送策略(根据总栈板数决定): |
|
|
|
* - 总栈板数 ≤ 5:推送1个任务(包含所有栈板) |
|
|
|
* - 总栈板数 > 5:推送2个任务(每个任务最多5个栈板) |
|
|
|
* 例如:8个栈板 → 任务1(5个)+ 任务2(3个) |
|
|
|
* 15个栈板 → 任务1(5个)+ 任务2(5个),剩余5个等后续推送 |
|
|
|
* 2. 因为手工盘点有2个通道,可以2个人同时操作 |
|
|
|
* 3. 每个任务完成后,自动推送下一批5个栈板 |
|
|
|
* 4. 始终保持最多2个任务同时流转 |
|
|
|
* |
|
|
|
* 【推送流程】: |
|
|
|
* 1. 校验盘点单状态必须为"已下达"(RELEASED) |
|
|
|
* 2. 生成wms_order_task单据,明细是各个栈板 |
|
|
|
* 3. 每次推送10个栈板,尽量每层都有栈板(按层轮询选取) |
|
|
|
* 4. 推送成功后状态改为"盘点中"(CHECKING) |
|
|
|
* 5. 第10个栈板出库后,由其他方法调用continuePushCount推送后续栈板 |
|
|
|
* 2. 根据盘点类型执行不同的推送策略 |
|
|
|
* 3. 生成wms_order_task单据,明细是各个栈板 |
|
|
|
* 4. 推送给WCS,由立库系统执行出库 |
|
|
|
* 5. 推送成功后更新盘点单状态为"盘点中"(CHECKING) |
|
|
|
* |
|
|
|
* 【自动流转机制】: |
|
|
|
* - 当任务的所有托盘盘点完成后,会自动调用continuePushCount推送下一批 |
|
|
|
* - 形成"推送→盘点→推送→盘点"的自动循环 |
|
|
|
* - 直到所有托盘都推送完成 |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* @param query 推送参数(包含site、countNo、username) |
|
|
|
* @return int 本次推送的栈板总数 |
|
|
|
* @author rqrq |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
@ -781,11 +806,61 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
throw new RuntimeException("只有已下达状态的盘点单才能推送"); |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 执行推送 - rqrq |
|
|
|
int pushedCount = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 10); |
|
|
|
// 2. 根据盘点类型确定推送策略 - rqrq |
|
|
|
boolean isManual = CountHeader.COUNT_TYPE_MANUAL.equals(header.getCountType()); |
|
|
|
int totalPushedCount = 0; |
|
|
|
|
|
|
|
if (isManual) { |
|
|
|
// ========== 手工盘点:首次推送策略 ========== - rqrq |
|
|
|
log.info("手工盘点,执行手工盘点推送策略"); |
|
|
|
|
|
|
|
// 2.1 查询未推送的托盘总数 - rqrq |
|
|
|
List<CountPalletData> unpushedPallets = baseMapper.queryUnpushedPallets(query.getSite(), query.getCountNo()); |
|
|
|
int totalUnpushedCount = unpushedPallets != null ? unpushedPallets.size() : 0; |
|
|
|
log.info("未推送托盘总数: {}", totalUnpushedCount); |
|
|
|
|
|
|
|
if (totalUnpushedCount == 0) { |
|
|
|
log.warn("没有未推送的托盘,跳过推送"); |
|
|
|
return 0; |
|
|
|
} |
|
|
|
|
|
|
|
// 2.2 根据托盘总数决定推送策略 - rqrq |
|
|
|
if (totalUnpushedCount <= 5) { |
|
|
|
// 托盘数≤5:推送1个任务(包含所有托盘)- rqrq |
|
|
|
log.info("托盘数≤5,推送1个任务(包含所有托盘)"); |
|
|
|
int pushedCount = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 5); |
|
|
|
totalPushedCount += pushedCount; |
|
|
|
log.info("推送完成,推送数量: {}", pushedCount); |
|
|
|
} else { |
|
|
|
// 托盘数>5:推送2个任务(每个最多5个托盘)- rqrq |
|
|
|
log.info("托盘数>5,推送2个任务(每个最多5个托盘)"); |
|
|
|
|
|
|
|
// 推送第1个任务(5个托盘)- rqrq |
|
|
|
log.info("开始推送第1个任务"); |
|
|
|
int pushedCount1 = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 5); |
|
|
|
totalPushedCount += pushedCount1; |
|
|
|
log.info("第1个任务推送完成,推送数量: {}", pushedCount1); |
|
|
|
|
|
|
|
// 推送第2个任务(最多5个托盘)- rqrq |
|
|
|
// 只有第1个任务推送成功,才推送第2个(避免第1个失败后第2个还推送)- rqrq |
|
|
|
if (pushedCount1 > 0) { |
|
|
|
log.info("开始推送第2个任务"); |
|
|
|
int pushedCount2 = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 5); |
|
|
|
totalPushedCount += pushedCount2; |
|
|
|
log.info("第2个任务推送完成,推送数量: {}", pushedCount2); |
|
|
|
} else { |
|
|
|
log.warn("第1个任务推送数量为0,跳过第2个任务推送"); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
// ========== 循环盘点:推送1个任务(10个托盘)========== - rqrq |
|
|
|
log.info("循环盘点,推送1个任务(10个托盘)"); |
|
|
|
totalPushedCount = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 10); |
|
|
|
log.info("推送完成,推送数量: {}", totalPushedCount); |
|
|
|
} |
|
|
|
|
|
|
|
// 3. 更新盘点单状态为"盘点中" - rqrq |
|
|
|
if (pushedCount > 0) { |
|
|
|
if (totalPushedCount > 0) { |
|
|
|
CountHeader updateHeader = new CountHeader(); |
|
|
|
updateHeader.setSite(query.getSite()); |
|
|
|
updateHeader.setCountNo(query.getCountNo()); |
|
|
|
@ -793,23 +868,70 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
updateHeader.setModifiedBy(query.getUsername()); |
|
|
|
baseMapper.updateCountHeaderStatus(updateHeader); |
|
|
|
log.info("盘点单状态已更新为CHECKING"); |
|
|
|
} else { |
|
|
|
log.warn("推送数量为0,未更新盘点单状态"); |
|
|
|
} |
|
|
|
|
|
|
|
log.info("pushCountToWcs 结束,本次推送栈板数: {}", pushedCount); |
|
|
|
return pushedCount; |
|
|
|
log.info("pushCountToWcs 结束,本次推送栈板总数: {}", totalPushedCount); |
|
|
|
return totalPushedCount; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description 继续推送盘点单到WCS(推后续10个栈板,由其他交互调用)- rqrq |
|
|
|
* @Description 继续推送盘点单到WCS(自动推送下一批,由任务完成后自动触发)- rqrq |
|
|
|
* |
|
|
|
* <p><b>业务逻辑:</b></p> |
|
|
|
* <p><b>业务逻辑(重要!请仔细阅读):</b></p> |
|
|
|
* <pre> |
|
|
|
* 【调用时机】: |
|
|
|
* - 由handleTaskAfterCount方法在任务完成后自动调用 |
|
|
|
* - 不是手动调用,而是自动触发的 |
|
|
|
* |
|
|
|
* 【循环盘点(CYCLE)】: |
|
|
|
* 1. 校验未完成任务数:最多允许1个任务 |
|
|
|
* 2. 有未完成任务时,直接返回0(不推送) |
|
|
|
* 3. 无未完成任务时,推送下一批10个栈板 |
|
|
|
* |
|
|
|
* 【手工盘点(MANUAL)】: |
|
|
|
* 1. 校验未完成任务数:最多允许2个任务 |
|
|
|
* 2. 已有2个未完成任务时,直接返回0(不推送,等待其他任务完成) |
|
|
|
* 3. 未完成任务数<2时,推送下一批5个栈板 |
|
|
|
* 4. 形成动态平衡:始终保持最多2个任务同时流转 |
|
|
|
* |
|
|
|
* 【流转示例 - 手工盘点15个托盘】: |
|
|
|
* 首次:推送任务1(5个)+ 任务2(5个) |
|
|
|
* ↓ |
|
|
|
* 任务1完成 → continuePushCount触发 |
|
|
|
* ├─ 检查未完成任务数:1个(任务2) |
|
|
|
* ├─ 判断:1 < 2(允许推送)✅ |
|
|
|
* └─ 推送任务3(5个) |
|
|
|
* 现在:任务2 + 任务3 并行 |
|
|
|
* ↓ |
|
|
|
* 任务2完成 → continuePushCount触发 |
|
|
|
* ├─ 检查未完成任务数:1个(任务3) |
|
|
|
* ├─ 判断:1 < 2(允许推送)✅ |
|
|
|
* ├─ 查询未推送托盘数:0个 |
|
|
|
* └─ 返回0(无更多托盘) |
|
|
|
* ↓ |
|
|
|
* 任务3完成 → continuePushCount触发 |
|
|
|
* └─ 查询未推送托盘数:0个 |
|
|
|
* └─ 返回0(无更多托盘) |
|
|
|
* |
|
|
|
* 【并发控制机制】: |
|
|
|
* - 通过查询未完成任务数来控制并发 |
|
|
|
* - 循环盘点:最多1个任务(单通道) |
|
|
|
* - 手工盘点:最多2个任务(双通道) |
|
|
|
* - 超过上限时返回0,不抛异常(因为是自动触发,不需要中断流程) |
|
|
|
* |
|
|
|
* 【推送流程】: |
|
|
|
* 1. 校验盘点单状态必须是"盘点中" |
|
|
|
* 2. 校验是否存在未完成的任务单(防止重复下达) |
|
|
|
* 3. 查询未推送的栈板(task_no为空的) |
|
|
|
* 4. 每次推送10个栈板,尽量每层都有栈板 |
|
|
|
* 2. 查询当前未完成的任务数 |
|
|
|
* 3. 判断是否达到并发上限 |
|
|
|
* 4. 未达到上限则推送下一批栈板 |
|
|
|
* 5. 生成wms_order_task单据并推送给WCS |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* @param query 推送参数(包含site、countNo、username) |
|
|
|
* @return int 本次推送的栈板数(0表示未推送或无更多托盘) |
|
|
|
* @author rqrq |
|
|
|
*/ |
|
|
|
@Override |
|
|
|
@Transactional(rollbackFor = Exception.class) |
|
|
|
@ -825,17 +947,41 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
throw new RuntimeException("盘点单状态不是盘点中,终止盘点"); |
|
|
|
} |
|
|
|
|
|
|
|
// 2. 校验是否存在未完成的任务单(状态不是已完成或已取消)- rqrq |
|
|
|
// 2. 根据盘点类型确定推送策略 - rqrq |
|
|
|
String countType = header.getCountType(); |
|
|
|
boolean isCycle = CountHeader.COUNT_TYPE_CYCLE.equals(countType); |
|
|
|
|
|
|
|
// 2.1 确定最大允许的并发任务数 - rqrq |
|
|
|
int maxAllowedTasks = isCycle ? 1 : 2; // 循环盘点最多1个,手工盘点最多2个 |
|
|
|
|
|
|
|
// 2.2 确定每批推送数量 - rqrq |
|
|
|
int maxCount = isCycle ? 10 : 5; // 循环盘点每批10个,手工盘点每批5个 |
|
|
|
|
|
|
|
log.info("盘点类型: {}, 最大并发任务数: {}, 单批推送数量: {}", |
|
|
|
countType, maxAllowedTasks, maxCount); |
|
|
|
|
|
|
|
// 3. 校验未完成任务数(根据盘点类型判断)- rqrq |
|
|
|
int uncompletedTaskCount = baseMapper.countUncompletedTask(query.getSite(), query.getCountNo()); |
|
|
|
if (uncompletedTaskCount > 0) { |
|
|
|
throw new RuntimeException("当前盘点单存在" + uncompletedTaskCount + "个未完成的任务单,请等待任务完成后再继续下达"); |
|
|
|
log.info("当前未完成任务数: {}", uncompletedTaskCount); |
|
|
|
|
|
|
|
// 3.1 判断是否达到并发上限 - rqrq |
|
|
|
if (uncompletedTaskCount >= maxAllowedTasks) { |
|
|
|
String msg = String.format("当前盘点单存在%d个未完成的任务单,%s最多允许%d个任务同时流转,暂不推送下一批", |
|
|
|
uncompletedTaskCount, isCycle ? "循环盘点" : "手工盘点", maxAllowedTasks); |
|
|
|
log.info(msg); |
|
|
|
return 0; // 不抛异常,直接返回0(因为是自动触发,不需要中断流程) |
|
|
|
} |
|
|
|
log.info("校验通过,无未完成的任务单"); |
|
|
|
log.info("校验通过,当前未完成任务数({})未达到上限({}), 可以推送下一批", |
|
|
|
uncompletedTaskCount, maxAllowedTasks); |
|
|
|
|
|
|
|
// 3. 执行推送 - rqrq |
|
|
|
int pushedCount = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), 10); |
|
|
|
// 4. 执行推送 - rqrq |
|
|
|
int pushedCount = doPushPallets(query.getSite(), query.getCountNo(), query.getUsername(), maxCount); |
|
|
|
|
|
|
|
log.info("continuePushCount 结束,本次推送栈板数: {}", pushedCount); |
|
|
|
if (pushedCount > 0) { |
|
|
|
log.info("continuePushCount 结束,本次推送栈板数: {}", pushedCount); |
|
|
|
} else { |
|
|
|
log.info("continuePushCount 结束,本次未推送(可能无更多待推送托盘)"); |
|
|
|
} |
|
|
|
return pushedCount; |
|
|
|
} |
|
|
|
|
|
|
|
@ -2393,12 +2539,69 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @Description 盘点完成后处理任务单(更新任务状态,触发下一批推送)- rqrq |
|
|
|
* |
|
|
|
* <p><b>调用时机:</b></p> |
|
|
|
* <pre> |
|
|
|
* - PDA盘点完成(pdaSubmitCount) |
|
|
|
* - RFID龙门架盘点完成(handleCountRfidCallback) |
|
|
|
* - 快速盘点完成(pdaQuickSubmitCount) |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* <p><b>业务逻辑(重要!请仔细阅读):</b></p> |
|
|
|
* <pre> |
|
|
|
* 【处理流程】: |
|
|
|
* 1. 查询该栈板关联的任务号(count_pallet.task_no) |
|
|
|
* 2. 如果没有任务号,说明不是通过推送出库的,跳过处理 |
|
|
|
* (例如:手工盘点中手动添加的托盘,不走推送流程) |
|
|
|
* 3. 更新wms_order_task_detail状态为"已完成" |
|
|
|
* 4. 检查该任务的所有明细是否都已完成 |
|
|
|
* 5. 如果都完成了,更新wms_order_task主表状态为"已完成" |
|
|
|
* 6. 触发continuePushCount自动推送下一批 |
|
|
|
* |
|
|
|
* 【自动流转机制】: |
|
|
|
* - 任务的所有托盘盘点完成后,自动推送下一批 |
|
|
|
* - 循环盘点:推送10个托盘 |
|
|
|
* - 手工盘点:推送5个托盘,但受2个任务并发限制 |
|
|
|
* - 形成"推送→盘点→推送→盘点"的自动循环 |
|
|
|
* |
|
|
|
* 【并发场景 - 手工盘点】: |
|
|
|
* 场景1:任务1和任务2同时执行 |
|
|
|
* - 任务1的最后一个托盘盘点完成 → 触发推送任务3 |
|
|
|
* - 任务2还在执行 |
|
|
|
* - 结果:任务2 + 任务3 并行(2个任务)✅ |
|
|
|
* |
|
|
|
* 场景2:已有2个任务在执行 |
|
|
|
* - 任务2的最后一个托盘盘点完成 → 尝试推送 |
|
|
|
* - 任务3还在执行 |
|
|
|
* - continuePushCount检查:已有1个未完成任务,但<2,可以推送 |
|
|
|
* - 如果有剩余托盘,推送任务4 |
|
|
|
* - 如果无剩余托盘,返回0 |
|
|
|
* |
|
|
|
* 【防重复调用】: |
|
|
|
* - 检查主表状态,如果已是"已完成",跳过触发推送 |
|
|
|
* - 避免重复调用导致重复推送 |
|
|
|
* |
|
|
|
* 【异常处理】: |
|
|
|
* - continuePushCount失败不抛异常 |
|
|
|
* - 避免影响当前托盘的盘点结果保存 |
|
|
|
* - 只记录日志,不中断流程 |
|
|
|
* </pre> |
|
|
|
* |
|
|
|
* @param site 工厂编码 |
|
|
|
* @param countNo 盘点单号 |
|
|
|
* @param palletId 托盘号 |
|
|
|
* @param username 用户名 |
|
|
|
* @author rqrq |
|
|
|
*/ |
|
|
|
private void handleTaskAfterCount(String site, String countNo, String palletId, String username) { |
|
|
|
// 1. 查询该栈板关联的任务号 - rqrq |
|
|
|
String taskNo = baseMapper.getTaskNoByPallet(site, countNo, palletId); |
|
|
|
|
|
|
|
if (!StringUtils.hasText(taskNo)) { |
|
|
|
// 没有任务号,说明不是通过推送出库的,不需要处理任务单 - rqrq |
|
|
|
// 例如:手工盘点中手动添加的托盘,不走推送流程 - rqrq |
|
|
|
log.info("该栈板没有关联任务号,跳过任务单处理,palletId: {}", palletId); |
|
|
|
return; |
|
|
|
} |
|
|
|
@ -2409,26 +2612,45 @@ public class PhysicalInventoryServiceImpl extends ServiceImpl<PhysicalInventoryM |
|
|
|
baseMapper.updateTaskDetailStatusByPallet(site, taskNo, palletId); |
|
|
|
log.info("已更新wms_order_task_detail状态为已完成,palletId: {}", palletId); |
|
|
|
|
|
|
|
// 3. 检查该任务的所有明细是否都已完成,且主表状态未完成时才触发下一批推送 - rqrq |
|
|
|
// 3. 检查该任务的所有明细是否都已完成 - rqrq |
|
|
|
String taskStatus = baseMapper.getWmsOrderTaskStatus(site, taskNo); |
|
|
|
if ("已完成".equals(taskStatus)) { |
|
|
|
// 任务已完成,说明是重复调用,不再触发下一批推送 - rqrq |
|
|
|
log.info("wms_order_task已是已完成状态,跳过触发下一批推送(可能是重复调用)"); |
|
|
|
} else { |
|
|
|
int uncompleteCount = baseMapper.checkAllTaskDetailCompletedByTaskNo(site, taskNo); |
|
|
|
if (uncompleteCount == 0) { |
|
|
|
// 所有明细都已完成,更新主表状态 - rqrq |
|
|
|
baseMapper.updateTaskStatusCompleted(site, taskNo); |
|
|
|
log.info("该任务所有明细已完成,主表状态已更新为已完成"); |
|
|
|
|
|
|
|
// 4. 触发下一批推送 - rqrq |
|
|
|
CountHeaderData query = new CountHeaderData(); |
|
|
|
query.setSite(site); |
|
|
|
query.setCountNo(countNo); |
|
|
|
query.setUsername(username); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 3.1 查询未完成的明细数量 - rqrq |
|
|
|
int uncompleteCount = baseMapper.checkAllTaskDetailCompletedByTaskNo(site, taskNo); |
|
|
|
log.info("该任务未完成的明细数量: {}", uncompleteCount); |
|
|
|
|
|
|
|
if (uncompleteCount == 0) { |
|
|
|
// 所有明细都已完成,更新主表状态 - rqrq |
|
|
|
log.info("该任务所有明细已完成,准备更新主表状态并触发下一批推送"); |
|
|
|
baseMapper.updateTaskStatusCompleted(site, taskNo); |
|
|
|
log.info("主表状态已更新为已完成"); |
|
|
|
|
|
|
|
// 4. 触发下一批推送(循环盘点和手工盘点都自动推送)- rqrq |
|
|
|
CountHeaderData query = new CountHeaderData(); |
|
|
|
query.setSite(site); |
|
|
|
query.setCountNo(countNo); |
|
|
|
query.setUsername(username); |
|
|
|
|
|
|
|
try { |
|
|
|
log.info("开始触发下一批推送"); |
|
|
|
int pushedCount = continuePushCount(query); |
|
|
|
log.info("触发下一批推送,推送数量: {}", pushedCount); |
|
|
|
|
|
|
|
if (pushedCount > 0) { |
|
|
|
log.info("✅ 触发下一批推送成功,推送数量: {}", pushedCount); |
|
|
|
} else { |
|
|
|
log.info("ℹ️ 当前未推送(可能已达到并发上限或无待推送托盘)"); |
|
|
|
} |
|
|
|
} catch (Exception e) { |
|
|
|
// 不抛异常,避免影响当前托盘的盘点结果保存 - rqrq |
|
|
|
log.error("❌ 触发下一批推送失败,但不影响当前托盘盘点结果: {}", e.getMessage(), e); |
|
|
|
} |
|
|
|
} else { |
|
|
|
log.info("该任务还有{}个明细未完成,等待其他托盘盘点", uncompleteCount); |
|
|
|
} |
|
|
|
} |
|
|
|
} |