# SHOPEE_010 出库集波 V1.2 改动计划

## 一、文档目的

本文档用于把《SHOPEE_010_出库集波V1.2.docx》中的需求先整理成可执行的改动计划，再按代码模块落地。

目标是先明确：

- 要改什么
- 改到哪些模块
- 先后顺序怎么排
- 每一步怎么验证

## 二、需求背景

本需求围绕“出库集波”展开，核心是把 Shopee WES 的波次规则、波次号段、订单筛选、工作站集波、补单、踢单、换箱换波和执行联动串成一套闭环流程。

从原文看，主要涉及以下能力：

- 新增和维护 wave rule
- 新增波次号段表 `shopee_wave`
- 按订单属性自动分析订单类型
- 工作站按订单类型和 wave rule 集波
- 按优先级抓单、补单、踢单
- 单品单件波次的动态加单和换箱换波
- 绑定分拨墙槽口和 wave rule
- 与上游接口做波次校验和号段申请

## 三、需求拆分

### 3.1 基础数据与规则配置

需要补齐 wave rule 的规则配置能力，包括：

- 新增 `wave_rule` 表
- 支持波次规则字段配置
- 支持初始化和后续新增、修改 wave rule
- 支持优先级 `wavePriority`
- 支持规则级最大订单数 `wave_rule.maxShipments`
- 支持规则字段和出库单字段匹配

### 3.2 波次号段管理

需要新增波次号段表 `shopee_wave`，用于：

- 按天向上游申请号段
- 用完后继续请求上游
- 记录号段状态
- 记录波次类型、编号和使用状态

### 3.3 订单分析与规则匹配

需要在出库单下发后自动分析订单：

- 单 SKU 单件 `SSSQ`，也就是 `Same SKU Same Qty`
- 单 SKU 多件 `SSAQ`
- 多 SKU 多件 `MSAQ`
- 多品单件 `MSSQ`

分析结果要写入出库单头部字段 `shipmentCategory1`，用于 wave rule 匹单。

当前代码侧已落地的口径是：

- 出库单明细发生变化后，自动重新统计并回填 `shipmentCategory1`
- `WaveRule.orderStructure` 已删除，波次规则当前按 `wave_rule.waveType` 与 `shipment_header.shipmentCategory1` 编码做基础匹配
- 现阶段按代码内已有的四类结构值落地，且 `SSSQ` 与 `Same SKU Same Qty` 视为同一逻辑，保持和既有波次类型一致

### 3.4 工作站集波

工作站需要支持：

- 按用户权限过滤可选订单类型
- 按订单类型先命中 wave rule，再按 rule 条件筛单集波
- 按工作站槽口绑定的 wave rule 集波
- 若槽口未绑定 wave rule，则按全量规则优先级匹单

### 3.4.1 按用户权限过滤可选订单类型

这里的“可选订单类型”指的是前端/接口层可让用户选择的 `shipmentType`，不是波次执行后的结果集。权限控制要尽量前置，避免把未授权的订单类型传进后续波次匹配流程。

当前设计口径如下：

- 参考 `preference_user_right` 的关系模型，单独新增一张波次订单类型权限表来维护数据，不再复用旧表。
- 建议新表名为 `shipment_type_user_right`，按“订单类型 + 用户”维度保存授权关系，字段语义可以参考 `preference_user_right`，但表名和业务语义要更专用，避免和首选项权限混淆。
- 新表字段建议为 `shipmentTypeId`、`userCode`、`created`、`createdBy`，其中 `shipmentTypeId` 对应 `config_detail.id`，`userCode` 对应当前用户，唯一约束建议按 `shipmentTypeId + userCode` 控制。
- 新表中的订单类型值对应出库类型字典主键，来源统一按系统内 `SHIPMENT_TYPE` 配置字典取值，也就是和 `shipment_header.shipmentType` 对应的那套出库类型字典。
- `userCode` 对应当前登录用户，过滤结果只展示该用户已授权的订单类型。
- 用户没有对应授权记录时，默认不展示该订单类型，不做“全量兜底放开”。
- 前端下拉或多选控件先按权限过滤可选项，再把过滤后的 `shipmentType` 传给波次匹配接口。
- 后端进入 `WaveRuleMatchService` 之前应当已经完成权限过滤；该服务只负责按已选 `shipmentType` 做规则和格口候选匹配，不再重复做用户权限判断。
- 权限维护侧可以参考现有“首选项权限”页面交互，但底层读写的是新建的波次订单类型权限表。

建议的接口链路如下：

