Files
FTC25009/docs/superpowers/specs/2026-07-16-dense-outbound-relocation-dispatch-design.md

282 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 密集库出库补偿、移库与指定通道作业设计
## 1. 目标
在不改变现有入库流程和非密集库出库规则的前提下,完成以下能力:
1. 出库作业下发 WCS 失败后自动补偿,最多自动重试 5 次,耗尽后支持人工下发。
2. 所有 03 密集库出库与移库作业在下发前都必须是所在通道当前最外侧作业。
3. 指定托盘和指定货位出库遇到遮挡时,自动生成 `in_or_out=23` 的移库作业。
4. KPI 货位平面图支持按通道、数量和目标通道生成同层移库作业。
5. 新增指定通道出库菜单,按通道组查看库存并按端头托盘数量生成出库作业。
6. 入库、普通出库、指定货位/通道出库和移库使用不同的 WCS 接口配置。
## 2. 现状约束
- 后端为 RuoYi Maven 多模块项目,出库作业与入库作业共用 `mydo_job`、`mydo_job_detail`。
- 作业归档过程存在 `INSERT ... SELECT *`,直接给 `mydo_job` 增加字段会同步影响活动表、归档表和多个存储过程。
- 03 仓的一个通道由同一排、同一层、同一通道带的 `group_coding` 标识。
- `bas_cargo_area_config.direction_type` 是入库由深到浅的货位选择方向:方向 `1` 为列号从小到大,方向 `2` 为列号从大到小。
- 当前出库代码错误复用了 `sendPutawayTask` 配置键;本设计改为按作业类型路由。
- 当前 WCS 模拟开关默认开启,真实联调时必须显式关闭。
## 3. 总体架构
采用“业务作业 + WCS 下发记录”的 Outbox 结构:
- `mydo_job` 继续保存业务作业,不增加补偿字段。
- 新表 `wcs_job_dispatch` 与作业一一关联,保存下发状态、重试次数、错误、时间和并发版本。
- `OutboundDispatchService` 是所有出库、指定货位、指定通道和移库作业下发 WCS 的唯一入口。
- `DenseLaneService` 负责密集库通道顺序、遮挡托盘、最外侧资格和移库目标货位选择。
- `RelocationJobService` 负责自动/人工移库作业生成和 WCS 完成后的库存搬移。
- Quartz 每分钟调用 `WcsOutboundRetryTask.retryFailedJobs()` 执行自动补偿。
所有入口统一调用同一套下发资格检查,前端按钮、定时重试和分配后即时下发不能绕过密集库最外侧规则。
## 4. WCS 下发记录
新增表 `wcs_job_dispatch`:
| 字段 | 类型 | 说明 |
| --- | --- | --- |
| `job_id` | nvarchar(255) PK | 对应作业 ID |
| `dispatch_status` | nvarchar(20) | `PENDING`、`SENDING`、`BLOCKED`、`RETRY_WAIT`、`SUCCESS`、`EXHAUSTED` |
| `auto_retry_count` | int | 已执行的自动重试次数,范围 0 至 5 |
| `manual_retry_count` | int | 人工下发次数 |
| `last_error` | nvarchar(1000) | 最后一次失败或等待原因 |
| `last_attempt_time` | datetime2 | 最后一次实际调用 WCS 时间 |
| `next_retry_time` | datetime2 | 下次允许自动重试时间 |
| `success_time` | datetime2 | 下发成功时间 |
| `version_no` | int | 并发抢占版本 |
| `create_time` | datetime2 | 创建时间 |
| `update_time` | datetime2 | 更新时间 |
初次下发失败不计入自动重试次数,状态设为 `RETRY_WAIT`、`auto_retry_count=0`。定时任务每调用一次前递增自动重试次数,失败达到 5 次后设为 `EXHAUSTED`。人工下发只递增 `manual_retry_count`,不重置自动重试次数,也不会重新进入自动队列。
密集库作业因不是当前最外侧而未调用 WCS 时,不计失败、不增加重试次数,记录统一设为 `BLOCKED` 并写入等待原因。外侧作业完成后立即唤醒同通道 `BLOCKED` 记录;定时任务每分钟复查一次 `BLOCKED` 记录作为异常恢复兜底。
通过原子状态更新把记录抢占为 `SENDING`,防止定时任务、人工按钮和业务线程并发重复下发。超过发送超时时间仍为 `SENDING` 的记录允许重新进入补偿流程。WCS 以 `taskNo` 作为幂等键;重复报文必须返回同一任务的成功确认。
## 5. 下发状态流转
### 5.1 分配后即时下发
1. 出库分配或作业生成事务提交。
2. 创建 `PENDING` 下发记录。
3. 调用统一下发服务。
4. 资格不满足:转为 `BLOCKED`,业务接口返回作业已生成、等待外侧作业完成。
5. WCS 调用失败:记录 `RETRY_WAIT`,业务接口返回作业已生成、WCS 下发失败并进入补偿队列。
6. WCS 调用成功:记录 `SUCCESS`,作业状态改为 `3`,执行阶段改为 `1`,写入开始时间。
分配成功后不因 WCS 网络失败回滚库存预占和作业生成,避免操作员重复分配。数据库分配失败仍按现有事务回滚。
### 5.2 自动重试
- Quartz 每分钟扫描 `RETRY_WAIT` 且 `auto_retry_count < 5`、`next_retry_time <= 当前时间` 的记录,同时复查 `BLOCKED` 作业是否已经成为最外侧。
- 每批按创建时间处理,单批数量可配置,默认 100。
- 重新执行作业存在性、作业状态、密集库最外侧和 WCS 路由检查。
- 网络或业务失败采用固定 1 分钟间隔;第 5 次失败转为 `EXHAUSTED`。
- `BLOCKED` 复查和最外侧资格不满足都不消耗重试次数。
### 5.3 人工下发
- 出库作业管理对 `PENDING`、`RETRY_WAIT`、`EXHAUSTED` 的未完成作业显示“手动下发 WCS”。
- 操作前弹出确认框,展示自动重试次数和最后错误。
- 人工下发仍执行密集库最外侧校验。
- 失败后保持原自动状态或 `EXHAUSTED`,更新错误和人工次数;成功后转为 `SUCCESS`。
## 6. 密集库最外侧规则
仅仓库 `03` 且货位存在非空 `group_coding` 时应用密集库规则。
- 同通道范围:`warehouse_id + group_coding`,`group_coding` 已包含排和层。
- 方向 `1`:入库从小列到大列,当前最外侧为所有占用/预占货位中的最大列。
- 方向 `2`:入库从大列到小列,当前最外侧为所有占用/预占货位中的最小列。
- 方向 `3`:按距离两侧边界较近者优先,同距离取较小列。
- 占用范围包括状态 `2`(满)、`3`(入库分配)、`4`(出库/移库分配)且托盘码非空的货位。
资格检查以事务中的实时货位数据为准,不能使用页面缓存。若同一最外侧托盘已经存在状态 `2/3` 的活动作业,只允许该作业继续,其他作业等待。
## 7. 遮挡处理与自动移库
### 7.1 触发范围
- 出库单分配明确指定库存明细、库存主记录或托盘时。
- 指定货位出库选择 03 仓密集库货位时。
- 指定托盘前存在其他占用托盘时。
### 7.2 遮挡托盘判定
按第 6 节的出库顺序,位于目标托盘外侧的占用托盘均为遮挡托盘。
- 遮挡托盘已有活动出库作业时,不再生成移库作业;该出库作业自然成为前置动作。
- 遮挡托盘没有活动出库/移库作业时,按从外到内顺序生成移库作业。
- 目标出库作业可以同时生成,但只有所有外侧货位释放后才具备下发资格。
### 7.3 自动移库目标
目标货位必须满足:
- 同仓库、同层、不同 `group_coding`。
- 状态为 `1`、未锁定、无托盘码。
- 符合托盘类型允许仓库和货位类型规则。
- 符合原通道的存储模式、项目号或物料相容规则。
- 不得被其他活动作业预占。
候选通道优先沿用现有入库规则:优先已有相容库存的通道,再按占用数量和通道编码选择;通道内按入库方向选择最深可用货位。生成作业后将源货位置为 `4`,目标货位置为 `3` 并预留托盘码。
## 8. 移库作业
- `in_or_out=23`,任务号调用 `util_serial_task '23'`。
- 主作业的 `cargo_space_id` 为源货位,`target_cargo_space_id` 为目标货位。
- 每个托盘生成一个移库主作业;按库存明细生成对应 `mydo_job_detail`,保留库存、批次、项目号和数量追溯。
- 移库作业使用独立 WCS 路由,不进入 PDA 出库确认列表。
WCS 完成移库回调后,在一个数据库事务内:
1. 锁定移库作业、源货位、目标货位和托盘库存。
2. 校验作业类型为 23、作业已下发、源目标仍与作业一致。
3. 将该托盘所有 `bas_stock.cargo_space_id` 更新为目标货位。
4. 源货位置为 `1` 并清空托盘码,目标货位置为 `2` 并保留托盘码。
5. 作业及明细归档并从活动表删除。
6. 事务提交后触发同源通道下一条等待作业的资格检查和即时下发。
重复完成回调通过活动/归档作业查询实现幂等,已完成时返回成功。
## 9. KPI 货位平面图指定移库
仅在 03 密集库视图显示“指定移库”命令。弹窗包含:
- 当前层,只读。
- 源通道选择,只列出当前层有满货位的通道。
- 移库托盘数量,范围 1 至源通道当前可移动托盘数。
- 目标通道选择,只列出当前层具有足够相容空位的其他通道。
- 外侧托盘预览,展示托盘码、源货位、库存明细和总数量。
提交时后端重新锁定并验证通道、层、数量、库存与目标容量,按外到内选择源托盘并生成移库作业。页面数据只用于预览,不作为作业生成依据。
## 10. 指定货位出库
现有非密集库逻辑保持不变。03 密集库指定货位出库调整为:
1. 锁定目标货位和通道。
2. 计算外侧遮挡托盘。
3. 为没有活动出库作业的遮挡托盘生成移库作业。
4. 生成目标托盘的指定货位出库作业及下发记录。
5. 先尝试下发当前最外侧移库/出库作业;目标作业等待通道逐步清空。
## 11. 指定通道出库
在出库管理下新增“指定通道出库”菜单和权限:
- `out:denseLaneOutbound:list`
- `out:denseLaneOutbound:preview`
- `out:denseLaneOutbound:generate`
列表仅查询 03 仓 `group_coding` 非空且至少存在一个状态 `2` 满货位的通道,展示:
- 通道组编码、货区、排、层和方向。
- 满托盘数量、可出库托盘数量、库存总数量。
- 聚合库存摘要和可展开库存明细。
- 当前最外侧托盘及货位。
生成弹窗要求选择整托数量和适配出库口。后端按当前外侧顺序选择前 N 个未被活动作业占用的托盘,返回预览供确认,确认后每托生成一个指定出库作业和完整作业明细。任何托盘状态变化都会使旧预览失效,提交时重新校验并返回明确冲突信息。
## 12. WCS 路由
配置增加独立键并允许通过环境变量覆盖:
```yaml
external:
systems:
WCS:
apis:
sendPutawayTask: ${WCS_PUTAWAY_PATH:/wcs/api/receiveWmsJob}
sendRetrieveTask: ${WCS_OUTBOUND_PATH:/wcs/api/receiveWmsOutJob}
sendLocationOutboundTask: ${WCS_LOCATION_OUTBOUND_PATH:/wcs/api/receiveWmsLocationOutJob}
sendRelocationTask: ${WCS_RELOCATION_PATH:/wcs/api/receiveWmsMoveJob}
```
路由规则:
- `in_or_out < 20`:`sendPutawayTask`。
- `in_or_out = 20`:`sendLocationOutboundTask`,覆盖指定货位和指定通道整托出库。
- `in_or_out = 23`:`sendRelocationTask`。
- 其他 `in_or_out >= 20`:`sendRetrieveTask`。
所有接口继续按作业所属仓库解析独立 WCS 根地址。
## 13. UI 设计
沿用现有 Vue 2、Element UI 和 RuoYi 页面密度:
- 出库作业列表增加“WCS 下发状态”“自动重试”“最后错误”列;错误使用 tooltip,避免撑高表格。
- “手动下发 WCS”使用发送图标和确认对话框,仅对允许状态显示。
- KPI 平面图在密集库工具栏增加移库命令,使用紧凑表单弹窗,不改变平面图颜色体系。
- 指定通道出库采用筛选栏、通道表格和单层生成弹窗;库存明细使用表格展开行,不嵌套装饰卡片。
## 14. 数据库迁移
新增一份可重复执行的 SQL Server 迁移脚本,包含:
- 创建 `wcs_job_dispatch`、状态/重试索引和字段说明。
- 确认 `bas_task_no` 存在 ID `23`,存在则保留当前序号,不存在则补充移库类型。
- 新增指定通道出库菜单、按钮权限和角色授权兼容处理。
- 新增 Quartz 任务记录,调用 `wcsOutboundRetryTask.retryFailedJobs`,Cron 为每分钟一次,禁止并发。
- 为密集库通道、活动作业和下发扫描补充必要索引,创建前检查同名索引。
迁移不修改或删除现有业务数据,可重复执行。
## 15. 错误处理
- WCS 网络失败、超时、非成功响应和报文序列化失败均写入下发记录。
- 最外侧不满足、前置作业未完成和目标货位状态变化属于等待/并发冲突,不消耗自动重试次数。
- 自动移库目标不足时,原出库分配整体失败,不生成部分移库作业。
- 人工指定移库目标容量或相容性不足时,整个请求回滚。
- 移库完成时源目标或库存不一致,事务回滚并返回明确错误,不做部分库存搬移。
- 菜单生成作业时预览已过期,返回刷新提示,不自动替换为其他托盘。
## 16. 测试与验收
### 16.1 后端单元与 Mapper 测试
- 首次失败进入补偿队列且自动次数为 0。
- 自动重试恰好最多执行 5 次,第 5 次失败后耗尽。
- 人工下发超过 5 次仍可调用,但不会恢复自动队列。
- 调度并发抢占只有一个调用者成功。
- 非最外侧作业不调用 WCS且不增加重试次数。
- 方向 1、方向 2 的最外侧和遮挡集合正确。
- 指定托盘/货位遮挡时按外到内生成类型 23 作业。
- 自动目标同层、不同通道、相容且按入库规则选深位。
- 移库完成原子更新库存、源目标货位并归档。
- 指定通道预览与生成只选择端头 N 个托盘。
- `20`、`23` 和普通出库分别命中正确 WCS 配置键。
### 16.2 前端验证
- 出库作业列表正确显示状态、次数、错误和人工按钮权限。
- KPI 指定移库只能选择同层目标通道,数量和容量校验提示明确。
- 指定通道出库列表、展开明细、预览数量和生成确认可用。
- 生产构建通过,桌面和常用窄屏下表格、弹窗无文字遮挡。
### 16.3 集成场景
1. 构造 WCS 连续失败,验证初次调用加 5 次自动重试后耗尽。
2. 人工恢复下发,验证成功状态和作业状态更新。
3. 在方向 1 和方向 2 通道各构造三托遮挡,验证下发顺序为外到内。
4. 指定最内托盘出库,验证先生成并完成两个移库作业,再下发目标出库。
5. KPI 指定跨通道同层移库,验证库存和货位一致。
6. 指定通道出库 N 托,验证只选择端头 N 托及其完整库存。
7. 验证非 03 仓原有自动、手动和指定货位出库不受影响。
## 17. 需求覆盖
| 原需求 | 设计章节 |
| --- | --- |
| 失败标记、5 次重试、定时器、人工下发、最外侧校验 | 4、5、6 |
| 指定托盘遮挡自动移库,类型 23 | 7、8 |
| KPI 平面图指定同层移库 | 9 |
| 出库 WCS 地址区分 | 12 |
| 指定货位出库遮挡移库 | 10 |
| 指定通道出库菜单与端头 N 托 | 11 |