[16591]更新Shopee集波规则文档

This commit is contained in:
曾志威
2026-07-29 14:37:17 +08:00
parent c09efccdb6
commit b9be8253de
3 changed files with 131 additions and 461 deletions
@@ -22,56 +22,57 @@
## 2. 主流程总图
### 2.1 计划集波工作站入口
```text
入口
findBestMatchedRuleByWorkStationAndOutboundType(session, warehouseCode, workStation, shipmentType)
├─ [1] 用户权限校验
│ └─ hasUserShipmentTypePermission
│ ├─ 命中禁止 → MSG_WRM_0013
│ └─ 通过 → 继续
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode[, workStationCode])
查询候选工作站
├─ currentJobMode 为拣选模式
│ ├─ acceptStatus = OPEN
│ └─ status = ENABLE
├─ 统计各工作站的可用空格口
│ ├─ 分播墙 status = ENABLE
│ ├─ 格口 status = ENABLE
│ └─ 格口 shopeeWaveCode 为空
├─ 多工作站排序
│ ├─ 可用空格口数量降序
│ └─ 数量相同时工作站 ID 升序
└─ 按排序结果逐个调用统一集波匹配流程
├─ 无启用分播墙 → 跳过当前工作站
└─ 单个工作站失败 → 记录日志并继续后续工作站
```
### 2.2 单工作站集波入口
```text
自动集波工作站扫描
└─ matchByStation(session, workstation)
├─ [1] 从 workstation 读取 id、warehouseCode、code、shipmentType
├─ [2] 入参校验
│ ├─ warehouseCode / workStation / shipmentType 任意为空 → MSG_WRM_0001
│ └─ 通过 → 继续
├─ [2] 工作站状态校验
│ ├─ status != ENABLE → MSG_WRM_0005
│ └─ warehouseCode 为空 → MSG_WRM_0006
├─ [3] 工作站校验
│ ├─ findEnabledWorkStation
│ ├─ 工作站不存在 / 未启用 / 仓库不匹配 → MSG_WRM_0002 / 0004 / 0005 / 0006
│ └─ 通过 → 继续
├─ [4] 接单状态校验
├─ [3] 接单状态校验
│ ├─ acceptStatus != OPEN → MSG_WRM_0015
│ └─ 通过 → 继续
├─ [5] 同工作站出库类型隔离
│ ├─ hasDifferentActiveShipmentType
│ ├─ 已存在其它 shipmentType 占用 → MSG_WRM_0014
│ └─ 通过 → 继续
├─ [6] Redis 锁
│ ├─ buildRuleMatchLockKey
│ ├─ getAndTryLock
│ ├─ 获取失败 → MSG_WRM_0012
├─ [4] 仓库维度 Redis 锁
│ ├─ ruleLockKey(warehouseCode)
│ ├─ tryLock 失败 → MSG_WRM_0018
│ └─ 获取成功 → 继续
├─ [7] 查询启用规则
│ ├─ findEnabledRules(warehouseCode)
│ ├─ rules 为空 → MSG_WRM_0007
│ └─ 继续
├─ [5] ORDER_PICK → assignSingles
├─ [8] 分流一:一波一单
assignSingleShipmentWaves
├─ [6] BATCH_PICK
取操作员与工作站 Wave Rule 交集
│ ├─ fillShortage
│ └─ 补单未分配时,按启用规则执行 assignTaskTickets / assignEmpty / assignRt
├─ [9] 分流二:已绑定 Shopee 波次格口补缺口
│ └─ fillShortageCells
├─ [7] 有任一分流完成分配 → success
─ [10] 分流三:空格口首次分配
assignEmptyCells
├─ [11] 成功返回
│ └─ ResponseMessageFactory.success()
└─ [12] finally
─ [8] finally
写入结束日志
└─ unlock
```
@@ -109,25 +110,26 @@ assignSingleShipmentWaves(warehouseCode, shipmentType, workstationId, rules)
### 3.2 补缺口分流
```text
fillShortageCells(warehouseCode, shipmentType, workstationId, rules)
├─ for each rule
├─ findShortageCells(session, workstationId, rule)
│ ├─ for each cell
│ ├─ 如果 cell 未绑定 shopeeWaveCode → 跳过
│ ├─ calcCellNeedQty
│ ├─ needQty <= 0 → 跳过
│ ├─ resolveShopeeWaveGroupKey
│ ├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ ├─ assignToCell
│ └─ 记录补单成功或失败日志
│ └─ 继续下一个 rule
fillShortage(warehouseCode, shipmentType, workstation)
├─ findShortage(workstationId) → 仅选择 shopee_wave.status=200 的关联格口
├─ for each cell
│ ├─ 如果 cell 未绑定 shopeeWaveCode → 跳过
│ ├─ calcNeedQty
│ ├─ needQty <= 0 → 跳过
│ ├─ needQty > 0 → 加入需补货格口明细
│ ├─ resolveBoundShipmentGroup
│ ├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
│ ├─ assignToCell
│ └─ 记录补单成功或失败日志
├─ MSG_WRM_0244 → 记录需补货格口数量及 cellCode|shopeeWaveCode|currentWaveRule|ticketType|needQty
└─ 返回
```
关键点:
- 只处理当前工作站里“已经绑定 Shopee 波次”的格口
- 补单时要保持同一 `groupKey`
- 候选资格只判断当前工作站格口关联的 `shopee_wave.status=200`
- 不按分播墙状态、格口状态、ticketType、任务完成时间或 `waitingTime` 过滤候选
- 补单执行时从已绑定订单解析 `groupKey`,保持同一 Shopee 波次订单特征一致。
- 补缺口不重新取新 Shopee 波次号。
### 3.3 空格口首次分配
@@ -153,9 +155,9 @@ assignCellNumbers(...)
│ │ ├─ acquireUnusedShopeeWave
│ │ └─ 得到新 shopeeWaveCode
│ ├─ 如果 cell 已有 shopeeWaveCode
│ │ ├─ calcCellNeedQty
│ │ ├─ calcNeedQty
│ │ ├─ needQty <= 0 → 跳过
│ │ ├─ resolveShopeeWaveGroupKey
│ │ ├─ resolveBoundShipmentGroup
│ │ └─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ └─ assignToCell
└─ 返回 success / error
@@ -248,13 +250,14 @@ addShipmentsToInternalWave(shipmentIds, waveId)
| 方法 | 作用 | 在流程图中的位置 |
|---|---|---|
| `findBestMatchedRuleByWorkStationAndOutboundType` | 主入口 | 主流程总图 |
| `assignShopeeWaveToAcceptedWorkStations` | 计划集波工作站扫描、排序与调度 | 计划集波工作站入口 |
| `matchByStation` | 单工作站统一分流入口 | 主流程总图 |
| `hasUserShipmentTypePermission` | 用户出库类型权限 | 主流程总图 [1] |
| `findEnabledWorkStation` | 工作站校验 | 主流程总图 [3] |
| `hasDifferentActiveShipmentType` | 同工作站出库类型隔离 | 主流程总图 [5] |
| `findEnabledRules` | 查启用规则 | 主流程总图 [7] |
| `assignSingleShipmentWaves` | 一波一单分流 | 3.1 |
| `fillShortageCells` | 补缺口分流 | 3.2 |
| `fillShortage` | status=200 Shopee 波次补缺口分流 | 3.2 |
| `assignEmptyCells` | 空格口首次分配 | 3.3 |
| `assignCellNumbers` | 规则驱动的格口分配 | 3.3 |
| `assignToCell` | 单个格口承接一批单据 | 4.1 |
@@ -48,7 +48,7 @@
| 订单结构 | 出库单头 `shipment_header.orderStructure` 记录数字编码:`1=SSSQ``2=SSAQ``3=MSAQ` |
| 规则类型匹配 | `wave_rule.waveType` 通过 `resolveMatchedOrderStructures` 转换为可抓的 `orderStructure` 集合 |
| 字段匹配 | 通过 `WAVE_RULE_FIELD_MATCHING` 字典追加 `wave_rule` 字段与 `shipment_header` 字段的 `IN/EQ/LE` 条件 |
| 同波约束 | 首次抓单会先按最候选解析 `groupKey`;补缺口时按当前 `shopee_wave.groupKey` 过滤,保证同一 Shopee 波次只集相同 `groupKey` |
| 同波约束 | 首次抓单会先按最高优先级候选解析 `groupKey`;补缺口时通过 `resolveBoundShipmentGroup` 从当前波次已绑定订单解析 `groupKey` |
| V1.4 新增 SKU 件数过滤 | `Max SKU Pieces Per Order``Mix Mode Max SKU Pieces Filter` 是 docx 标准要求;当前 `WaveRule` 域和匹单 SQL 未见对应落地字段,列为待实现 |
**三种分流入口:**
@@ -56,7 +56,7 @@
| 分支 | 条件 | 处理方式 |
|------|------|----------|
| 一波一单分流 | `pickType=1``XSCK+ticketType=1``ticketType=5``ticketType=6` | 一单一个内部波次;RT 不使用 Shopee 波次号 |
| 已绑波次格口补缺口 | 空闲格口已绑定 `shopeeWaveCode``currentWaveRule` 匹配当前规则 | `low_threshold/maxShipments` 计算缺口后补单 |
| 已绑波次格口补缺口 | 格口关联的 `shopee_wave.status=200` | 候选只按波次状态判断;进入执行后读取格口规则和 ticketType,并`lowThreshold/maxShipments` 计算补单数量 |
| 空格口首次分配 | 其他普通批量候选单 | 按规则优先级逐规则匹配,先抢占格口,再绑定 Shopee 波次,再创建并运行内部波次 |
### PDF 补充:WMS 出库 vendor 交互范围
@@ -77,24 +77,13 @@ PDF 名词口径:`波次ID` 约等于 WMS 拣货任务;`PickingTask` 通常
## 2. 入口与总流程
### 外部入口
```
POST wms/automation/waveRuleMatch/findBestMatchedRule
→ WaveRuleMatchController.findBestMatchedRule(map)
→ WaveRuleMatchService.findBestMatchedRuleByWorkStationAndOutboundType(warehouseCode, workStation, shipmentType)
```
### 调用链总图
```
findBestMatchedRuleByWorkStationAndOutboundType(String, String, String)
assignShopeeWaveToAcceptedWorkStations(TtxSession, String, String)
├─ 参数校验(warehouseCode / workStation / shipmentType 非空)
findEnabledWorkStation(warehouseCode, workStation)
│ └─ 支持工作站编码 或 数字 ID 查询
└─ findBestMatchedRuleByWorkStationAndOutboundType(Long, String) ← 统一入口
├─ 扫描开启接单的工作站
matchByStation(TtxSession, WcsWorkStation)
├─ [1] 工作站校验:存在 / 启用 ENABLE / 仓库匹配
├─ [2] hasDifferentActiveShipmentType
@@ -111,7 +100,7 @@ findBestMatchedRuleByWorkStationAndOutboundType(String, String, String)
├─ [5] assignSingleShipmentWaves(先尝试一波一单候选)
│ → pickType=1 / ticketType=1,5,6
├─ [6] fillShortageCells(已绑定 Shopee 波次格口补缺口)
├─ [6] fillShortage(status=200 Shopee 波次格口补缺口)
│ → 空闲 + shopeeWaveCode 有值 + currentWaveRule 匹配
├─ [7] assignEmptyCells(空格口首次分配)
@@ -164,6 +153,16 @@ hasDifferentActiveShipmentType(warehouseCode, workstationId, shipmentType)
当前实现/待实现:`WaveRuleMatchService` 已做同一工作站不同出库类型隔离;用户与出库类型的权限绑定仍是 TODO,不能在本文档中描述为已落地。
### 3.5 计划集波工作站调度
`assignShopeeWaveToAcceptedWorkStations` 扫描指定仓库的候选工作站,并按工作站当前 `shipmentType` 依次执行集波匹配。
- 候选工作站必须满足:`currentJobMode` 为拣选模式、`acceptStatus=OPEN``status=ENABLE`
- 指定工作站编码时,只处理该工作站;未指定时处理仓库内全部候选工作站。
- 多个候选工作站按可用空格口数量降序处理;数量相同时按工作站 ID 升序处理。
- 可用空格口统计口径:工作站绑定的分播墙为启用状态、格口为启用状态,且格口 `shopeeWaveCode` 为空。
- 工作站没有启用分播墙时跳过;`shipmentType` 不作为调度过滤条件,匹配时直接读取 `wcs_work_station.shipmentType`。单个工作站匹配失败不阻断后续工作站。
---
## 4. 一波一单分流流程
@@ -361,8 +360,8 @@ WHERE warehouseCode = :warehouseCode
| 场景 | 方法 | 规则 |
|------|------|------|
| 首次空格口集波 | `findAvailableShipments(..., groupKey='')` | 先用已拼好的候选 SQL 查最早候选单的 `groupKey`,再追加 `appendGroupKeyFilter` 限制同一特征值 |
| 已绑波次格口补缺口 | `resolveShopeeWaveGroupKey` + `findAvailableShipments(..., shopeeWaveCode, groupKey)` | 从 `shopee_wave.groupKey` 解析当前波次特征值,只补相同 `groupKey` 的单 |
| 首次空格口集波 | `findAvailableShipmentBatch(..., groupKey='')` | 先查询候选分组,再按优先级选择本轮 `groupKey` |
| 已绑波次格口补缺口 | `resolveBoundShipmentGroup` + `findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)` | 从当前波次已绑定订单解析特征值,只补相同 `groupKey` 的单 |
| 空 `groupKey` | `appendGroupKeyFilter` | 空值只匹配空值,避免空值与非空值串波 |
这意味着同一 Shopee 波次内不会混入不同 `groupKey` 的订单;如果候选池存在多个 `groupKey`,当前按抓单优先级排序后的首个候选单决定本轮特征值。
@@ -528,13 +527,13 @@ addShipmentsToInternalWave(shipmentIds, waveId)
### 11.1 入口
当前状态:`pollReplenishment/pollReplenishmentOnce` 已删除。补缺口能力在首次集波入口中通过 `fillShortageCells` 执行。
当前状态:`pollReplenishment/pollReplenishmentOnce` 已删除。补缺口能力在首次集波入口中通过 `fillShortage` 执行。
已具备的依赖方法:
- `fillShortageCells(...)`入口内逐规则处理已绑定 Shopee 波次的可补格口。
- `findShortageCells(Long workStationId, WaveRule rule)`:SQL 查询空闲、已绑定 Shopee 波次、`currentWaveRule` 匹配当前规则的格口
- `calcCellNeedQty(...)`:计算单品单件或非单品单件缺口
- `fillShortage(...)`处理当前工作站关联使用中 Shopee 波次的格口。
- `findShortage(Long workStationId)`:候选资格只判断关联 `shopee_wave.status=200`
- `calcNeedQty(...)`:计算当前 Shopee 波次的剩余容量
- `assignToCell(...)`:先抢占格口,再请求 Shopee 波次绑定接口,再创建并运行内部波次。
入口不返回分配明细;成功、失败和跳过原因通过系统处理日志记录。
@@ -542,64 +541,59 @@ addShipmentsToInternalWave(shipmentIds, waveId)
### 11.2 流程
```
findBestMatchedRuleByWorkStationAndOutboundType(...)
matchByStation(TtxSession, WcsWorkStation)
└─ fillShortageCells(warehouseCode, shipmentType, workstationId, rules)
└─ fillShortage(warehouseCode, shipmentType, workstation)
└─ for each rule:
findShortageCells(workstationId, rule)
└─ findShortage(workstationId)
→ wcs_sorting_wall_cell
useStatus = IDLE
→ shopeeWaveCode 非空
→ currentWaveRule = rule.code
shopee_wave.status = 200
└─ for each cell:
calcCellNeedQty
findAvailableShipments(..., shopeeWaveCode, groupKey)
calcNeedQty
findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
assignToCell
```
### 11.3 fillShortageCells 逻辑
### 11.3 fillShortage 逻辑
```
fillShortageCells(warehouseCode, shipmentType, workstationId, rules):
fillShortage(warehouseCode, shipmentType, workstation):
├─ 参数校验(warehouseCode / shipmentType / workstationId / rules
├─ findShortage(workstationId) → 仅按 shopee_wave.status=200 选择候选格口
├─ for each rule:
│ ├─ findShortageCells → 查当前规则可补格口
for each cell:
├─ calcCellNeedQty → 计算缺口
│ ├─ 单品单件: 未拣货数量 < low_threshold → need = maxShipments
│ ├─ 非单品单件: need = maxShipments - 当前 Shopee 波次已绑单量
│ └─ need <= 0 → 跳过
├─ for each cell:
│ ├─ 按 currentWaveRule 读取启用规则
calcNeedQty → maxShipments - 当前 Shopee 波次已绑单量
├─ needQty <= 0 → 跳过
│ ├─ needQty > 0 → 加入需补货格口明细
│ ├─ resolveBoundShipmentGroup → 从已绑定订单解析当前波次 groupKey
├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
│ │ ├─ waveType/orderStructure 匹配
│ │ ├─ WAVE_RULE_FIELD_MATCHING 字段匹配
│ │ ├─ 排除 kickOutWave 过滤的已拒单
│ │ └─ 只补同一 groupKey 单据
│ └─ assignToCell
│ ├─ ensureCellReadyForWave → 先抢占格口
│ ├─ bindShipmentShopeeWave → Shopee 波次绑定接口
│ ├─ createInternalWave
│ └─ addShipmentsToInternalWave → WaveService#run
├─ resolveShopeeWaveGroupKey → 同一 Shopee 波次只补相同 groupKey 单据
├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
│ → waveType/orderStructure 匹配
│ → WAVE_RULE_FIELD_MATCHING 字段匹配
│ → 排除 kickOutWave 过滤的已拒单
│ → 只补同一 groupKey 单据
└─ assignToCell
├─ ensureCellReadyForWave → 先抢占格口
├─ bindShipmentShopeeWave → Shopee 波次绑定接口
├─ createInternalWave
└─ addShipmentsToInternalWave → WaveService#run
├─ MSG_WRM_0244 → 记录需补货格口数量及 cellCode|shopeeWaveCode|currentWaveRule|ticketType|needQty
└─ 返回补单结果
```
### 11.4 缺口计算
```
calcCellNeedQty(warehouseCode, rule, cell, shopeeWaveCode)
calcNeedQty(warehouseCode, rule, shopeeWaveCode, ticketType)
```
| 规则类型 | 触发条件 | 补单数量 |
|----------|----------|----------|
| 单品单件 | `< low_threshold` | `maxShipments`(一次性补满) |
| 非单品单件 | 始终尝试 | `maxShipments - 当前波次已绑单量` |
| 新格口/无波次号 | — | 返回 null(不截断) |
| Flow Pick | 未完成订单数不高于 `lowThreshold` | `maxShipments - 未完成订单数` |
| Dynamic Wave | status=200 候选进入执行 | `maxShipments - 当前波次已绑单量` |
| ticketType 无效 | 执行元数据不完整 | `0` |
---
@@ -757,19 +751,18 @@ SQL 形态:
### 14.1 首次集波
```
findBestMatchedRuleByWorkStationAndOutboundType(String, String, String) [入口-工作站编码]
└─ findEnabledWorkStation
└─ findBestMatchedRuleByWorkStationAndOutboundType(Long, String) [统一入口]
assignShopeeWaveToAcceptedWorkStations(TtxSession, String, String) [计划任务入口]
└─ matchByStation(TtxSession, WcsWorkStation) [统一入口]
├─ findEnabledRules
├─ assignSingleShipmentWaves [一波一单分流]
│ ├─ findSingleShipmentWaveShipments
│ ├─ createInternalWave
│ └─ addShipmentsToInternalWave
├─ fillShortageCells [已绑定 Shopee 波次格口补缺口]
│ ├─ findShortageCells
│ ├─ calcCellNeedQty
│ ├─ resolveShopeeWaveGroupKey
│ ├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
├─ fillShortage [status=200 Shopee 波次格口补缺口]
│ ├─ findShortage
│ ├─ calcNeedQty
│ ├─ resolveBoundShipmentGroup
│ ├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
│ └─ assignToCell
└─ (标准集波) for each rule: [标准集波]
├─ findCells → matchCurRuleCells / matchRuleCells / matchEmptyCells
@@ -782,7 +775,7 @@ findBestMatchedRuleByWorkStationAndOutboundType(String, String, String) [入
├─ skipSingleRpln (单品单件)
├─ assignToCell
│ ├─ pickCellShipmentIds
│ ├─ calcCellNeedQty
│ ├─ calcNeedQty
│ ├─ ensureCellReadyForWave → occupySortingWallCell
│ ├─ bindShipmentShopeeWave
│ │ ├─ requestShopeeWaveBind
@@ -799,13 +792,12 @@ findBestMatchedRuleByWorkStationAndOutboundType(String, String, String) [入
### 14.2 首次集波内补缺口
```
fillShortageCells
└─ for each rule:
├─ findShortageCells
fillShortage
└─ findShortage(workstationId) → shopee_wave.status=200
└─ for each cell:
├─ calcCellNeedQty
├─ resolveShopeeWaveGroupKey
├─ findAvailableShipments(..., shopeeWaveCode, groupKey)
├─ calcNeedQty
├─ resolveBoundShipmentGroup
├─ findAvailableShipmentBatch(..., shopeeWaveCode, groupKey)
└─ assignToCell
```
@@ -835,11 +827,13 @@ fillShortageCells
findEnabledRules(warehouseCode)
工作站查询:
assignShopeeWaveToAcceptedWorkStations(session, warehouseCode[, workStationCode])
└─ 候选工作站按启用空格口数降序、工作站 ID 升序处理
findEnabledWorkStation(warehouseCode, workStation)
波次查询:
countShopeeShipments(warehouseCode, shopeeWaveCode)
resolveShopeeWaveGroupKey(warehouseCode, shopeeWaveCode)
resolveBoundShipmentGroup(warehouseCode, shopeeWaveCode, ticketType)
resolveShopeeWaveType(warehouseCode, shipmentIds)
工具方法:
@@ -878,7 +872,7 @@ fillShortageCells
|------------|-------------------|----------|
| `shipment_header.orderStructure` 自动分析 | `ShipmentHeaderService#calcOrderStructure` 写入 `1=SSSQ``2=SSAQ``3=MSAQ` | 已落地 |
| `wave_rule.waveType``shipment_header.orderStructure` 包含关系 | `appendWaveTypeOrderStructureFilter` + `resolveMatchedOrderStructures` | 已落地 |
| 同一波次 `groupKey` 一致 | `findAvailableShipments` 首次解析候选 `groupKey`;补缺口读取 `shopee_wave.groupKey` | 已落地 |
| 同一波次 `groupKey` 一致 | `findAvailableShipmentBatch` 首次选择候选组;补缺口 `resolveBoundShipmentGroup` 从已绑定订单解析 | 已落地 |
| 上墙优先级 | `findEnabledRules``wavePriority desc, id asc`;高优先级不足 `maxShipments` 仍优先消费 | 已落地 |
| 抓单优先级 | `priority desc``cutOffTime asc``purchaseTime asc``orderTime asc``id asc` | 已落地 |
| `wave_rule.maxShipments` | `resolveShipmentLimit` 使用规则级 `maxShipments`,默认 30 | 已落地 |
@@ -909,7 +903,7 @@ fillShortageCells
| Shopee 合波校验真实接口 | `requestShopeeWaveBind` 为预留入口,当前只保留整批成功/失败契约 | 接口契约明确后补真实调用 |
| create_picking_task / RTS-MTO 上游校验 | 标准要求与 Shopee 波次获取、合波校验衔接;当前仅有服务侧统一预留入口 | 真实接口契约确认后接入 |
| 库存不足踢单通知上游撤单 | 当前由回池链路清空 `shopeeWave/waveRule`,上游撤单接口仍待接入 | `wms-wave` 回池链路 |
| 计划任务轮询补单 | 原 `pollReplenishment/pollReplenishmentOnce` 已删除;当前补缺口在首次集波入口 `fillShortageCells` 执行 | 如需后台轮询,需恢复独立计划任务入口 |
| 计划任务轮询补单 | 原 `pollReplenishment/pollReplenishmentOnce` 已删除;当前补缺口在首次集波入口 `fillShortage` 执行 | 如需后台轮询,需恢复独立计划任务入口 |
| 推拣货任务一单一波一槽口 | 普通批量已过滤 `pickType=2`,独立分流入口当前未保留 | 待恢复 `pickType=2 + shopeeWave` 专用分流 |
| Flow Pick 封箱/换箱接口 | 本地流程已有占位方法,真实 ESS/AGV 接口未接入 | `requestFlowPickSealBox` / `requestFlowPickChangeBox` / `handleFlowPickChangeWaveFailure` |
| 工作站用户权限控制出库类型 | 当前只校验工作站启用、仓库和不同出库类型隔离 | 用户权限与出库类型绑定仍待实现 |
@@ -919,7 +913,6 @@ fillShortageCells
| 位置 | TODO | 优先级 |
|------|------|--------|
| `findBestMatchedRuleByWorkStationAndOutboundType` | 用户权限与出库类型绑定 | P1 |
| `resolveMasterCode` 前注释 | 库存最优分配规则对齐 | P2 |
| `acquireUnusedShopeeWave` 前注释 | Shopee 波次号段申请接口 & 用完续号 | P1 |
| `requestFlowPickSealBox` Javadoc | Flow Pick 封箱接口正式对接 | P1 |
@@ -1,326 +0,0 @@
# 出库集波规则匹配 API 文档
> 对应实现:`WaveRuleMatchController#findBestMatchedRule` → `WaveRuleMatchService#findBestMatchedRuleByWorkStationAndOutboundType`
>
> 需求标准来源:`C:\work\gitlab\shopee\ttx-project-doc\集波\SHOPEE_010_出库集波V1.2.docx`V1.42026-06-04 逻辑修改 / 匹单字段逻辑更新)。
---
## 概述
工作站集波的核心入口。用户在 WES 工作站界面选择出库类型后,前端调用本接口触发自动集波流程:
1. 根据**工作站 + 出库类型**找出当前可用的分播墙格口
2. 按规则优先级查找订单池中符合条件的出库单
3. 完成 Shopee 波次绑定、ESS 校验、内部波次创建与运行
4. 当前实现成功后返回 `ResponseMessageFactory.success()`,不返回「格口 + 命中规则 + 波次」明细
---
## 请求
### URL
```
POST wms/automation/waveRuleMatch/findBestMatchedRule
```
### 请求体 (JSON)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `warehouseCode` | String | 是 | 仓库编码 |
| `workStation` | String | 是 | 工作站编码(或工作站数字 ID) |
| `shipmentType` | String | 是 | 出库类型,如 `XSCK`(销售出库)、`MTO``RTS``RT` |
示例:
```json
{
"warehouseCode": "WH01",
"workStation": "WS001",
"shipmentType": "XSCK"
}
```
### 出库类型说明
| 出库类型 | 含义 | 处理方式 |
|----------|------|----------|
| `XSCK` | 销售出库 | 标准集波(按 ticketType 分流:1=销售出库任务 一波一单,其他批量) |
| `MTO` | MTO 出库 | 标准集波(按 ticketType 分流:5=MTO任务 一波一单,其他批量) |
| `RTS` | RTS 出库 | 标准批量集波 |
| `RT` | RT 出库 | 一波一单,不使用 Shopee 波次号,走库内流程 |
### 标准要求补充
- `wave_rule.wavePriority` 先决定上墙优先级,`maxShipments` 决定抓单数量;即使高优规则订单不足,也要优先匹高优规则的单。
- 同一规则内抓单优先级按 `shipment_header.priority` 降序、`cutOffTime` 升序、`purchaseTime` 升序、`orderTime` 升序。
- `shipment_header.orderStructure` 若为空,出库单下发后需自动分析并写回:`SSSQ=1``SSAQ=2``MSAQ=3`
- `wave_rule.waveType``shipment_header.orderStructure` 按 docx 表格做包含关系匹配,表中命名/编码有歧义处保留原文并按 docx 待确认。
- 匹单还要求 `shipment_header.groupKey` 一致才能集在一起。
- `multi_attr_list` 采用全包含匹配:规则值 `A,B,C` 对单据 `A,B` 可匹配;规则值 `B,C` 对单据 `A,B` 不匹配。
- `wcs_sorting_wall_cell.waveRule` 支持多选,多个规则逗号分隔。
- 工作站选择出库类型受用户权限控制,同一工作站不混做不同出库类型。
- 仅抓正常单据,取消/挂起/异常单不抓。
- `shipment_header.pickType=1/0` 仅允许在订单池 `100` 状态切换。
- `XSCK``MTO``RTS``RT``ticketType` 映射按 docx 口径分别为:销售任务 `1` / 销售订单 `2`MTO 任务 `5` / MTO 订单 `4`RTS `3`RT `6`
- 推拣货任务标准要求为:上游同步 wave 号段,WES 不走取号逻辑,1 拣货单 1 波次 1 槽口,不走补单。
- Flow Pick 标准要求覆盖 `SSSQ/MSSQ` 以及 RTS/MTO 下发单据;满槽换箱换波走 Flow Pick 换箱接口。
- 集波后需按单据类型取得 `shopee_wave` 并请求上游校验:销售 create_picking_task 走 3.2.7RTS/MTO 走 3.3.6;失败订单后续通过补单/踢单处理。
---
## 响应
### 成功响应
```json
{
"success": true,
"data": [
{
"shipmentId": 12345,
"waveId": 67890,
"shopeeWaveCode": "SW20260520001",
"waveRule": "RULE_XSCK_SSSQ",
"sortingWallId": 1001,
"sortingWallCode": "WALL_A01",
"cellId": 2001,
"cellCode": "A01-01"
}
]
}
```
> 标准要求:返回格口 + 命中规则 + 波次分配明细。
>
> 当前实现:成功时通常仅返回 `ResponseMessageFactory.success()`,不保证返回上述明细;若调用链未产出分配结果,请以 success 为准。
| 响应字段 | 类型 | 说明 |
|----------|------|------|
| `shipmentId` | Long | 已集波出库单 ID |
| `waveId` | Long | 内部波次 ID |
| `shopeeWaveCode` | String | Shopee 波次号(RT 类型返回空字符串 `""` |
| `waveRule` | String | 命中的波次规则编码 |
| `sortingWallId` | Long | 分播墙 ID |
| `sortingWallCode` | String | 分播墙编码 |
| `cellId` | Long | 格口 ID |
| `cellCode` | String | 格口编码 |
> 特殊:RT 和推拣货任务返回简化结构 `{shipmentId, waveId, shopeeWaveCode, waveRule, shipmentType}`。
### 错误响应
```json
{
"success": false,
"messageCode": "MSG_WRM_0001",
"message": "参数不完整:warehouseCode、workStation、shipmentType 不能为空"
}
```
### 错误码
| 消息码 | 说明 | 触发条件 |
|--------|------|----------|
| `MSG_WRM_0001` | 参数不完整 | `warehouseCode`/`workStation`/`shipmentType` 任意为空 |
| `MSG_WRM_0002` | 工作站不可用 | 工作站不存在、仓库不匹配或未启用 |
| `MSG_WRM_0003` | 参数不完整 | `workstationId`/`shipmentType` 为空(内部入口) |
| `MSG_WRM_0004` | 工作站[{0}]不存在 | 工作站 ID 无效 |
| `MSG_WRM_0005` | 工作站[{0}]未启用 | 工作站状态非 ENABLE |
| `MSG_WRM_0006` | 工作站[{0}]仓库编码为空 | 工作站未关联仓库 |
| `MSG_WRM_0007` | 未找到可用波次规则 | 仓库下无启用规则 |
| `MSG_WRM_0008` | 无可用格口 | 工作站下所有格口已被占用 |
| `MSG_WRM_0009` | 规则匹配到但无格口可用 | 当前规则下无空闲格口承接 |
| `MSG_WRM_0010` | 当前规则下无可匹配出库单 | 订单池无符合条件的出库单 |
| `MSG_WRM_0011` | 规则或工作站为空 | 内部校验 |
| `MSG_WRM_0012` | 工作站正在集波中 | Redis 锁获取失败,同一工作站同一出库类型正在集波 |
| `MSG_WRM_0014` | 工作站已有其它出库类型作业 | 同一工作站已存在其它 `shipmentType` 的占用格口/波次 |
---
## 处理流程
```
POST /findBestMatchedRule
├─ 1. 参数校验 (warehouseCode / workStation / shipmentType)
├─ 2. 工作站校验 (存在 / 启用 / 仓库匹配)
├─ 3. 同工作站出库类型隔离 (不允许一个工作站混做不同 shipmentType)
├─ 4. Redis 锁 (仓库+工作站+出库类型, 防重复集波)
├─ 5. 查询启用波次规则 (按 wavePriority desc)
├─ [分支 A] 一波一单
│ └─ assignSingleShipmentWaves
│ 按单拣选、销售任务、MTO 任务、RT 一单一波;RT 不使用 Shopee 波次号
├─ [分支 B] 已绑定 Shopee 波次格口补缺口
│ └─ fillShortageCells
│ 仅处理空闲、已绑定 shopeeWaveCode、currentWaveRule 匹配当前规则的格口
└─ [分支 C] 空格口首次分配 (逐规则按优先级循环)
├─ 6. 查可用格口 (三层级):
│ ├─ sameCurRuleCells — currentWaveRule 同当前规则
│ ├─ ruleCells — waveRule 配置匹配当前规则
│ └─ emptyCells — 完全空闲格口
├─ 7. 按规则查询出库单:
│ ├─ 一波一单候选项 (pickType=1 / ticketType 任务 / RT)
│ └─ 批量集波候选项 (排除一波一单)
├─ 8. 创建内部波次 (WaveService#createInternalWave)
├─ 9. 格口循环处理:
│ ├─ 已有波次号 → 补单缺口判断
│ └─ 无波次号 → 抢占 Shopee 波次号 (acquireUnusedShopeeWave)
├─ 10. assignToCell:
│ ├─ pickCellShipmentIds — 按格口缺口截断候选单
│ ├─ ensureCellReadyForWave — 先抢占格口 (USED)
│ ├─ bindShipmentShopeeWave — ESS 绑定校验
│ │ ├─ 成功 → 写入 shipment_header.shopeeWave,并更新 shopee_wave pickType/groupKey
│ │ └─ 失败 → 写入 kickOutWave, 同波次后续过滤
│ └─ addShipmentsToInternalWave → WaveService#run
└─ 11. 成功返回 ResponseMessageFactory.success(),不返回格口分配明细
```
---
## 核心规则
### 规则优先级
1. **上墙优先级**`wave_rule.wavePriority`(数字越大越优先)
2. **抓单优先级**(同一规则内):
- `priority` 数字越大越优先
- 同优先级按 `cutOffTime` 越早越优先
- 同截单时间按 `purchaseTime` 越早越优先
- 同购买时间按 `orderTime` 越早越优先
- 同时间按 `id` 升序兜底
### V1.4 标准要求与当前实现
| 标准要求 | 当前实现/待实现 |
|----------|-----------------|
| 修改历史 V1.4:匹单字段逻辑更新 | 本文按 docx V1.4 同步 `waveType/orderStructure``groupKey`、规则字段匹配和新增字段说明 |
| `wave_rule` 字段按 WES 小驼峰与 base 字段命名规范维护 | 当前规则域已包含 `waveType``wavePriority``maxShipments` 及多项规则属性;主数据字段命名以代码和迁移为准 |
| 新增/强调 `Max SKU Pieces Per Order` | 标准要求:单订单任一 SKU 件数超过阈值时排除本规则;当前 `WaveRule`/匹单 SQL 未见对应规则字段,待实现 |
| 新增/强调 `Mix Mode Max SKU Pieces Filter` | 标准要求:仅 Mix-mode/MSAQ 规则使用,用于排除单 SKU 件数过高的混合订单;当前未见对应匹单实现,待实现 |
| 出库单 `orderStructure` 为空时自动分析 | 当前由订单明细分析后写入 `shipment_header.orderStructure``SSSQ=1``SSAQ=2``MSAQ=3` |
| 工作站出库类型权限控制 | 标准要求:登录工作站按用户权限下拉可选出库类型;当前服务已做同工作站不同出库类型隔离,用户权限绑定仍为待实现 |
| Shopee 波次号获取与上游合波校验 | 当前 `acquireUnusedShopeeWave` 抢占 `shopee_wave` 未使用号段;`requestShopeeWaveBind` 是上游校验预留入口,create_picking_task / RTS-MTO 真实接口契约仍待接入 |
| 库存最优分配与库存不足踢单 | 标准要求:效期/FIFO、工作站与库存点位半径优先、同半径低层货位优先;当前集波入口只触发 `WaveService#run`,库存不足踢单回池与上游撤单通知仍需在库存/回池链路接入 |
### 格口分配优先级
| 优先级 | 格口类型 | 条件 |
|--------|----------|------|
| 1(最高) | `sameCurRuleCells` | `currentWaveRule` 与当前规则相同 |
| 2 | `ruleCells` | `currentWaveRule` 为空且 `waveRule` 匹配当前规则 |
| 3(兜底) | `emptyCells` | `currentWaveRule``waveRule` 均为空 |
> 标准要求:`wcs_sorting_wall_cell.waveRule` 支持分播墙明细界面多选,一个格口可绑定多个 `wave_rule`,多个规则用逗号分隔存储;当前匹配按逗号拆分后判断是否包含当前规则编码。
### 一波一单处理
以下单据类型按一波一单处理(每次集波只取 1 单,创建独立内部波次):
| 类型 | 标识 | 说明 |
|------|------|------|
| 按单拣选 | `pickType=1` | 按单拣选专用 |
| 销售出库任务 | `shipmentType=XSCK` + `ticketType=1` | 一波一单 |
| MTO 任务 | `ticketType=5` | 一波一单 |
| RT | `ticketType=6` | 不使用 Shopee 波次号 |
### 订单结构与 Flow Pick
| 项 | 标准要求 | 当前实现/说明 |
|----|----------|---------------|
| `waveType/orderStructure` 包含关系 | 按 docx 表:`SSSQ(1)->SSSQ(1)``MSSQ(5)->SSSQ(1)``SSAQ(4)->SSSQ/SSAQ(1/2)``SSSQ(Same SKU same Qty)(2)->MSAQ(3)``MSAQ(3)->SSSQ/SSAQ/MSAQ(1/2/3)` | 当前通过 `appendWaveTypeOrderStructureFilter` 固定过滤。`SSSQ(Same SKU same Qty)``SSSQ` 命名相近,按 docx 待确认 |
| `groupKey` | 同一 Shopee 波次只能集相同 `shipment_header.groupKey` 特征值订单 | 当前首次抓单按首个候选单确定 `groupKey`,补单按 `shopee_wave.groupKey` 过滤 |
| Flow Pick | 标准定义:SSSQ/MSSQ 为 Flow PickRTS/MTO 下发单据也按 Flow Pick 处理 | 当前已有 Flow Pick 换箱换波本地流程与接口占位,真实 ESS/AGV 接口待接入 |
| 推拣货任务 | 上游推送 Shopee 波次号,WES 不走取号逻辑;1 拣货任务 / 1 波次 / 1 槽口;不走补单 | 当前普通批量已过滤 `pickType=2`,专用分流入口待恢复 |
### 字段匹配
通过数据字典 `WAVE_RULE_FIELD_MATCHING` 配置出库单字段与规则字段的匹配关系:
| 操作符 | 含义 | 示例 |
|--------|------|------|
| `IN` | 多值包含 | `userDef1 IN (channelId 多选值)` |
| `=` / `EQ` / `EQUAL` / `EQUALS` | 等值匹配 | `userDef7 = shopId` |
| `LE` / `<=` / `LESS_OR_EQUAL` | 小于等于 | `priority <= urgentFlag` |
**字段白名单**(防 SQL 拼接注入):
- **出库单字段**30+):`shipmentType``ticketType``pickType``sourcePlatform``sourceErp``route``carrierCode``shipToCountry``userDef1-8`
- **规则字段**16):`channelId``fulfillmentChainId``orderSize``skuSizeType``categoryLevel1Id``shopGroup``shopId``urgentFlag``outerPackagingType``deliveryRegion``fragile``liquid``highValue``battery``danger`
补充口径:
- 标准要求:只对 `wave_rule` 中对应订单类型列标记 `Y` 的字段做匹配;SKU 来源字段需与单据所有明细 SKU 主档字段全部匹配。
- 标准要求:`multi_attr_list` 多值字段匹配时按 docx 示例为“规则值全包含单据值”,例如规则 `A,B,C` 可匹配单据 `A,B`,规则 `B,C` 不匹配单据 `A,B`;该包含方向按 docx 待确认。
- 当前实现:逗号多值字段目前按任一值命中生成条件,`multi_attr_list` 全包含匹配仍待调整。
### 并发控制
Redis 分布式锁,锁 Key`wave_rule_match:{warehouseCode}:{workstationId}:{shipmentType}`
同一工作站 + 同一出库类型同时只有一个请求能进入集波流程;同一工作站如果已有其它出库类型作业,入口直接拒绝,避免混做。
### 异常过滤
出库单必须同时满足以下条件才能进入抓单池:
- `leadingSts = 100`(订单池状态)
- `trailingSts = 100`(订单池状态)
- `processType = 'NORMAL'`(正常处理流程)
- `waveId is null or waveId = 0`(未加入任何波次)
- `lockCode is null or lockCode = ''`(未被锁定)
- `cancelTime is null or cancelTime = ''`(未取消)
- `holdTime is null or holdTime = ''`(未挂起)
---
## 补单机制(当前代码口径)
当前代码未保留后台计划任务 `pollReplenishment/pollReplenishmentOnce`。补缺口能力收口在首次集波入口中的 `fillShortageCells`,用于处理当前工作站已绑定 Shopee 波次且规则一致的空闲格口。
- **触发方式**:调用首次集波入口时顺带执行
- **扫描范围**:当前工作站 + 已绑定 Shopee 波次 + `currentWaveRule` 匹配的空闲格口
- **单品单件**:低于 `WAVE/low_threshold` 时触发,一次性按 `maxShipments` 抓单
- **非单品单件**:缺口 = `maxShipments - 当前波次已绑单量`
- **Flow Pick 满槽**:达到 `maxShipments` 时触发换箱换波
- **后台轮询**:如需恢复,需按最终调度策略重新接入计划任务入口
---
## 相关表
| 表名 | 说明 | 关键字段 |
|------|------|----------|
| `wave_rule` | 波次规则 | `code`, `waveType`, `wavePriority`, `maxShipments`, `status`, `channelId`, `shopId` 等;V1.4 标准新增 `Max SKU Pieces Per Order``Mix Mode Max SKU Pieces Filter` 待实现 |
| `shopee_wave` | Shopee 波次号段 | `code`, `waveType`, `warehouseCode`, `pickType`, `groupKey`, `flow_pick`, `status` (100=未使用, 200=使用中, 900=使用完成) |
| `shipment_header` | 出库单头 | `shopeeWave`, `kickOutWave`, `ticketType`, `pickType`, `groupKey`, `orderStructure`, `waveId` |
| `wcs_sorting_wall_cell` | 分播墙格口 | `waveRule`, `currentWaveRule`, `shopeeWaveCode`, `useStatus` |
| `wcs_work_station_pick_task_header` | 拣货任务头 | `shopeeWave`, `waveRule`, `shipmentId` |
| `wave` | 内部波次 | `waveRule` |
---
## 测试与验证
推荐验证顺序:
1. **编译**:按需执行 `:wms-wave:compileGroovy`,本轮文档同步未执行
2. **静态检查**:确认无新增测试类和旧测试类引用残留
3. **手工验证**:使用实际 wave_rule 和出库单数据调用本接口,验证系统处理日志、格口占用和订单波次绑定
4. **ESS 联调**:验证 `bindShipmentShopeeWave` 中 ESS 绑定接口成功/失败结果
5. **补单验证**:确认 `fillShortageCells` 能补满已绑定 Shopee 波次格口且 `kickOutWave` 过滤生效