1. 前端进入集波或波次规则配置页面时，先获取当前用户可见的出库类型列表。
2. 服务端根据当前用户查询 `shipment_type_user_right` 表，取出已授权的订单类型主键。
3. 将命中的订单类型主键映射回 `SHIPMENT_TYPE` 字典，返回可选 `shipmentType` 列表。
4. 用户只能在这个列表里选择订单类型，然后再进入后续波次规则匹配。

当前代码状态说明：

- `WaveRuleMatchService` 已拆分为 `workstationId + 出库类型` 的主入口，旧的工作台编码入口保留兼容。
- 该入口当前已按规则逐条查单并返回格口分配结果，后续仍需补齐更完整的字段匹配和执行衔接。

### 3.5 抓单、补单、踢单

需要实现三类执行动作：

- 抓单：按规则和优先级从订单池取单
- 补单：在波次未满时补齐订单
- 踢单：库存不足或上游校验失败时剔除订单
- 轮询补单：在波次创建后按固定间隔回查订单池，持续补入仍然满足规则且未满载的订单，直到达到补单上限或等待窗口结束

### 3.6 单品单件特殊流程

单品单件 `SSSQ` 场景需要额外支持：

- 动态加单
- 槽口封箱后换箱换波
- 补单和踢单解耦
- 尽量保持槽口满载

### 3.7 上游接口联动

需要补齐与上游的接口逻辑，包括：

- 申请波次号段
- 校验波次是否允许合波
- 踢单时回调撤单
- 初始化 wave rule

### 3.8 分拨墙与槽口绑定

需要扩展分拨墙格口与 wave rule 的绑定能力：

- `wcs_sorting_wall_cell` 增加 `waveRule`
- 支持多规则绑定，多个规则用逗号分隔
- 支持界面多选下拉更新，取值来自 `wave_rule` 表

### 3.9 号段申请与上游合波校验

集波完成后，系统需要从 `shopee_wave` 表里取一个与当前出库单类型一致的 `waveType` 号段，向上游请求校验这批单据是否允许集在一波中。

当前需求口径如下：

- 从 `shopee_wave` 中选择 `waveType` 与出库单类型一致的波次号段。
- 以该波次号段作为本次集波和上游校验的唯一波次标识。
- 调用上游接口，确认当前候选单是否允许合波。
- 如果上游返回不允许集在一起的订单，则该批单据不能直接成波。
- 若当前批次单量少于一波最大订单数，不在这里强行补单重试，而是交给后面的轮询补单逻辑处理。
- 补单逻辑要和各流程解耦，推荐在工作站开始作业后再补单，避免把上游合波校验和补单耦合到一起。

当前代码状态说明：

- 上游合波校验接口还需要后续梳理接口契约后再落实现。
- 这一节先把业务口径写清楚，补单动作统一放到后文轮询补单逻辑中维护。

### 3.10 格口分配逻辑

这里的“格口分配”不是给格口动态生成一条规则，而是把当前工作站下可执行的格口和对应 wave rule 组装成可直接消费的分配结果，供后续流程继续决策。

当前理解和代码侧约束如下：

- 先按工作站查询可用分播墙，再按分播墙查询格口。
- 只保留启用状态且使用状态为 `IDLE` 的格口。
- `waveRule` 是格口自身的绑定约束，表示该格口能接收的规则范围，不是运行时临时分配出来的结果。
- `currentWaveRule` 是格口当前正在使用的规则字段，用来记录本次实际分配到的 `waveRule`，便于补单和执行衔接。
- `shopeeWaveCode` 记录的是格口和 Shopee 波次号段的关联，不承担规则分配职责。
- 这里不反查格口当前绑定的出库单，也不在本层做出库单候选筛选。
- 当前代码会先按仓库和出库类型查启用波次规则，再按规则逐条查单、查格口，形成“格口 + 命中规则 + 可用出库单”的分配结果。
- 如果格口配置了多个 `waveRule`，按逗号、中文逗号或分号拆分后做匹配，命中的规则优先回填到返回结果里。

当前代码状态说明：

- `WaveRuleMatchService` 已实现分配入口：
  - `findBestMatchedRuleByWorkStationAndOutboundType(String warehouseCode, String workStation, String shipmentType)`
  - `findBestMatchedRuleByWorkStationAndOutboundType(Long workstationId, String shipmentType)`
