Files
bj_power/bj_power_mes/docs/superpowers/specs/2026-05-01-system-design-alignment-phase1-design.md
T

436 lines
12 KiB
Markdown
Raw Normal View History

# 2026-05-01 系统设计方案对齐重构(第一阶段)设计
## 背景
本次重构以 `系统设计方案.md` 为准,对当前系统中与文档冲突的实现进行纠偏。目标不是一次完成全部新架构落地,而是在第一阶段优先完成**领域语义统一**,为后续 Redis SSOT、事件重放和三层调度器改造打基础。
用户已确认本次采用**分阶段迁移**,第一阶段范围为:
- 工站域与编排域**并行对齐**
- 允许修改数据库 schema / ent schema
- 当前代码与文档冲突时,**以文档为准**
## 第一阶段目标
第一阶段目标是把系统从“现有实现自定义的一套运行语义”收敛到“系统设计方案.md 定义的语义”,重点完成以下三类一致化:
1. **工站域一致化**:公开 `Station` 契约以文档定义为准,槽位操作下沉为内部实现细节。
2. **编排域一致化**`Job / Task / RecipeStep / Position / Status` 统一按文档命名和流转。
3. **数据模型一致化**:数据库 schema 和 ent schema 改为表达文档定义的运行语义,而不是继续围绕旧执行内核组织。
第一阶段结束后,系统可以继续使用现有执行内核的兼容外壳运行,但代码、接口和数据库都必须已经“说文档的语言”。
## 第一阶段范围
### 包含
- 统一 `JobStatus / TaskStatus / StationStatus / PositionType / StepType` 等枚举与领域类型
- 重构 `internal/station` 的公开接口
- 重构 `internal/processor` 的公开接口与运行态模型
- 重构 `schema/job.go``schema/recipe_step.go`
- 新增 `task``job_step_instance` schema
-`JobProcessor / JobRuntime / Dispatcher` 适配新领域模型继续运行
- 做必要的数据迁移与兼容回填
### 不包含
- Redis 作为运行时单一真相源(SSOT)的完整落地
- Lua 原子版本更新与抢占
- 事件重放能力落地
- 三层调度器完整实现
- PLC 协议与机器人底层通信架构重写
## 模块边界
### station
负责设备抽象、站点状态、命令执行、现场事件接入。
第一阶段中:
- `Station` 对上层暴露能力语义,而不是直接暴露槽位增删改查语义
- 槽位数组、批处理完成态、换料内部逻辑保留为站点内部实现
### processor
负责工单/工件编排、步骤推进、任务生成与状态流转。
第一阶段中:
- `JobProcessor` 改造为编排门面(facade
- `JobRuntime` 继续存在,但降级为“执行会话对象”,不再作为业务语义真相源
- `RobotTask` 拆分为领域 `Task` 和执行对象两个层次
### schema / ent
负责把文档中的运行语义固化为数据库结构,为后续 Redis 和事件模型落地预留字段和引用方式。
## 核心领域模型
### Job
`Job` 是工件运行态核心实体,第一阶段的主字段以文档为准:
- `id`
- `work_order_id`
- `product_type_id`
- `recipe_id`
- `current_step_id`
- `status`
- `position_type`
- `position_ref_id`
- `context`
- `priority`
- `suspended_reason`
- `version`
- `last_event_id`
- `created_time`
- `last_updated`
约束:
- `current_step_id` 取代 `currentStepIndex` 成为主步骤定位字段
- `position_type + position_ref_id` 成为主位置表达
- `version + last_event_id` 在第一阶段先入库,为第二阶段事件一致性铺路
### Job 状态机
对外可见状态与数据库状态统一为:
- `CREATED`
- `IN_HANDLING`
- `PROCESSING`
- `WAITING_UNLOAD`
- `ON_BUFFER`
- `WAITING_DECISION`
- `COMPLETED`
- `SCRAPPED`
- `SUSPENDED`
要求:
- 外部接口、数据库枚举、监控输出只允许出现文档状态
-`JobRuntime` 内部若存在过渡状态,只能作为会话辅助状态,不能暴露为领域状态
### Position
位置统一由以下字段表达:
- `position_type`
- `ON_EQUIPMENT`
- `ON_BUFFER`
- `IN_HAND`
- 迁移期按业务需要兼容 `ON_DOCK``ON_SAMPLING`
- `position_ref_id`
- 指向设备、槽位、接驳台槽位或抽检位等具体位置
约束:
- `tempSlotNo / onMachineId / onSlot / dockNo / slotNo` 可作为迁移兼容字段短期保留
- 新业务判断只能基于 `position_type + position_ref_id`
### Task
Task 作为明确的领域对象存在,至少包含:
- `id`
- `job_id`
- `type`
- `status`
- `from_pos`
- `to_pos`
- `assigned_robot`
- `started_time`
- `completed_time`
- `result`
- 可选增强:`timeout_at``retry_count`
Task 状态机统一为:
- `CREATED`
- `DISPATCHED`
- `RUNNING`
- `SUCCESS`
- `FAILED`
- `TIMEOUT`
要求:
- 领域 Task 可持久化、可查询、可审计
- `RobotTask` 仅保留为执行对象,不再直接承担完整领域语义
### RecipeStep
`recipe_step` 以文档模型为准,重点字段包括:
- `step_id`
- `recipe_id`
- `step_name`
- `step_type`
- `resource_type`
- `tool_type`
- `allowed_resources`
- `processing_params`
- `next_step_default`
- `next_step_branches`
- `step_timeout`
- `description`
约束:
- 步骤跳转以 `step_id` 为主,而不是 `stepIndex`
- `stepIndex` 可保留为排序字段,但不再作为主业务引用
### JobStepInstance
新增 `job_step_instance` 用于记录某个 Job 对某个 Step 的一次执行尝试,包括:
- `job_id`
- `step_id`
- `attempt_no`
- `status`
- `equipment_id`
- `start_time`
- `end_time`
- `result`
作用:
- 支撑返工与恢复
- 提供比 `job.context` 更清晰的步骤履历
- 为第二阶段事件重放与审计提供基础
## 工站接口重构
### 目标公开接口
第一阶段公开 `Station` 契约对齐文档语义:
- `ID() string`
- `Type() string`
- `GetStatus() StationStatus`
- `CanAccept(jobType string) bool`
- `Execute(cmd StationCommand) (assignedSlot string, err error)`
- `OnEvent(event StationEvent)`
### 设计原则
1. `ID` 改为 `string`,统一设备编码表达,避免后续 Redis key 和业务标识反复转换。
2. `CanAccept` 接收 `jobType`,为设备能力约束、机床绑定和工艺过滤预留位置。
3. `Execute` 对外返回 string 型位点引用,便于表达数字槽位、缓冲位、接驳台位等统一位置。
4. 槽位管理保留,但属于站点内部实现,不再作为上层统一契约。
### 分层方式
- **公开领域接口层**:供 processor / scheduler 使用,只表达站点能力、状态和命令语义。
- **内部状态实现层**:保留当前 `BaseStation` 槽位数组、状态迁移、批处理完成与换料逻辑。
### 状态模型纠偏
当前站点状态从旧语义映射到文档语义:
- `PROCESSING``BUSY`
- `DONE``WAITING`
- `ERROR``FAULT`
上层只关心是否可继续编排,不直接依赖内部槽位处于 `DONE` 还是 `OCCUPIED`
### 兼容策略
分两步推进:
1. 保留现有 `BaseStation` 与具体站点实现,新增适配层包装成新 `Station`
2. 逐步移除 processor 对 `OccupySlot / ReleaseSlot / TransitionSlot / FindSlotByJobID` 等旧接口的直接依赖,统一通过 `Execute / OnEvent / GetStatus / CanAccept` 交互。
## 编排接口与运行时改造
### OrderProcessorInterface
第一阶段保留“工单入口”的结构,但语义改写为文档术语优先:
- 工单控制:`Start / Pause / Resume / Cancel / Restore`
- 单工件控制:`SuspendJob / ResumeJob / ReworkJob`
- 监控查询:输出文档定义的状态和值对象,不暴露旧内部状态机语义
### JobProcessor
`JobProcessor` 从“大而全编排器”收缩为第一阶段编排门面:
- 对外仍作为工单编排入口
- 对内协调以下子职责:
- `JobRepository / TaskRepository`
- `RecipeResolver`
- `RuntimeFactory`
- `TaskDispatcher`
- `BufferManager`
- `RecoveryCoordinator`(先留接口)
目标不是立刻实现所有拆分,而是先明确责任边界,避免继续把新旧语义堆叠在一个类型中。
### JobRuntime
`JobRuntime` 继续存在,但降级为“执行会话对象”:
- 持有运行时上下文
- 根据 `RecipeStep` 推进步骤
- 响应 Task 完成/失败
- 触发状态落库
约束:
- 不是业务状态真相源
- 领域状态必须与文档状态一致
- 会话辅助状态仅用于执行期控制,不能直接外露
### Task 的双层模型
第一阶段明确拆分:
- **Task(领域对象)**
- 可持久化、可审计
- 表达业务任务状态与位置迁移
- **TaskExecution / RobotTask(执行对象)**
- 持有 `ActionFn`
- 持有运行时引用
- 由 dispatcher 串行或并发执行
迁移结果:
- 数据库与接口看 Task
- 调度执行层看 RobotTask 或等价执行对象
### 状态推进硬约束
- Job 状态只能走文档允许的迁移
- Task 状态只能走 `CREATED -> DISPATCHED -> RUNNING -> SUCCESS/FAILED/TIMEOUT`
- `current_step_id` 是 Job 前进依据
- `job_step_instance` 记录每次步骤尝试
- 暂存台槽位的占用释放只由 Job 终态驱动,不由工件暂时离开槽位驱动
## 数据库与迁移策略
### 总体策略
采用 **先扩展、再切换、最后清理** 的方式:
1. 先补齐文档要求的新字段/新表
2. 让代码优先读写新语义字段
3. 旧字段仅作为短期兼容
4. 第一阶段稳定后再评估是否删除旧兼容字段
### job 表
#### 保留并强化
- `workOrderId`
- `productTypeId`
- `recipeId`
- `currentStepId`
- `status`
- `positionType`
- `positionRefId`
- `context`
- `priority`
- `suspendedReason`
- `createdTime`
- `lastUpdated`
#### 新增
- `version`
- `lastEventId`
#### 降级为兼容字段
- `currentStepIndex`
- `tempSlotNo`
- `onMachineId`
- `onSlot`
- `dockNo`
- `slotNo`
规则:
- 新代码只从新主字段读
- 旧字段只允许迁移期回填或兼容查询
### recipe_step 表
第一阶段对齐为文档语义:
- `step_id` 作为业务步骤标识
- `next_step_default``next_step_branches` 统一以步骤 ID 表达跳转
- 补齐 `step_timeout`
- 保留 `allowed_resources / processing_params / tool_type / resource_type`
### 新增 task 表
用于持久化 Task 领域对象,支持任务状态查询、超时回退和审计。
### 新增 job_step_instance 表
用于记录 Job 的步骤执行尝试明细,减少上下文字段滥用,支撑返工和恢复。
### 切换步骤
1. 修改 ent schema
2. 重新生成 ent 代码
3. 改 repository / query 层兼容新旧字段
4. 改 processor 使用新字段
5. 编写一次性数据迁移逻辑:
- `currentStepIndex -> currentStepId`
- 旧位置字段 -> `positionType + positionRefId`
6. 稳定后再决定是否移除旧字段
## 实施顺序
建议按以下 6 步推进:
1. **统一常量与领域类型**
- 先统一 `JobStatus / TaskStatus / StationStatus / PositionType / StepType`
2. **改 schema 与 ent**
- 重构 `job``recipe_step`
- 新增 `task``job_step_instance`
3. **建立过渡层**
- 收口新旧字段映射
- 不让映射逻辑散落在业务代码中
4. **重构 station 公共接口**
- 先引入新契约与适配层
5. **重构 processor 编排入口**
- `JobProcessor` 改为门面
- `JobRuntime` 改为执行会话
- `RobotTask` 拆为领域 Task 与执行对象
6. **做数据迁移与回填**
- 验证历史数据可被新逻辑正确读取
## 回归验证
第一阶段至少验证以下路径:
1. **工单启动**
- 能正确加载 recipe、创建 Job 运行态、进入首个文档定义状态
2. **扫码入暂存台**
- 成功进入 `ON_BUFFER`
- 失败进入 `SUSPENDED`
3. **机加工到下料**
- `PROCESSING -> WAITING_UNLOAD -> IN_HANDLING`
4. **终态释放**
- `COMPLETED / SCRAPPED` 时释放暂存位
5. **任务生命周期**
- `CREATED -> DISPATCHED -> RUNNING -> SUCCESS/FAILED/TIMEOUT`
6. **恢复 / 返工接口语义**
- 即使第二阶段完整恢复体系未落地,接口语义也必须已与文档一致
## 风险控制
### 风险 1:新旧状态并存
- 解决:对外只暴露新状态,旧状态只允许存在于适配层内部
### 风险 2:位置语义混乱
- 解决:业务判断统一只认 `positionType + positionRefId`
### 风险 3:步骤跳转仍依赖 index
- 解决:尽快把主流转切到 `step_id``stepIndex` 只保留排序用途
### 风险 4:上层仍直接操纵工站槽位
- 解决:逐步封口,只允许通过 `Execute / OnEvent / GetStatus / CanAccept` 交互
## 第一阶段完成标准
第一阶段完成,不以 Redis 已接入为标准,而以下列结果为准:
- 代码、数据库、接口都使用文档定义的术语
- station 与 processor 不再存在一套公开语义、一套内部语义互相冲突的问题
- 旧执行内核仍可运行,但已运行在新领域模型之上
- 第二阶段可以直接接 Redis SSOT、三层调度和事件重放,而不需要再先做一轮术语翻译