diff --git a/docs/superpowers/specs/2026-07-10-inbound-cargo-space-height-design.md b/docs/superpowers/specs/2026-07-10-inbound-cargo-space-height-design.md new file mode 100644 index 0000000..a8488cc --- /dev/null +++ b/docs/superpowers/specs/2026-07-10-inbound-cargo-space-height-design.md @@ -0,0 +1,85 @@ +# 入库货位高度分配设计 + +## 目标 + +为 01、02 仓建立普通货位和高货位数据,并让 WCS 请求入库货位时按 `height` 等级分配: + +- 01、02 仓的 1 层、5 层货位设置为 `the_height = 2`。 +- 01、02 仓其余层货位设置为 `the_height = 1`。 +- WCS `height = 2` 只能分配 `the_height = 2` 的货位。 +- WCS `height = 1` 优先分配 `the_height = 1` 的货位;普通货位没有可用候选时,才允许分配 `the_height = 2` 的货位。 + +## 当前链路 + +WCS 通过 `POST /api/external/wcs/inbound/location` 请求具体货位。控制器按作业 ID、任务号或托盘号找到入库作业,然后调用 `PdaService.lockInboundCargoSpace`,由 `PdaMapper.xml` 在作业既定巷道中并发锁定一个空货位。货位写回 `mydo_job` 后返回 WCS。 + +`PUT_OCCUPIED` 放货有货异常会在同一仓库、同一巷道重新选择货位。该路径读取 `mydo_job` 后通过 `BasCargoSpaceMapper.xml` 选择候选,因此首次分配时必须把 WCS 高度等级保存到 `mydo_job.height`,重分配才能继承相同限制。 + +## 接口与校验 + +`WcsInboundLocationRequest` 增加 `BigDecimal height`,与现有 `MydoJob.height` 类型保持一致。 + +- `height` 必传。 +- 只接受数值 `1` 或 `2`,其他值返回 WCS 业务错误,不进行锁位。 +- 首次成功分配时将值写入 `mydo_job.height`。 +- 已分配作业再次收到相同高度时幂等返回原货位,不重复锁位。 +- 已分配作业再次收到不同高度时返回冲突错误,不覆盖已保存高度,也不静默更换货位。 + +## 正常分配 + +`PdaService.lockInboundCargoSpace` 增加高度参数,并将其作为 `v_height` 传给锁位和已锁货位查询。 + +候选过滤统一使用容量规则 `bcs.the_height >= #{v_height}`。数据库只维护等级 1、2,因此该规则等价于: + +- 请求 2:只保留货位 2。 +- 请求 1:保留货位 1、2。 + +候选排序先按 `the_height` 是否与请求值完全相同排序,再保留现有的“空位最多排优先、排/层/列/货位编号升序”规则。这样请求 1 在任何普通候选存在时都不会占用高货位,请求 2 则只能命中高货位。 + +查询同一托盘已有锁位时返回 `the_height`。服务层先检查已有锁位是否满足本次请求:满足时幂等复用;不满足时返回冲突错误,不再锁定第二个货位。这样可覆盖“货位已锁定但作业回写前进程中断”的残留场景。 + +## 异常重分配 + +`selectAvailableInboundCargoSpaceInRoadway` 使用 `MydoJob.height` 应用相同的容量过滤和精确高度优先排序。`PUT_OCCUPIED` 不要求 WCS 再次上传高度,以数据库中首次持久化的作业高度为准。 + +历史作业中 `height` 为空或非 1、2 时,不允许重分配并返回明确错误,避免无约束选位。 + +## 数据库变更 + +增加可重复执行的 SQL 脚本,并在当前配置的 `FTC25009-WMS` 数据库中事务执行: + +```sql +update dbo.bas_cargo_space +set the_height = case when the_floor in (1, 5) then 2 else 1 end +where warehouse_id in ('01', '02'); +``` + +执行前后的事务内校验要求: + +- 目标总数必须为 340 行。 +- `the_height = 2` 必须为 136 行。 +- `the_height = 1` 必须为 204 行。 +- 01、02 仓不得残留空值或其他高度值。 + +若任一断言不满足则回滚。当前只读统计显示 340 行的 `the_height` 均为 `NULL`,因此首次执行实际更新 340 行。 + +## 测试与验证 + +自动化测试覆盖: + +- 请求缺少高度、非 1/2 高度时拒绝。 +- 首次请求把高度传入锁位服务并持久化到作业。 +- 相同高度重复请求幂等,不同高度重复请求冲突。 +- 已有残留锁位与请求高度不兼容时拒绝,不产生第二个锁位。 +- 正常锁位 SQL 对高度 1/2 的过滤及普通货位优先排序。 +- `PUT_OCCUPIED` 查询继承 `MydoJob.height` 并使用同样排序。 +- 现有货位排优先策略仍保留。 + +完成后运行 `mvn -pl ruoyi-admin test`,再运行后端打包或至少 `mvn -pl ruoyi-admin -am package -DskipTests` 验证编译。数据库执行后重新查询 01、02 仓逐层分布作为线上结果证据。 + +## 范围约束 + +- 只修改入库具体货位分配和放货有货重分配,不改变出库逻辑。 +- 只更新 01、02 仓;其他仓库货位高度不变。 +- 不改变托盘类型、仓库、巷道、货位状态和并发锁定等既有条件。 +- 不调整 `the_height` 数据类型;本次只使用整数等级 1、2。