- 工作站控制器已暴露同名接口，可直接通过 `wms/automation/wcsWorkStation/findBestMatchedRuleByWorkStationAndOutboundType` 调用该服务。
- 服务内部先查启用中的波次规则，再按规则逐条查可用出库单和可用格口，最终返回 `ResponseMessage` 包装的分配结果。
- `assignCellNumbers` 已改成单规则处理，当前会先查出库单，再查格口，并按格口顺序回填命中的订单候选。
- 返回结果中每个格口会带上 `matchedRule`、`availableShipments`，并保留 `sortingWallId`、`sortingWallCode`、`workStationId`、`col`、`tier`、`shopeeWaveCode`、`currentWaveRule` 等基础字段。
- 当前已经接入基础出库单查询、排序和 `WAVE_RULE_FIELD_MATCHING` 字典基础匹配；高级字段语义仍待补齐。

### 3.11 `maxShipments` 逻辑

`maxShipments` 需要拆成两层理解，不能混成一个字段口径：

- `wave_master.maxShipments`：波次内部表字段，只用于波次创建阶段的截单控制，不参与本节可用出库单查找逻辑。
  - 波次创建时，会先按 `shipmentFilterCode` 对出库单做候选筛选。
  - 然后再按 `maxShipments` 截断候选单量。
  - 如果 `maxShipments > 0`，则按该值限制单次创建波次时可取出的最大出库单数量。
  - 如果 `maxShipments <= 0`，当前实现默认最多取 `100` 单。
  - 候选单量最终仍需满足 `minShipments`，否则本次不创建波次。
- `wave_rule.maxShipments`：规则级单次抓单上限。
  - 当前已在规则表和配置页中保留。
  - 规则匹配链路已开始接入，当前查单逻辑会消费该字段作为单次抓单上限。
  - 后续如需调整上限口径，只修改规则层，不要和其他内部表字段混用。

当前代码状态说明：

- 波次主流程在创建阶段会读取 `wave_master.maxShipments`，但这里的可用出库单查找逻辑不直接依赖它。
- `wave_rule.maxShipments` 当前已在 `findAvailableShipments` 中消费，作为当前规则的单次抓单上限。
- 文档中出现 `maxShipments` 时，必须注明它属于哪一层，避免歧义。

### 3.12 可用出库单查找逻辑

格口候选和出库单候选是两条独立链路，不能把“查格口”当成“查单”。

当前需要补齐的可用出库单查找逻辑如下：

- 先从出库单池里查候选单，再判断是否满足当前波次规则。
- 候选单要同时满足仓库、出库类型、订单结构、状态、锁定状态和规则字段匹配条件。
- 当前代码已接入仓库、出库类型、`shipmentCategory1`、在池状态、锁定状态、`wave_rule.maxShipments` 和 `WAVE_RULE_FIELD_MATCHING` 字典字段匹配。
- 规则字段匹配重点包括：
  - `channel_id`
  - `fulfillment_chain_id`
  - `order_structure`
  - `order_size` / `max_order_size`
  - `sku_size_type`
  - `category_level_1_id`
  - `shop_group`
  - `shop_id`
  - `urgent_flag`
  - `outer_packaging`
  - `delivery_region`
  - `is_fragile`
  - `is_liquid`
  - `is_high_value`
  - `with_battery`
  - `is_danger`
  - `max SKU Pieces Per Order`
  - `Mix Mode Max SKU Pieces Filter`
- 多值字段默认支持按“包含”筛选；需要排除或仅保留某些值时，按字段配置约定转换成 `include` / `exclude` / `only` 口径。
- `order_structure` 仍以 `shipmentCategory1` 为实际落字段做匹配，`SSSQ` 与 `Same SKU Same Qty` 视为同一逻辑。
- `wave_master` 是内部表，不参与这里的可用出库单查找逻辑。
- 规则层的 `wave_rule.maxShipments` 已在 `WaveRuleMatchService#findAvailableShipments` 中作为当前规则的单次抓单上限使用。

当前代码状态说明：

- 这条“可用出库单查找”逻辑已经开始落到 `WaveRuleMatchService#findAvailableShipments`。
- `WaveRuleMatchService` 现在已同时负责规则遍历、订单池基础查单和格口分配，但还没有覆盖所有字段匹配条件。
- 后续如果要进一步细化为“规则命中后再查单”，应继续在当前服务或独立查询服务里补齐，不要和格口状态、任务回写混写。

### 3.13 出库单分配后的处理

出库单分配不是最终结果，而是进入波次执行前的准备阶段。

当前需要把分配后的动作单独拆开维护，避免只做“查到单”却没有后续状态闭环。

分配成功后，建议按以下顺序处理：

