Junjie
13 小时以前 1e66871d906bee660c114e390d9bf95181a44d38
docs: add station reroute refactor design spec
1个文件已添加
314 ■■■■■ 已修改文件
docs/superpowers/specs/2026-03-24-station-reroute-refactor-design.md 314 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/superpowers/specs/2026-03-24-station-reroute-refactor-design.md
New file
@@ -0,0 +1,314 @@
# 输送站重规划执行管道重构设计
## 1. 目标
本次改动只处理 `StationOperateProcessUtils` 中输送站重新计算路径、堵塞恢复、排序放行、绕圈监控相关逻辑的结构重构。
目标如下:
- 统一 `checkStationRunBlock`、`checkStationIdleRecover`、`checkStationOutOrder`、`watchCircleStation` 四条链路中的重规划执行流程。
- 把“目标站决策”和“命令派发副作用”从入口扫描逻辑中拆开,降低后续维护复杂度。
- 在不改变现有业务语义的前提下,固定重规划的执行顺序,减少遗漏校验、遗漏清理、遗漏状态同步的风险。
- 保持现有 `ZyStationV5Thread`、`StationMoveCoordinator`、Redis key、WMS 交互、出库排序/绕圈规则不变。
## 2. 非目标
以下内容不在本次范围内:
- 不修改 `ZyStationV5Thread#getRunBlockRerouteCommand` 的候选路径选择算法。
- 不修改 `resolveOutboundDispatchDecision` 的业务判断结果,只允许重构其调用方式。
- 不调整 `StationMoveCoordinator` 的会话模型与 Redis 结构。
- 不新增新的运行时服务类,不把逻辑拆出 `StationOperateProcessUtils` 到独立组件。
- 不借本次重构修复潜在业务问题或补充新的兜底分支。
## 3. 现状
当前 `StationOperateProcessUtils` 已超过 2200 行,以下四条链路存在明显重复:
- `checkStationRunBlock`
- `checkStationIdleRecover`
- `checkStationOutOrder`
- `watchCircleStation`
这些方法内部都在做相似的事情:
- 加载 `WrkMast`、`pathLenFactor`、`outOrderList`
- 解析下一目标站
- 判断是否跳过本次重规划
- 生成命令
- 清理旧 session / 旧分段命令 / idle 期间旧下发命令
- 派发命令
- 同步绕圈观察状态
- 记录 `StationMoveCoordinator` 派发会话
但是它们把“扫描候选站点”和“执行重规划”混写在一起,导致:
- 相同保护条件出现在多个入口中,后续变更容易漏改。
- 派发前后的副作用顺序分散在不同方法里,难以确认一致性。
- 运行堵塞、停留超时、排序放行、绕圈监控之间的差异点被淹没在重复流程里。
## 4. 选择方案
采用“统一重规划执行管道”的方案:
- 四个入口方法继续保留,各自只负责扫描符合条件的候选站点。
- 新增一套内部上下文与执行计划模型,把重规划过程统一成固定流程:
  - `loadContext`
  - `resolveDecision`
  - `buildCommandPlan`
  - `executePlan`
  - `applyEffects`
- 场景差异通过内部枚举和上下文字段表达,不通过新建多个对外服务类表达。
不采用“只抽几个私有方法”的方案,因为那样无法真正收口流程顺序,只会把重复代码换一种位置继续存在。
不采用“拆成多个独立 service”的方案,因为当前仓库缺少既有测试护栏,这种拆分会放大变更面,不符合本次“现有行为尽量不变、只重构结构”的目标。
## 5. 设计
### 5.1 总体结构
`StationOperateProcessUtils` 内部新增以下私有结构:
- `RerouteSceneType`
  - 表示四种场景:
    - 运行堵塞重算
    - 停留超时重算
    - 出库排序重算
    - 绕圈监控重算
- `RerouteContext`
  - 收口当前场景执行所需的输入,包括:
    - `BasDevp`
    - `StationThread`
    - `StationProtocol`
    - `WrkMast`
    - `outOrderList`
    - `pathLenFactor`
    - 当前场景类型
    - 是否需要 runBlock 专用命令
    - 是否需要重置分段命令
    - 是否需要清理 idle 期间旧命令
    - 派发场景名与日志上下文
