28 KiB
WaveRuleMatchService 流程文档
对应实现:
C:\work\gitlab\shopee\wes-loghub\wms-wave\src\main\groovy\com\ittx\wms\wave\service\hairo\WaveRuleMatchService.groovy本文档描述该服务的当前代码流程,按调用入口到出口的完整链路展开,包括主流程、分流流程、补缺口流程、回滚流程和详细的规则匹配机制。
目录
- 服务概述
- 入口与总流程
- 前置校验与并发控制
- 一波一单分流流程
- 推拣货任务当前口径
- 标准集波主流程
- 格口分配策略
- Shopee 波次绑定与 ESS 校验
- 内部波次创建与运行
- 回滚机制
- 首次集波内补缺口流程
- Flow Pick 换箱换波流程
- 字段匹配规则(WAVE_RULE_FIELD_MATCHING)
- 方法调用关系图
1. 服务概述
WaveRuleMatchService 是 Shopee 出库集波的核心服务,职责:
- 接收「工作站 + 出库类型」请求
- 查询启用的波次规则(
wave_rule),按优先级排序 - 查找工作站下可用的分播墙格口(
wcs_sorting_wall_cell) - 从订单池(
shipment_header)中匹配符合条件的出库单 - 完成 Shopee 波次号绑定、ESS 合波校验、内部波次创建与运行
- 首次集波成功返回
ResponseMessageFactory.success(),不返回「格口 + 命中规则 + 波次」明细
三种分流入口:
| 分支 | 条件 | 处理方式 |
|---|---|---|
| 一波一单分流 | pickType=1、XSCK+ticketType=1、ticketType=5、ticketType=6 |
一单一个内部波次;RT 不使用 Shopee 波次号 |
| 已绑波次格口补缺口 | 空闲格口已绑定 shopeeWaveCode 且 currentWaveRule 匹配当前规则 |
按 low_threshold/maxShipments 计算缺口后补单 |
| 空格口首次分配 | 其他普通批量候选单 | 按规则优先级逐规则匹配,先抢占格口,再绑定 Shopee 波次,再创建并运行内部波次 |
2. 入口与总流程
外部入口
POST wms/automation/waveRuleMatch/findBestMatchedRule
→ WaveRuleMatchController.findBestMatchedRule(map)
→ WaveRuleMatchService.findBestMatchedRuleByWorkStationAndOutboundType(warehouseCode, workStation, shipmentType)
调用链总图
findBestMatchedRuleByWorkStationAndOutboundType(String, String, String)
│
├─ 参数校验(warehouseCode / workStation / shipmentType 非空)
├─ findEnabledWorkStation(warehouseCode, workStation)
│ └─ 支持工作站编码 或 数字 ID 查询
│
└─ findBestMatchedRuleByWorkStationAndOutboundType(Long, String) ← 统一入口
│
├─ [1] 工作站校验:存在 / 启用 ENABLE / 仓库匹配
├─ [2] hasDifferentActiveShipmentType
│ → 已占用 USED 格口关联 Shopee 波次反查当前工作站作业出库类型
│ → 有其它出库类型时记录系统处理日志并返回 MSG_WRM_0014
│
├─ [3] buildRuleMatchLockKey → Redis 锁(仓库+工作站+出库类型)
│ 失败 → MSG_WRM_0012 "工作站正在集波中"
│
├─ [4] findEnabledRules(warehouseCode)
│ → wave_rule WHERE warehouseCode=? AND status=ENABLE ORDER BY wavePriority DESC
│ 无规则 → MSG_WRM_0007
│
├─ [5] assignSingleShipmentWaves(先尝试一波一单候选)
│ → pickType=1 / ticketType=1,5,6
│
├─ [6] fillShortageCells(已绑定 Shopee 波次格口补缺口)
│ → 空闲 + shopeeWaveCode 有值 + currentWaveRule 匹配
│
├─ [7] assignEmptyCells(空格口首次分配)
│ → 普通批量候选单进入空格口分配
│
└─ [8] 成功返回 ResponseMessageFactory.success()
3. 前置校验与并发控制
3.1 工作站校验
findEnabledWorkStation(warehouseCode, workStation)
- 入参
workStation兼容 编码(String)和 数字 ID(自动识别) - 校验:
status=ENABLE、warehouseCode匹配 - 失败返回对应
MSG_WRM_0004/0005/0006
3.2 Redis 锁
buildRuleMatchLockKey → "wave_rule_match:{warehouseCode}:{workstationId}:{shipmentType}"
RedissonLockService.getAndTryLock(lockKey)
- 锁粒度:仓库 + 工作站 + 出库类型
- 获取失败时不阻塞等待,直接返回
MSG_WRM_0012(前端可提示用户稍后重试) - 在 finally 块中
WmsRedissionLockService.unlock(lock)
3.3 同工作站出库类型隔离
hasDifferentActiveShipmentType(warehouseCode, workstationId, shipmentType)
→ findActiveWorkstationShipmentTypes(warehouseCode, workstationId)
- 查询当前工作站已占用的
USED格口。 - 通过格口
shopeeWaveCode关联shipment_header.shopeeWave,获取当前工作站正在作业的shipmentType。 - 若存在非本次请求的出库类型,入口直接拒绝本次集波。
- 拒绝时记录系统处理日志,并返回
MSG_WRM_0014。 - 该校验位于 Redis 锁之前,避免无效请求占用集波锁。
4. 一波一单分流流程
触发条件
pickType=1、XSCK+ticketType=1、ticketType=5、ticketType=6
方法
assignSingleShipmentWaves(warehouseCode, shipmentType, workstationId, rules)
流程图
for each WaveRule:
findSingleShipmentWaveShipments(warehouseCode, shipmentType, rule)
→ buildAvailableShipmentSql + appendSingleWaveFilter + appendRuleFieldMatching
→ LIMIT 1(只取 1 单)
if shipment found:
createInternalWave(warehouseCode, rule) → 创建内部波次
addShipmentsToInternalWave([shipmentId], waveId)
→ ShipmentHeaderService.addMultipleToWave + WaveService.run
return success
无任何规则命中 → MSG_WRM_0010
特点
- RT 不使用 Shopee 波次号(
shopeeWaveCode返回空字符串"") - 当前一波一单分流不走 ESS 校验
- 不走格口分配(不绑定格口)
- 一单一个内部波次
5. 推拣货任务当前口径
触发条件
pickType=2(出库单由上游推入时设置,同时预写入 shopeeWave)当前作为待恢复的独立分流口径。
方法
当前代码中未保留 assignPickingTaskSingleWaves 和 findPickingTaskShipments 方法。
流程图
当前已落地:
appendBatchWaveFilter 排除 pickType=2,避免推拣货任务进入普通批量集波。
待恢复:
上游推单写入 pickType=2 + shopeeWave 后,
WES 独立分流入口按一单一波一槽口处理。
特点
| 特性 | 说明 |
|---|---|
| 波次号来源 | 上游预写入 shipment_header.shopeeWave,WES 不走取号逻辑 |
| 当前状态 | 普通批量集波已过滤 pickType=2 |
| 待实现 | 独立分流入口、一单一波一槽口、内部波次运行 |
| 补单 | 后续仍按“不走补单逻辑”处理 |
6. 标准集波主流程
6.1 主循环
for (WaveRule rule : rules) {
List<WcsSortingWallCell> cells = findCells(workstationId)
// 三层级分组
List<List<WcsSortingWallCell>> cellGroups = [
sameCurRuleCells, // currentWaveRule == rule.code
ruleCells, // currentWaveRule 为空 && waveRule 匹配
emptyCells // 两者均为空
]
for (group : cellGroups) {
assignCellNumbers(warehouseCode, shipmentType, workstationId, rule, group)
// 命中即返回,不继续尝试下一组格口
}
}
6.2 assignCellNumbers 核心流程
assignCellNumbers(warehouseCode, shipmentType, workstationId, rule, cells):
│
├─ 1. findSingleShipmentWaveShipments → 查一波一单候选
│ → pickType=1 / XSCK+ticketType=1 / ticketType=5 / RT
│ 有 → singleShipmentWave=true, 取 1 单
│ 无 → 继续
│
├─ 2. findAvailableShipments → 查批量集波候选
│ → 排除 pickType=1,2 + 排除一波一单 ticketType
│ → 规则字段匹配 + 订单结构匹配 + kickOutWave 过滤
│ → LIMIT = maxShipments(默认 30)
│
├─ 3. 仍无候选 → MSG_WRM_0010
│
├─ 4. logPriorityRuleConsumption — 记录优先级消费日志
│
├─ 5. createInternalWave(warehouseCode, rule) → 创建内部波次
│ 失败 → MSG_WRM_0009
│
└─ 6. for each cell in cells:
│
├─ 取出 cell.shopeeWaveCode
│
├─ [一波一单] singleShipmentWave=true && cell 已有波次号 → continue(不补入已用格口)
│
├─ [已有波次] cell 已有波次号 → skipSingleRpln 检查
│ → 单品单件 且 未低于 low_threshold → continue
│
├─ [无波次号] → acquireUnusedShopeeWave → 取新号
│ 失败 → rollbackInternalWaveIfEmpty + MSG_WRM_0009
│
├─ [补充新单] 已有波次号 + 非单品单件/低于阈值
│ → findAvailableShipments(..., shopeeWaveCode) 过滤 kickOutWave
│
├─ assignToCell:
│ ├─ pickCellShipmentIds → 按格口缺口截断(单品单件=maxShipments, 其他=差额)
│ ├─ ensureCellReadyForWave → 先抢占格口 USED
│ ├─ bindShipmentShopeeWave → Shopee 波次绑定接口;成功整批写 shopeeWave,失败整批写 kickOutWave
│ ├─ addShipmentsToInternalWave → 加入波次 + 运行
│ └─ 成功后消费当前候选池
│
└─ 一个格口消费一批后 break(不再占用其他格口)
6.3 订单池 SQL 基础条件
SELECT id FROM shipment_header
WHERE warehouseCode = :warehouseCode
AND shipmentType = :shipmentType
AND leadingSts = 100 -- 订单池状态
AND trailingSts = 100 -- 订单池状态
AND processType = 'NORMAL' -- 正常处理流程
AND ifnull(waveId, 0) = 0 -- 未加入任何波次
AND (lockCode IS NULL OR trim(lockCode) = '') -- 未被锁定
AND (cancelTime IS NULL OR cancelTime = '') -- 未取消
AND (holdTime IS NULL OR holdTime = '') -- 未挂起
7. 格口分配策略
7.1 三层级匹配
| 层级 | 方法 | 条件 | 优先级 |
|---|---|---|---|
| 1 | matchCurRuleCells |
currentWaveRule == rule.code |
最高 |
| 2 | matchRuleCells |
currentWaveRule 为空 且 waveRule 逗号分隔匹配 rule.code |
中 |
| 3 | matchEmptyCells |
currentWaveRule 和 waveRule 均为空 |
兜底 |
7.2 格口抢占
occupySortingWallCell(warehouseCode, cell, rule, shopeeWaveCode)
UPDATE wcs_sorting_wall_cell
SET useStatus = 'USED',
shopeeWaveCode = ?,
currentWaveRule = ?,
lastUpdatedBy = ?
WHERE id = ?
AND warehouseCode = ?
AND status = 'ENABLE'
AND useStatus = 'IDLE'
- 使用乐观锁(
useStatus='IDLE'条件),同一格口只能被一个请求抢占成功 - 已有相同 Shopee 波次和规则的 USED 格口通过
ensureCellReadyForWave复用(计划任务补单场景)
7.3 格口查询
findCells(workstationId)
→ 找出工作站下所有分播墙 -> 各墙下启用格口 -> 过滤 IDLE 状态格口
8. Shopee 波次绑定与 ESS 校验
8.1 波次号抢占
acquireUnusedShopeeWave(warehouseCode, waveType)
- 查询
shopee_wave表:warehouseCode+waveType+status=100(UNUSED),按 id 升序取前 20 - 逐条尝试乐观更新:
SET status=200(USING) WHERE id=? AND status=100 - 更新成功的第一条即抢占成功,标记
processStamp='CLAIMED_UNUSED' - 全部失败返回 null(号段耗尽)
8.2 Shopee 波次绑定
bindShipmentShopeeWave(warehouseCode, shipmentIds, shopeeWaveCode)
流程:
1. buildShopeeWaveBindLockKey
→ 同一 Shopee 波次号绑定串行,避免多个入口同时使用同一号段。
2. isShopeeWaveCodeAvailable
→ 获取锁后重新检查 shopee_wave 是否仍可用。
3. requestShopeeWaveBind
→ 预留真实上游绑定接口。
→ 当前口径只有整批成功或整批失败,不处理部分成功。
4. 失败单据 → markKickOutWave(shipmentIds, shopeeWaveCode)
→ UPDATE shipment_header SET kickOutWave = CONCAT(已有的, ',', 新波次号)
→ 逗号分隔,同波次号去重
5. 成功单据 → UPDATE shipment_header SET shopeeWave = ? WHERE id IN (...)
→ 同步更新 shopee_wave.status/pickType/groupKey
6. 返回 ResponseMessageFactory.success()
8.3 接口对接状态
requestShopeeWaveBind 当前预留真实接口调用位置:
接口成功 → 本批出库单全部成功绑定 shopeeWave
接口失败 → 本批出库单全部写入 kickOutWave
正式接口契约确认后,只需补齐 requestShopeeWaveBind 的真实调用;当前不再保留柔性拒单解析方法。
9. 内部波次创建与运行
9.1 createInternalWave
createInternalWave(warehouseCode, rule)
resolveMasterCode(warehouseCode)— 从系统参数读取波次主表编码(WCS/EQUIPMENT_MANUFACTURER)waveSvc.createInternalWave(session, warehouseCode, masterCode, ruleCode)— 创建内部波次- 返回 waveId(Long)
- 失败返回 null
9.2 addShipmentsToInternalWave
addShipmentsToInternalWave(shipmentIds, waveId)
shipmentHeaderService.addMultipleToWave(session, shipmentIds, waveId)— 加入波次waveSvc.run(session, waveId)— 异步提交wms.wave队列消息- 任一步失败返回错误
9.3 波次运行结果
WaveService#run 只提交异步队列消息并返回提交结果,WaveRuleMatchService 不解析同步库存不足踢单明细。
库存不足踢单由 wms-wave 回池链路处理:回池时清空出库单头 shopeeWave/waveRule,不记录 kickOutWave;上游撤单接口仍需在回池链路接入。
10. 回滚机制
回滚场景与对应方法
| 场景 | 回滚方法 | 操作 |
|---|---|---|
| Shopee 波次绑定失败 | rollbackShipmentShopeeWave |
回退 shipment_header.shopeeWave = null(仅当 waveId=0 时) |
| 内部波次空无单据 | rollbackInternalWaveIfEmpty |
UPDATE wave SET status=999(标记异常,不删除数据) |
| 格口抢占失败 | rollbackWaveShipments + 逐单 rollbackShipmentShopeeWave |
从内部波次剔除 + 回退 Shopee 波次号 |
| ESS 拒绝 | markKickOutWave |
写入 kickOutWave,同波次后续补单过滤 |
| 库存不足踢单 | ShipmentAllocationService.clearShopeeWaveBindingAfterKickOut |
清空 shopeeWave/waveRule,不写 kickOutWave |
| 内部波次加单失败 | removeShipmentsFromWave |
调用 ShipmentHeaderService.batchRemoveFromWave |
| 新 Shopee 波次未使用 | rollbackClaimedShopeeWaveIfUnused |
恢复 shopee_wave.status=100(仅当无任何引用时) |
| 旧波次无未完成任务 | markShopeeWaveCompletedIfNoOpenTask |
shopee_wave.status=300(COMPLETED) |
回滚原则
- 不删除数据:波次标记 999 而非 DELETE,Shopee 波次号恢复 100 而非删除
- 不取消出库单:
handleFlowPickChangeWaveFailure明确注释"不取消出库单" - 先绑后解:绑定顺序为 Shopee 波次号 → 格口 → 内部波次,回滚按反序执行
11. 首次集波内补缺口流程
11.1 入口
当前状态:pollReplenishment/pollReplenishmentOnce 已删除。补缺口能力在首次集波入口中通过 fillShortageCells 执行。
已具备的依赖方法:
fillShortageCells(...):入口内逐规则处理已绑定 Shopee 波次的可补格口。findShortageCells(Long workStationId, WaveRule rule):SQL 查询空闲、已绑定 Shopee 波次、currentWaveRule匹配当前规则的格口。calcCellNeedQty(...):计算单品单件或非单品单件缺口。assignToCell(...):先抢占格口,再请求 Shopee 波次绑定接口,再创建并运行内部波次。
入口不返回分配明细;成功、失败和跳过原因通过系统处理日志记录。
11.2 流程
findBestMatchedRuleByWorkStationAndOutboundType(...)
│
└─ fillShortageCells(warehouseCode, shipmentType, workstationId, rules)
│
└─ for each rule:
findShortageCells(workstationId, rule)
→ wcs_sorting_wall_cell
→ useStatus = IDLE
→ shopeeWaveCode 非空
→ currentWaveRule = rule.code
│
└─ for each cell:
calcCellNeedQty
findAvailableShipments(..., shopeeWaveCode, groupKey)
assignToCell
11.3 fillShortageCells 逻辑
fillShortageCells(warehouseCode, shipmentType, workstationId, rules):
│
├─ 参数校验(warehouseCode / shipmentType / workstationId / rules)
│
├─ for each rule:
│ ├─ findShortageCells → 查当前规则可补格口
│ └─ for each cell:
│ ├─ calcCellNeedQty → 计算缺口
│ │ ├─ 单品单件: 未拣货数量 < low_threshold → need = maxShipments
│ │ ├─ 非单品单件: need = maxShipments - 当前 Shopee 波次已绑单量
│ │ └─ need <= 0 → 跳过
│
├─ resolveShopeeWaveGroupKey → 同一 Shopee 波次只补相同 groupKey 单据
│
├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ → 排除 kickOutWave 过滤的已拒单
│
└─ assignToCell
├─ ensureCellReadyForWave → 先抢占格口
├─ bindShipmentShopeeWave → Shopee 波次绑定接口
├─ createInternalWave
└─ addShipmentsToInternalWave → WaveService#run
11.4 缺口计算
calcCellNeedQty(warehouseCode, rule, cell, shopeeWaveCode)
| 规则类型 | 触发条件 | 补单数量 |
|---|---|---|
| 单品单件 | < low_threshold |
maxShipments(一次性补满) |
| 非单品单件 | 始终尝试 | maxShipments - 当前波次已绑单量 |
| 新格口/无波次号 | — | 返回 null(不截断) |
12. Flow Pick 换箱换波流程
12.1 触发
当前 Flow Pick 换箱换波接口为占位流程,需结合真实触发入口恢复;原计划任务补单入口已删除。
isFlowPickCellFull:单品单件 + countShopeeShipments >= maxShipments
12.2 流程
changeFlowPickWave(workstation, cell, rule):
│
├─ 1. findFlowPickRemainingShipments
│ → 当前工作站 + 旧波次 + 规则下 未完成拣货任务的订单
│ 无 → markShopeeWaveCompletedIfNoOpenTask + 返回
│
├─ 2. requestFlowPickSealBox(封箱)
│ → essFlowPickService.sealBox(动态调用)
│ 未接入 → 记录日志,返回 true(继续本地换波)
│ 失败 → 终止
│
├─ 3. resolveShopeeWaveType(从剩余订单推断波次类型)
│
├─ 4. acquireUnusedShopeeWave(取新号段)
│ 失败 → 返回错误
│
├─ 5. requestFlowPickChangeBox(换箱)
│ → essFlowPickService.changeBoxWave(动态调用)
│ 未接入 → 记录日志,返回 true(继续本地回写)
│ 失败 → rollbackClaimedShopeeWaveIfUnused + handleFlowPickChangeWaveFailure + 返回错误
│
├─ 6. updateFlowPickShopeeWave(回写新波次号)
│ → UPDATE shipment_header SET shopeeWave = 新号
│ → UPDATE wcs_work_station_pick_task_header SET shopeeWave = 新号
│ → UPDATE wcs_sorting_wall_cell SET shopeeWaveCode = 新号
│
└─ 7. markShopeeWaveCompletedIfNoOpenTask(旧波次标记完成)
12.3 当前状态
| 步骤 | 实现状态 | 说明 |
|---|---|---|
| 剩余订单查询 | ✅ 已实现 | findFlowPickRemainingShipments |
| 封箱接口 | ⚠️ 动态调用占位 | essFlowPickService.sealBox(未接入时本地跳过) |
| 新波次取号 | ✅ 已实现 | acquireUnusedShopeeWave |
| 换箱接口 | ⚠️ 动态调用占位 | essFlowPickService.changeBoxWave(未接入时本地跳过) |
| 字段回写 | ✅ 已实现 | updateFlowPickShopeeWave |
| 旧波次完成 | ✅ 已实现 | markShopeeWaveCompletedIfNoOpenTask |
| 失败回滚 | ⚠️ 仅日志 | handleFlowPickChangeWaveFailure 仅记录日志,未实现 AGV 回库/订单池恢复 |
13. 字段匹配规则(WAVE_RULE_FIELD_MATCHING)
13.1 数据字典结构
通过数据字典表 config_detail + config_value 配置,recordType = 'WAVE_RULE_FIELD_MATCHING'。
| 字段 | 含义 | 示例 |
|---|---|---|
identifier |
出库单字段 | userDef1、shipmentCategory1、priority |
value1 |
规则字段 | channelId、shopId、urgentFlag |
value2 |
操作符 | IN / EQ / LE |
warehouseCode |
仓库级/全局 | 优先匹配当前仓,无配置时读取 * |
13.2 支持的操作符
| 操作符 | SQL 生成 | 说明 |
|---|---|---|
IN(默认) |
shipmentField IN (:values) |
规则值按逗号/中文逗号拆分多选 |
EQ / = / EQUAL / EQUALS |
shipmentField = :value |
等值匹配 |
LE / <= / LESS_OR_EQUAL |
ifnull(shipmentField, 0) <= :value |
数值小于等于 |
13.3 字段白名单
防止字典配置拼出非预期 SQL,白名单校验在 toRuleFieldMapping 中执行:
出库单字段(30+):
shipmentType, ticketType, pickType, sourcePlatform, sourceErp, erpOrderType, route, carrierCode, shipmentSubType, shipmentCategory1~8, shipToCountry/State/City/District/Town(Code), hostCompanyCode, storeCode, shipObjType, requestedDeliveryType, priority, userDef1~8
规则字段(16):
channelId, fulfillmentChainId, orderSize, skuSizeType, categoryLevel1Id, shopGroup, shopId, urgentFlag, outerPackaging, outerPackagingId, deliveryRegion, fragile, liquid, highValue, battery, danger
13.4 执行流程
resolveRuleFieldMappings(warehouseCode)
→ 查 config_detail (优先 warehouseCode, 兜底 '*')
→ toRuleFieldMapping(detail)
→ 白名单校验
→ normalizeRuleOperator 规范化操作符
→ 返回 RuleFieldMapping(ruleField, shipmentField, operator)
appendRuleFieldMatching(sql, params, warehouseCode, rule)
for each mapping:
switch operator:
IN → appendIn(sql, params, shipmentField, ruleField + 'Values', splitCandidates(ruleValue))
EQ → appendEquals(sql, params, shipmentField, ruleField + 'Value', ruleValue)
LE → appendLessOrEqual(sql, params, shipmentField, ruleField + 'Value', toNonNegativeInteger(ruleValue))
14. 方法调用关系图
14.1 首次集波
findBestMatchedRuleByWorkStationAndOutboundType(String, String, String) [入口-工作站编码]
└─ findEnabledWorkStation
└─ findBestMatchedRuleByWorkStationAndOutboundType(Long, String) [统一入口]
├─ findEnabledRules
├─ assignSingleShipmentWaves [一波一单分流]
│ ├─ findSingleShipmentWaveShipments
│ ├─ createInternalWave
│ └─ addShipmentsToInternalWave
├─ fillShortageCells [已绑定 Shopee 波次格口补缺口]
│ ├─ findShortageCells
│ ├─ calcCellNeedQty
│ ├─ resolveShopeeWaveGroupKey
│ ├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ └─ assignToCell
└─ (标准集波) for each rule: [标准集波]
├─ findCells → matchCurRuleCells / matchRuleCells / matchEmptyCells
└─ assignCellNumbers
├─ findSingleShipmentWaveShipments
├─ findAvailableShipments
├─ createInternalWave
├─ acquireUnusedShopeeWave
└─ for each cell:
├─ skipSingleRpln (单品单件)
├─ assignToCell
│ ├─ pickCellShipmentIds
│ ├─ calcCellNeedQty
│ ├─ ensureCellReadyForWave → occupySortingWallCell
│ ├─ bindShipmentShopeeWave
│ │ ├─ requestShopeeWaveBind
│ │ ├─ markKickOutWave (整批失败)
│ │ └─ updateShopeeWaveBindingFields (整批成功)
│ ├─ addShipmentsToInternalWave → run
│ └─ 成功后消费当前候选池
└─ (回滚)
├─ rollbackInternalWaveIfEmpty
├─ rollbackWaveShipments → removeShipmentsFromWave
└─ rollbackShipmentShopeeWave
14.2 首次集波内补缺口
fillShortageCells
└─ for each rule:
├─ findShortageCells
└─ for each cell:
├─ calcCellNeedQty
├─ resolveShopeeWaveGroupKey
├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
└─ assignToCell
14.3 查询方法
订单查询:
buildAvailableShipmentSql ← 基础 SQL(异常过滤)
buildAvailableShipmentParams ← 基础参数
appendBatchWaveFilter ← 排除一波一单
appendSingleWaveFilter ← 一波一单条件
appendRuleFieldMatching ← WAVE_RULE_FIELD_MATCHING 字典匹配
├─ appendIn / appendEquals / appendLessOrEqual
└─ resolveRuleFieldMappings → toRuleFieldMapping
appendKickOutFilter ← 排除已拒单
appendShipmentPriorityOrder ← 排序
格口查询:
findCells(workstationId)
findCells(workstationId, rule)
matchCurRuleCells / matchRuleCells / matchEmptyCells
findActiveWorkstationShipmentTypes(warehouseCode, workStationId)
规则查询:
findEnabledRules(warehouseCode)
工作站查询:
findEnabledWorkStation(warehouseCode, workStation)
波次查询:
countShopeeShipments(warehouseCode, shopeeWaveCode)
resolveShopeeWaveGroupKey(warehouseCode, shopeeWaveCode)
resolveShopeeWaveType(warehouseCode, shipmentIds)
工具方法:
toLongValue / toNonNegativeInteger
ruleStringValue / ruleIntegerValue
splitCandidates / matchesCellWaveRule
buildRuleMatchLockKey
isSingleRule
resolveMasterCode / resolveShipmentLimit / resolveLowThreshold
normalizeRuleOperator
附录:关键常量
| 常量 | 值 | 用途 |
|---|---|---|
SHIPMENT_IN_POOL |
100 |
出库单订单池状态 |
SHIPMENT_PROCESS_NORMAL |
'NORMAL' |
正常处理流程标识 |
DEFAULT_SHIPMENT_LIMIT |
30 |
规则未配 maxShipments 的兜底上限 |
PICK_TYPE_PICKING_BY_ORDER |
1 |
按单拣选标识 |
PICK_TYPE_PICKING_TASK |
2 |
推拣货任务标识 |
TICKET_TYPE_RT |
6 |
RT ticketType,一波一单且不使用 Shopee 波次号 |
TICKET_TYPE_XSCK_TASK |
1 |
销售出库任务 |
TICKET_TYPE_MTO_TASK |
5 |
MTO 任务 |
WAVE_RULE_FIELD_MATCHING |
'WAVE_RULE_FIELD_MATCHING' |
字段匹配字典类型 |
附录:TODO 清单(代码内标注)
| 位置 | TODO | 优先级 |
|---|---|---|
findBestMatchedRuleByWorkStationAndOutboundType |
用户权限与出库类型绑定 | P1 |
resolveMasterCode 前注释 |
库存最优分配规则对齐 | P2 |
acquireUnusedShopeeWave 前注释 |
Shopee 波次号段申请接口 & 用完续号 | P1 |
requestFlowPickSealBox Javadoc |
Flow Pick 封箱接口正式对接 | P1 |
requestFlowPickChangeBox Javadoc |
Flow Pick 换箱接口正式对接 | P1 |
requestShopeeWaveBind Javadoc |
Shopee 波次绑定接口正式契约对接 | P0 |
handleFlowPickChangeWaveFailure Javadoc |
换波失败完整回滚(订单池恢复/取消任务/AGV 回库) | P1 |