- 固化波次和规则关联，保证本次分配对应的 `wave_rule`、波次号段和业务单据能串起来。
- 回写出库单的波次关联状态，例如 `shopeeWave`、`kickOutWave` 等字段，防止同一单再次被其他波次重复占用。
- 回写工作站任务头的波次信息，例如 `shopeeWave`、`waveRule`，让执行层知道当前任务属于哪一条波次链路。
- 同步分拨墙/格口占用状态，并把当前正在使用的规则写入 `wcs_sorting_wall_cell.currentWaveRule`，确保补单和执行层能识别当前槽口实际使用的规则。
- 触发后续执行动作，包括拣货、集波、补单和必要的执行校验。

如果分配后发现库存不足、规则冲突、上游校验失败或并发占用失败，则需要进入踢单或回流流程：

- 已分配单据从当前波次中移除。
- 恢复单据的可分配状态，重新回到候选池。
- 记录失败原因，供后续重试或人工排查。

当前代码状态说明：

- 当前文档层面先把“分配后动作”拆出来，后续再按模块补实现。
- 分配后的状态回写和执行联动，不应混在格口候选或出库单查找逻辑里。

### 3.14 集波后自动运行波次

集波完成后，系统需要自动进入波次运行阶段，而不是停留在“已分配未执行”的中间状态。

当前流程建议按以下顺序执行：

- 先基于 `wave_rule` 对出库单十几个规则属性做完全匹配，确认当前波次可运行。
- 再执行库存最优分配逻辑：
  - 优先先到期先出，保证先进先出。
  - 其次按工作站点位与库存点位半径最小的库存优先。
  - 同半径内，低层货位优先。
- 运行成功后，回写波次和任务关联字段：
  - 将 `shopee_wave` 的波次号段绑定到拣货任务头 `shopeeWave` 字段。
  - 同时绑定到出库单头 `shopeeWave` 字段。
  - 将当前 `waveRule` 记录到 `wave` 表的 `waveRule` 字段。
  - 同步记录到拣货任务头 `waveRule` 字段，供拣货执行衔接使用。
  - 将 `shopee_wave.status` 更新为 `200`，表示使用中。
- 如果运行过程中发现库存不足的单据，直接踢单：
  - 调用上游撤单接口，通知上游本次集波剔除的单号。
  - 该类库存不足踢单也要进入后续轮询补单逻辑，不直接结束整条波次链路。
- 运行成功后，波次进入执行态，后续继续衔接拣货、补单、踢单和执行校验。

当前代码状态说明：

- `WaveRuleMatchService#addShipmentsToInternalWave` 当前已在批量加入内部波次后调用 `WaveService#run`。
- `WaveService#run` 是异步提交队列消息，`WaveRuleMatchService` 不解析同步踢单结果。
- 库存不足踢单由 `wms-wave` 回池链路处理，回池时清空 `shipment_header.shopeeWave/waveRule`，不记录 `kickOutWave`。
- 库存不足踢单后的上游撤单接口仍待接入，补单由后续轮询按格口缺口继续处理。

### 3.15 上墙优先级与字段匹配

波次上墙和抓单时，优先级和字段匹配需要按照统一口径执行，不能把“先匹规则”写成“先按数量抓单”。

当前需求口径如下：

- 先看 `wave_rule` 表的优先级字段 `wavePriority`，数字越大优先级越高。
- 按 `wavePriority` 从高到低排序，优先抓取满足 `wave_rule` 的订单。
- 抓单时同时参考 `wave_rule.maxShipments`，按规则设定的最大订单数进行截断。
- 如果高优先级 `wave_rule` 的订单池暂时不足 `maxShipments`，也要优先匹配该高优先级规则下能拿到的单，不因为数量不满就降级到低优先级规则。
- 规则命中的订单池内部，还要按订单下发优先级继续排序：
  - 先看 `shipment_header.priority`，数字越大优先级越高。
  - 同一优先级下，先看 `shipment_header.cutOffTime`，按时间到分钟维度越早越优先。
  - 再看 `shipment_header.purchaseTime`，按时间到小时维度越早越优先。
  - 最后看 `shipment_header.orderTime`，按订单创建时间越早越优先。
- 抓单前需要先做字段匹配，只有 `wave_rule` 字段与出库单字段匹配上的单子，才进入后续抓单优先级判断。
- 命中规则后，再绑定该 `wave_rule` 对应的分拣墙槽口，槽口绑定关系优先跟随规则而不是跟随订单数量变化。
- 字段匹配规则统一走数据字典 `WAVE_RULE_FIELD_MATCHING`，用于声明：
  - 出库单字段
  - `wave_rule` 表字段
  - 匹配条件类型，例如 `包含`、`等于`
  - 对应的运算符口径，例如 `in`、`=`

字段匹配的执行含义：