- `RerouteDecision`
  - 表示本次是否继续执行重规划,以及解析得到的目标信息:
    - `targetStationId`
    - 是否绕圈
    - `OutOrderDispatchDecision`
    - `StationTaskLoopService.LoopEvaluation`
    - 是否应记录 loop issue
- `RerouteCommandPlan`
  - 表示命令生成与派发计划:
    - 最终 `StationCommand`
    - 派发前动作
    - 派发后动作
    - 日志内容
这些结构只在 `StationOperateProcessUtils` 内部使用,不新增新的对外接口。
### 5.2 统一主流程
统一后的重规划执行流程固定为五步:
1. `loadContext`
   - 由入口方法完成最小筛选后构造 `RerouteContext`
   - 补齐 `WrkMast`、`pathLenFactor`、`outOrderList`
   - 记录当前场景后续需要的控制标志
2. `resolveDecision`
   - 统一判断本次是否应该继续执行
   - 统一解析下一目标站
   - 统一附带绕圈/loop 信息
   - 对运行堵塞场景保留两条原有路径:
     - 入库重分配库位
     - 普通堵塞重算路线
3. `buildCommandPlan`
   - 根据场景决定命令生成方式:
     - 普通运行堵塞重算继续使用 `getRunBlockRerouteCommand`
     - 其它场景继续使用 `buildOutboundMoveCommand`
   - 收口派发前动作:
     - 是否 `cancelSession`
     - 是否 `resetSegmentMoveCommandsBeforeReroute`
     - 是否清理 idle 期间旧下发命令
4. `executePlan`
   - 统一处理公共保护:
     - 当前 buffer 是否仍含本任务命令
     - idle recover 是否因最近刚派发而跳过
     - 是否需要申请 out-order dispatch lock
     - 是否命中短时去重派发
5. `applyEffects`
   - 统一处理派发成功后的副作用:
     - `syncOutOrderWatchState`
     - `stationMoveCoordinator.recordDispatch`
     - idle track 回写
     - 场景专属日志
### 5.3 四个入口方法的职责边界
四个入口方法保留,但只负责“筛选候选站点”,不再各自拼装完整重规划流程。
#### `checkStationRunBlock`
负责:
- 扫描 `autoing + loading + taskNo > 0 + runBlock = true` 的站点
- 获取任务与 runBlock 锁
- 把候选站点送入统一管道
场景内部保留原有差异:
- 入库且站点位于 `runBlockReassignLocStationList` 时,继续走“重新分配库位并直连派发”分支。
- 其它情况继续走“重新计算路线”分支。
#### `checkStationIdleRecover`
负责:
- 扫描 `autoing + loading + taskNo > 0 + !runBlock` 的站点
- 仅在当前站点已到达当前决策点时进入后续判定
- 把候选站点送入统一管道
保留原行为:
- 使用 `StationTaskIdleTrack`
- 使用“最近刚派发则跳过”的保护
- 派发前清理 idle 期间旧 `BasStationOpt`
#### `checkStationOutOrder`
负责:
- 扫描配置在 `outOrderList` 中、当前停在排序决策点且仍需继续放行的站点
- 把候选站点送入统一管道
保留原行为:
- 使用 `resolveOutboundDispatchDecision`
- 使用 out-order dispatch lock
- 进入公共副作用时同步绕圈观察状态
#### `watchCircleStation`
负责:
- 扫描当前命中 `WATCH_CIRCLE_STATION_` 观察目标的站点
- 只有在真正到达下一决策点时才继续执行
- 把候选站点送入统一管道
保留原行为:
- 继续使用 `resolveOutboundDispatchDecision`
- 继续复用出库排序的派发与观察状态同步逻辑
### 5.4 场景差异的表达方式
统一执行骨架不意味着抹平场景差异,差异只在以下位置保留:
- 决策来源不同:
  - runBlock 入库重分配库位来自 WMS 返回结果
  - 其它场景来自 `resolveOutboundDispatchDecision` 或任务原目标站
- 命令构建方式不同:
  - runBlock 普通重算使用 `getRunBlockRerouteCommand`
  - 其它场景使用 `buildOutboundMoveCommand`
- 派发前动作不同:
  - idle recover 额外清理 idle 期间旧下发命令
  - outOrder / watchCircle 需要 out-order dispatch lock