- 先按 `WAVE_RULE_FIELD_MATCHING` 逐项判断出库单是否满足规则字段。
- 匹配上的单子，再进入优先级排序和抓单数量控制。
- 订单下发优先级只在同一规则命中的候选单之间生效，不改变 `wavePriority` 的规则排序结果。
- 不同字段可以有不同匹配条件，不要把所有字段强制当成同一种比较方式。
- `wave_rule.maxShipments` 只控制当前规则最多抓多少单，当前已在可用出库单查询中作为 `limit` 使用。

当前代码状态说明：

- 当前代码已按 `wavePriority` 做规则排序，并按 `shipment_header.priority`、`cutOffTime`、`purchaseTime`、`orderTime` 做同规则内候选单排序。
- `WaveRuleMatchService#findAvailableShipments` 已接入 `WAVE_RULE_FIELD_MATCHING`，按 `identifier` 读取实际 `shipment_header` 字段，按 `value1` 读取 `wave_rule` 字段，按 `value2` 执行 `IN`、`=` 或 `LE` 匹配。
- `WAVE_RULE_FIELD_MATCHING` 已作为统一数据字典维护，当前无同名默认映射；未配置有效 `identifier/value1` 或不在白名单内的配置会被忽略。

### 3.16 槽口绑定与拣货衔接

集波后，除了按 `waveRule` 选出可用格口候选外，还需要把绑定结果和拣货执行设计衔接起来。

当前需求口径如下：

- 集波后绑定对应 `wcs_sorting_wall_cell.waveRule` 设定相同规则的槽口。
- 绑定的槽口需要记录在任务表或波次表中，作为拣货执行衔接依据。
- 绑定记录不是重新生成规则，而是把当前波次命中的槽口固化下来，供后续拣货执行使用。
- `wcs_sorting_wall_cell` 里的 `waveRule` 负责表达槽口可接收的规则范围。
- `shopeeWave`、`waveRule` 等字段负责把波次、任务和槽口串起来，避免执行阶段再重新推导。

当前代码状态说明：

- 现有 `wcs_sorting_wall_cell` 已具备 `waveRule`、`currentWaveRule` 和 `shopeeWaveCode` 这类基础关联字段。
- 后续如果需要更强的执行衔接能力，可以在任务表或波次表补充槽口记录字段，但文档口径先统一为“绑定结果需要可落库”。
- 这一节和上墙优先级不是同一件事，上墙优先级负责“选哪条规则、抓哪些单”，槽口绑定负责“这些单最终落到哪个执行槽口”。

### 3.17 单品单件动态加单与换箱换波

`SSSQ` 单品单件场景需要支持波次动态加单和踢单，因为这类货品体积不固定，槽口要求是“尽量放满为止”。

当前需求口径如下：

- 仅 `SSSQ` 场景使用单独的动态加单阈值参数 `low_threshold`。
- `low_threshold` 用于在槽口未放满时触发轮询补单。
- 当前代码已新增 `WAVE/low_threshold` 系统参数，默认值为 `0`；当格口已有 `shopeeWaveCode` 且当前 Shopee 波次已绑定出库单量低于该阈值时，允许继续补单，达到或超过阈值时跳过该格口补单。
- 计划任务形式的统一入口 `pollReplenishment/pollReplenishmentOnce` 已落地，复用 `WaveRuleMatchService#replenishCell` 处理 `low_threshold` 和已有 Shopee 波次格口的补单。
- 如果按当前波次分配后，例如 30 单一波只分到了 27 单，但槽口还没放满，则需要继续加单。
- 如果槽口已经放满，则先封箱，再进入换波流程。
- 未分拣的单据需要按踢单逻辑重新回到订单池。

换箱换波的处理口径如下：

- 当 `SSSQ` 波次槽口已满但当前波次还没跑完时，剩余订单需要重新取一个与单据类型一致的 `shopee_wave` 波次号段。
- 这个场景走 `flow pick` 的换箱接口，不是上文 1.7 的上游合波校验接口。
- 上游校验通过后：
  - 将剩余订单的拣货任务头 `shopeeWave` 更新为新波次号段。
  - 同时更新出库单头 `shopeeWave`。
  - 同步更新 `shopee_wave.status` 为 `200` 使用中。
  - 因为剩余订单数量通常不足规则最大订单数，所以仍要继续走轮询补单逻辑。
- 上游校验不通过时：
  - 对订单执行拦截取消出库，恢复到正常订单池逻辑。
  - 取消对应任务下发。
  - 同时给 AGV 下发取消回库指令。

当前代码状态说明：

- 这一节仅针对 `SSSQ` 单品单件场景。
- 轮询补单、换箱换波、踢单和 AGV 取消回库要保持解耦，但共享同一套候选回流思想。
- 当前 `low_threshold` 已限定只影响 `SSSQ` 单品单件 wave rule，不扩散到其他 wave rule。
- 当前实现以 `wave_rule.maxShipments` 作为满槽触发阈值；满槽后查询当前工作站、旧 Shopee 波次、当前 `waveRule` 下未完成任务订单，重新获取同单据类型 Shopee 波次号段，并批量回写任务头、出库单头和格口 `shopeeWaveCode`。
- 当前通过可选 `essFlowPickService.sealBox/changeBoxWave` 承接 Flow Pick 真实接口；接口未接入时记录系统处理日志并执行 WES 本地状态闭环。
- 换波失败时不会删除 `shopee_wave` 数据；新号段未被引用时仅回退状态为未使用。恢复订单池、取消任务下发、AGV 取消回库仍等待真实接口联调。

### 3.18 轮询补单逻辑

轮询补单由计划任务持续扫描，不和集波主流程、上游合波校验、换箱换波强耦合。

#### 3.18.1 `SSSQ` 单品单件轮询补单

当前口径如下：

- 当工作站对应单品单件 `waveRule` 的槽口未完成订单数低于系统参数 `low_threshold` 时，触发补单。
- 补单时，拿工作站对应订单类型以及该槽口的 `waveRule` 去订单池抓单。
- 抓单优先级与上文 `3.15` 中一致。
- 一次性补 `wave_rule.maxShipments` 指定的最大订单数，实际接入后由补单服务消费该字段。
- 抓到的单据用该槽口的 `shopeeWave + 单号` 请求上游接口验证是否可以继续合波。
- 上游校验 `ok` 时，WES 自动加入波次运行，与上文 `3.14` 逻辑一致。
- 上游校验 `不 ok` 时，重新在订单池继续抓单再请求上游校验。
- 已抓取但不符合的单子，需要过滤出当前波次号外。
- 单子可能被多个波次号请求上游校验失败，技术实现时需要支持多次踢单记录，`kickOutWave` 可用逗号分隔保存。

#### 3.18.2 非 `SSSQ` 场景轮询补单

当前口径如下：

- 轮询扫描正在作业的各工作站和各槽口。
- 统计当前槽口绑定的 wave 订单数是否满足 `wave_rule.maxShipments`，该字段当前先按配置口径记录。
- 统计范围包含该槽口当前波次进行中、已完成、未开始的单子。
- 将汇总结果与 `wave_rule.maxShipments` 比较，若缺少 `X` 单，则去订单池抓 `X` 单。
- 抓单时仍按上文 `3.15` 的抓单优先级执行。
- 抓到的单据按该槽口的 `shopeeWave + 单号` 请求上游接口验证是否可以继续合波。
- 上游校验 `ok` 时，WES 自动加入波次运行，与上文 `3.14` 逻辑一致。
- 上游校验 `不 ok` 时，重新在订单池继续抓单再请求上游校验。
- 已抓取不符合的单子需要从当前波次号外过滤掉。
- 被 ESS 合波校验拒绝的单子，需要在 `shipment_header.kickOutWave` 里记录 Shopee wave 号，多个 wave 号用逗号隔开，后续该波次号再次抓单时过滤；库存不足踢单不记录 `kickOutWave`。

当前代码状态说明：

- 当前已落地计划任务形式的轮询补单入口 `pollReplenishment/pollReplenishmentOnce`，并复用单格口补单方法 `WaveRuleMatchService#replenishCell`。
- 当前入口复用 `findPickingWorkstations`、`findReplenishmentCells`，用于扫描正在作业的工作站和已绑定 Shopee 波次的 `USED` 格口，并逐格口触发补单。
- 当前格口分配链路和轮询补单链路均具备“ESS 校验失败写 `kickOutWave`、再次抓单时过滤同 Shopee 波次”的基础能力，并已支持常见失败订单 ID、单号和 Map 结构解析；真实 ESS 响应契约仍待接口联调确认。
- `SSSQ` 与非 `SSSQ` 场景的补单策略不同，但都要保持“先抓单、再上游校验、失败过滤、重新抓单”的闭环。

### 3.19 推拣货任务与推出库单的区别

`Shopee` 推送过来的单据分两类，后续流程不能混用：

#### 3.19.1 推拣货任务

推拣货任务是“命中人工 + 自动化区”的单子，特点如下：