- 派发后副作用不同:
  - runBlock 入库重分配需要更新库位与任务
  - idle recover 需要回写 `StationTaskIdleTrack`
  - outOrder / watchCircle 需要同步绕圈观察状态
差异只存在于场景策略,不再散落于各入口主流程中。
## 6. 链路检查
### 6.1 输入
- `BasDevp` 中的出库排序站点配置与运行堵塞重分配站点配置
- `StationThread` 当前站点状态
- `StationProtocol` 当前站点任务、堵塞、装载状态
- `WrkMast` 当前任务状态
- `StationMoveCoordinator` 当前会话
- Redis 中的 runBlock / idle / outOrder / watchCircle 辅助状态
### 6.2 处理流程
- 入口方法扫描候选站点
- 构建 `RerouteContext`
- 解析 `RerouteDecision`
- 生成 `RerouteCommandPlan`
- 执行公共保护
- 派发命令
- 应用派发后的场景副作用
### 6.3 状态变化
- 命令派发前的 session 取消、分段命令重置、旧命令清理顺序固定化
- 观察状态与 move session 记录仍在原有 Redis / coordinator 结构中变化
- 不新增新的持久化表与 Redis key 类型
### 6.4 输出
- 成功时输出与当前场景一致的 `StationCommand`
- 保持原有日志语义
- 保持原有 `StationMoveCoordinator` 和 `WATCH_CIRCLE_STATION_` 相关状态推进
### 6.5 上下游影响
- 上游插件调用点不变,仍由 `NormalProcess`、`GslProcess`、`XiaosongProcess`、`FakeProcess` 调用原入口方法
- 下游 `StationThread`、`ZyStationV5Thread`、`MessageQueue` 接口不变
- `StationMoveCoordinator`、`StationTaskLoopService`、`BasStationOptService` 接口不变
## 7. 风险与保护
### 7.1 风险
- 如果重构时把“入口筛选”和“场景执行”边界切错,可能导致某些站点提前进入重规划流程。
- 如果派发前副作用顺序改变,可能引入 session 状态、旧分段命令、idle 清理不一致的问题。
- 如果公共层错误吸收了场景差异,可能改变 runBlock 入库重分配与普通 reroute 的行为。
### 7.2 保护
- 入口扫描条件保持原样,先只做搬迁,不做判定收紧或放宽。
- runBlock 入库重分配场景单独保留,不强行并入普通 reroute 命令生成。
- 派发前后副作用顺序由统一模板固定,但步骤内容沿用现有实现。
- 不改线程侧路径重规划算法,不改 Redis key 命名,不改 WMS 接口交互。
## 8. 验证
当前仓库已确认没有现成 `src/test/java` 与测试依赖,因此验证分为两层:
1. 新增最小测试基建
   - 补充 `spring-boot-starter-test`
   - 为统一重规划执行骨架补定向单测
2. 定向回归验证
   - 运行堵塞普通重算仍使用 `getRunBlockRerouteCommand`
   - idle recover 在“最近刚派发”场景仍会跳过
   - outOrder / watchCircle 进入同一执行骨架,但候选筛选条件不同
   - buffer 中仍有当前任务命令时不会重复派发
   - 派发成功后 `syncOutOrderWatchState` 与 `recordDispatch` 的触发条件保持不变
3. 编译与测试命令
   - `mvn -q -DskipTests compile`
   - 新增的定向测试命令
   - 如环境允许,再执行 `mvn test`
## 9. 实施清单
- 重构 `src/main/java/com/zy/core/utils/StationOperateProcessUtils.java`
- 在类内新增统一重规划上下文、决策、命令计划结构
- 收口 `checkStationRunBlock`
- 收口 `checkStationIdleRecover`
- 收口 `checkStationOutOrder`
- 收口 `watchCircleStation`
- 补充最小测试依赖与定向测试
## 10. 假设与未验证前提
- 用户已明确确认本次以“现有行为尽量不变、只重构结构”为主。
- 用户已明确确认 `checkStationRunBlock`、`checkStationIdleRecover`、`checkStationOutOrder`、`watchCircleStation` 四条链路一起统一。
- 当前未验证所有现场 Redis 状态与历史异常数据分布,设计按现有正常流程组织。
- 当前未验证本地完整测试基建是否已存在,需在实施阶段实际补充并执行后确认。