- 推送时会同步推上游波次号段，不需要上文的取号逻辑。
- WES 按推送的拣货任务建立出库单。
- 再按上文的集波逻辑，结合工作站、出库类型和 `wave_rule` 抓单。
- 这类订单按“1 单跑 1 个波次”处理，且后续无需补单。
- 这类订单区分标识待梳理接口后提供。
- 这类单子统一按“1 拣货单 1 波次 1 槽口”设计，不走轮询补单逻辑。

#### 3.19.2 推出库单

推出库单是“纯命中自动化区”的单子，特点如下：

- 这类单子不走推拣货任务的特殊流程。
- 按上文的正常集波流程处理。
- 按波次号段申请、上游合波校验、槽口绑定、轮询补单等标准链路执行。
- 这类单据仍需遵守 `wave_rule`、`wavePriority`、`maxShipments`、`low_threshold` 等通用规则。

当前代码状态说明：

- 推拣货任务和推出库单需要在入口层就分开，不要进入同一套补单或取号逻辑。
- 推拣货任务的区分标识目前还待接口梳理后补充。
- 后续实现时，推拣货任务以“一单一波一槽口”为核心约束，推出库单继续走标准集波链路。

## 四、模块映射

### 4.1 `wms-wave`

建议作为波次规则和波次号段的主实现模块，承接：

- `wave_rule` 配置
- `shopee_wave` 号段管理
- wave rule 优先级
- 波次最大订单数
- 波次运行与状态流转

已落地的代码改动：

- 新增 `WaveRule` 实体和 `WaveRuleService`
- 新增 `ShopeeWave` 实体和 `ShopeeWaveService`
- `Wave` 新增 `waveRule`
- 新增 Flyway 脚本创建 `wave_rule` 与 `shopee_wave`

### 4.2 `wms-shipping`

承接出库单和订单头字段变更，包括：

- 出库单类型
- `shipment_header.priority`
- `shipment_header.cutOffTime`
- `shipment_header.purchaseTime`
- `shipment_header.orderTime`
- `shipmentCategory1`
- `kickOutWave`
- `shopeeWave`

已落地的代码改动：

- `ShipmentHeader` 新增 `cutOffTime`、`purchaseTime`、`orderTime`
- `ShipmentHeader` 新增 `shopeeWave`、`kickOutWave`
- `shipment_header`、`archive_shipment_header`、`deleted_shipment_header` 的 table 配置同步补齐
- Flyway 脚本同步补齐三张表字段

### 4.3 `wms-task`

承接拣货任务、任务头字段和集波后的执行联动，包括：

- 拣货任务头 `shopeeWave`
- 拣货任务头 `waveRule`
- 单品单件动态加单
- 任务和波次执行衔接

已落地的代码改动：

- `WcsWorkStationPickTaskHeader` 新增 `shopeeWave`、`waveRule`
- `wcs_work_station_pick_task_header` table 配置同步补齐

### 4.4 `wms-core-wave`

承接波次相关核心算法，比如：

- wave rule 匹单逻辑
- 优先级排序
- 订单池筛选
- 波次运行前后的核心判定
- 当前波次创建顺序已经调整为“先查 `wave_rule`，再按规则条件查出库单”

### 4.5 `wms-core-task`

承接任务执行相关的核心能力，包括：

- 任务生成和执行约束
- 拣货和波次联动
- 任务优先级衔接

### 4.6 `wms-common`

承接共用基础能力，包括：

- 数据字典 `WAVE_RULE_FIELD_MATCHING`
- 通用枚举
- 共享字段定义
- 通用规则封装

### 4.7 `wms-automation`

承接自动化区和 AGV 联动能力，包括：

- 任务取消
- 取消回库指令
- 自动化区单据的执行边界

已落地的代码改动：

- `WcsSortingWallCell` 新增 `waveRule`
- `WcsSortingWallCell` 新增 `currentWaveRule`
- `WcsSortingWallCell` 现已支持保存 `shopeeWaveCode`
- `UpdateSortingWallCellCmd` 新增 `waveRule`
- `UpdateSortingWallCellCmd` 新增 `currentWaveRule`
- `WcsSortingWallCellService` 已支持更新 `waveRule`
- `WcsSortingWallCellService` 已支持更新 `currentWaveRule`
- `wcs_sorting_wall_cell` table 配置和 Flyway 脚本已同步补齐

### 4.8 `wms-inventory`

承接库存相关判定和库存不足场景依赖：

- 库存不足踢单
- 可用库存优先分配
- 库存点位和工作站半径联动

### 4.9 配置模块

建议同步更新以下配置模块：

- `wms-wave-config`
- `wms-shipping-config`
- `wms-task-config`
- `wms-inventory-config`

用于承接规则枚举、静态配置和界面配置项。

## 五、实施计划

### 第 1 步：补规则和表结构

先完成以下内容：

- `wave_rule` 表设计和字段补齐
- `shopee_wave` 表设计和字段补齐
- 分拨墙槽口表 `wcs_sorting_wall_cell` 增加 `waveRule`
- 出库单头和任务头字段补齐

这一阶段的目标是把所有需求依赖的字段和持久化基础先补齐。

状态：已完成。

### 第 2 步：补基础规则引擎

再完成：

- wave rule 字段匹配
- 波次优先级排序
- 订单类型识别
- 出库单筛选规则
- 分拨墙槽口 wave rule 匹配

这一阶段的目标是让系统能够判断“哪些单能进哪条规则”。

状态：基础能力已落地，高级语义待实现。
当前已能按规则逐条查单并返回格口分配结果，已接入基础排序、`maxShipments` 截断、`waveType/shipmentCategory1` 匹配和 `WAVE_RULE_FIELD_MATCHING` 字典基础匹配。

### 第 3 步：补集波主流程

然后实现：

- 工作站按订单类型和规则集波
- 号段申请
- 波次生成
- 波次绑定出库单和任务
- 集波后自动运行

备注：

- 由于完整波次级规则引擎仍在重建阶段，波次主流程仍沿用原有 `shipmentFilterCode` 过滤逻辑。

这一阶段的目标是跑通主链路。

状态：基础主链路已落地，接口闭环待实现。
当前已具备创建内部波次、批量加入内部波次、异步提交运行、基础字段回写和 ESS 拒单基础解析能力；上游号段申请、真实 ESS 响应契约、库存不足踢单上游撤单和任务头字段完整闭环仍待补齐。

### 第 4 步：补补单与踢单

随后实现：

- 轮询补单
- 轮询补单建议拆成以下几层：
- 第一层先判断波次是否仍处于允许补单的状态，避免已锁定、已释放、已关闭的波次继续被补。
- 第二层按波次规则重新拉取订单池候选单，必须同时满足规则匹配、状态可用、未被其他波次占用。
- 第三层按波次剩余容量和订单优先级决定是否补入，补入后同步刷新波次统计和订单池状态。
- 第四层若连续若干轮没有新增候选单，则提前结束轮询，避免空转。
- 第五层若补单过程中发生库存不足、规则冲突或并发锁失败，应立即停止当前轮询，并保留已成功补入的结果。
- 库存不足踢单
- 上游校验失败踢单
- 失败单据回流订单池

这一阶段的目标是让系统在异常情况下还能稳定收敛。

状态：待实现。

### 第 5 步：补单品单件特殊逻辑

最后补单品单件特殊流程：

- 动态加单
- 换箱换波
- 封箱后的波次切换
- 仅对 `SSSQ` 生效的补单/换波策略

这一阶段的目标是把高频特殊场景单独收口。

状态：待实现。

## 六、验证计划

### 6.1 规则验证

- 验证 wave rule 字段映射是否正确
- 验证规则优先级排序是否正确
- 验证多选、包含、等于等匹配条件是否正确

### 6.2 数据验证

- 验证 `wave_rule` 表和 `shopee_wave` 表的增删改查
- 验证出库单、任务头字段是否能正确落库
- 验证分拨墙槽口与 wave rule 的绑定关系

### 6.3 流程验证

- 验证集波主流程是否能从订单池成功抓单
- 验证补单流程是否能触发
- 验证踢单流程是否能正确回流
- 验证单品单件换箱换波是否符合预期

### 6.4 联动验证

- 验证上游号段申请
- 验证上游校验失败时的回退逻辑
- 验证自动化区取消回库指令

### 6.5 当前验证结果

- 已完成 JSON 表配置静态校验。
- 已完成字段名和迁移脚本的人工一致性校对。
- 当前环境缺少 `gradlew` 和全局 `gradle`，未执行仓库级编译。

## 七、风险点

- wave rule 字段多、匹配条件多，容易出现规则解释不一致。
- 单品单件和非单品单件的补单逻辑不同，容易产生分支遗漏。
- 上游接口未完全明确时，接口契约和失败处理需要先留好扩展点。
- 出库单、任务、波次、槽口四者之间的状态同步需要严格控制。

## 八、建议落地顺序

1. 先补表结构和字段。
2. 再补规则匹配和基础算法。
3. 然后补主集波流程。
4. 再补补单、踢单和异常回退。
5. 最后补单品单件特殊流程。

## 九、下一步建议

- 如果你要，我可以继续把这份计划拆成“按模块的开发任务清单”。
- 如果你要，我也可以继续把这份计划细化成“接口级别”的需求拆分文档。
