497 lines
19 KiB
Markdown
497 lines
19 KiB
Markdown
# 系统设计方案第一阶段对齐重构 Implementation Plan
|
|||
|
|
|
||
|
|
> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||
|
|
|
||
|
|
**Goal:** 让工站域、编排域和数据库 schema 全部对齐 `系统设计方案.md` 的第一阶段语义,并保持现有执行内核可继续运行。
|
||
|
|
|
||
|
|
**Architecture:** 先统一领域常量和 ent schema,再用适配层收口 `station` 与 `processor` 的公开语义,最后通过迁移脚本和回归测试完成旧字段到新模型的平滑切换。第一阶段不接入 Redis SSOT、不实现事件重放,也不完整落地三层调度器,只为这些能力预埋模型与边界。
|
||
|
|
|
||
|
|
**Tech Stack:** Go 1.23.4, go-zero, ent v0.14.5, PostgreSQL, testify
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 文件结构
|
||
|
|
|
||
|
|
### 必改文件
|
||
|
|
- Modify: `constants/constants.go` — 统一第一阶段使用的 `StationStatus`、`PositionType`、必要的 `TaskStatus` 与补充步骤类型。
|
||
|
|
- Modify: `schema/job.go` — `job` 主模型对齐文档,新增 `version`、`lastEventId`,降级旧位置字段为兼容字段。
|
||
|
|
- Modify: `schema/recipe_step.go` — `recipe_step` 从基于 index 的流转改为以 `step_id` 为主。
|
||
|
|
- Modify: `schema/gen.go` — 仅确认生成入口,无语义改动。
|
||
|
|
- Modify: `internal/station/interface.go` — 公共 `Station` 契约切到文档语义。
|
||
|
|
- Modify: `internal/station/base.go` — 保留槽位内部实现,但不再作为上层公共 API 依赖。
|
||
|
|
- Modify: `internal/station/registry.go` — Registry 改为 string ID / 新接口语义。
|
||
|
|
- Modify: `internal/station/cnc.go`
|
||
|
|
- Modify: `internal/station/washer.go`
|
||
|
|
- Modify: `internal/station/inspection.go`
|
||
|
|
- Modify: `internal/station/deburr.go`
|
||
|
|
- Modify: `internal/station/cleaning.go`
|
||
|
|
- Modify: `internal/station/sampling.go`
|
||
|
|
- Modify: `internal/station/dock.go` — 具体站点适配新接口。
|
||
|
|
- Modify: `internal/processor/interface.go` — 顶层编排接口输出新领域状态。
|
||
|
|
- Modify: `internal/processor/task.go` — 拆分领域 Task 与执行对象。
|
||
|
|
- Modify: `internal/processor/job_runtime.go` — 运行时状态与 DB 状态统一到文档语义。
|
||
|
|
- Modify: `internal/processor/job_processor.go` — 改造成第一阶段编排门面。
|
||
|
|
- Modify: `internal/processor/dispatcher.go` — 消费新的 TaskExecution 结构。
|
||
|
|
- Modify: `internal/processor/recipe_loader.go` — 读取新 `recipe_step` 字段与 `step_id` 语义。
|
||
|
|
- Modify: `internal/svc/service_context.go` — 注入与恢复逻辑适配新接口。
|
||
|
|
|
||
|
|
### 新建文件
|
||
|
|
- Create: `schema/task.go` — Task 领域持久化模型。
|
||
|
|
- Create: `schema/job_step_instance.go` — Job 步骤尝试履历模型。
|
||
|
|
- Create: `internal/station/adapter.go` — 旧槽位站点到新 `Station` 语义的适配层。
|
||
|
|
- Create: `internal/processor/task_model.go` — 领域 Task、位置值对象、TaskStatus。
|
||
|
|
- Create: `internal/processor/job_runtime_status.go` — `JobRuntime` 内部状态与文档状态映射收口。
|
||
|
|
- Create: `internal/processor/repositories.go` — 新旧字段映射与 repository 收口点。
|
||
|
|
- Create: `internal/station/interface_test.go` — 新接口语义测试。
|
||
|
|
- Create: `internal/station/registry_test.go` — string ID registry 测试。
|
||
|
|
- Create: `internal/processor/task_test.go` — 任务状态机与领域/执行对象分离测试。
|
||
|
|
|
||
|
|
### 现有测试要改
|
||
|
|
- Modify: `internal/processor/job_runtime_test.go`
|
||
|
|
- Modify: `internal/processor/temp_slot_allocator_test.go` — 仅保留与“终态释放槽位”一致的断言。
|
||
|
|
- Modify: `internal/processor/replenisher_test.go` — 如受位置语义影响则同步更新。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Chunk 1: 常量与 Schema 对齐
|
||
|
|
|
||
|
|
### Task 1: 统一领域常量与状态枚举
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `constants/constants.go`
|
||
|
|
- Test: `internal/processor/job_runtime_test.go`
|
||
|
|
- Test: `internal/station/interface_test.go`
|
||
|
|
|
||
|
|
**Depends on:** 无
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,锁定新状态语义**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestStationStatusValuesMatchPhase1Design(t *testing.T) {
|
||
|
|
assert.Equal(t, []string{"IDLE", "BUSY", "WAITING", "FAULT"}, constants.StationStatus("").Values())
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestPositionTypeValuesIncludeDockAndSamplingCompatibility(t *testing.T) {
|
||
|
|
values := constants.PositionType("").Values()
|
||
|
|
assert.Contains(t, values, "ON_EQUIPMENT")
|
||
|
|
assert.Contains(t, values, "ON_BUFFER")
|
||
|
|
assert.Contains(t, values, "IN_HAND")
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/... ./internal/station/...`
|
||
|
|
Expected: FAIL,提示 `StationStatus` 仍为 `PROCESSING/DONE/ERROR` 或缺少新枚举。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 最小化修改常量定义**
|
||
|
|
- 将 `StationStatus` 改为 `IDLE/BUSY/WAITING/FAULT`
|
||
|
|
- 新增 `TaskStatus` 枚举
|
||
|
|
- 如需要,补充 `PositionType_OnDock`、`PositionType_OnSampling` 作为兼容值
|
||
|
|
- 不修改与本阶段无关的常量
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行测试确认通过**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/... ./internal/station/...`
|
||
|
|
Expected: PASS 或仅剩 schema/编译相关失败。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 记录检查点**
|
||
|
|
- 非 git 仓库,跳过 commit
|
||
|
|
- 在执行日志中记录“常量层已切到文档语义”
|
||
|
|
|
||
|
|
### Task 2: 重构 `job`、`recipe_step` 并新增 `task` / `job_step_instance`
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `schema/job.go`
|
||
|
|
- Modify: `schema/recipe_step.go`
|
||
|
|
- Create: `schema/task.go`
|
||
|
|
- Create: `schema/job_step_instance.go`
|
||
|
|
- Modify: `schema/gen.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 1
|
||
|
|
|
||
|
|
- [ ] **Step 1: 先写 schema 层断言测试或编译断言**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestJobStatusDefaultsMatchPhase1Design(t *testing.T) {
|
||
|
|
assert.Equal(t, string(constants.JobStatus_Created), string(constants.JobStatus_Created))
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
说明:ent schema 当前缺少直接单测入口,这一步以最小编译断言 + 生成后编译为主。
|
||
|
|
|
||
|
|
- [ ] **Step 2: 修改 `schema/job.go`**
|
||
|
|
- 保留:`workOrderId/productTypeId/recipeId/currentStepId/status/positionType/positionRefId/context/priority/suspendedReason/createdTime/lastUpdated`
|
||
|
|
- 新增:`version`、`lastEventId`
|
||
|
|
- 旧字段 `currentStepIndex/tempSlotNo/onMachineId/onSlot/dockNo/slotNo` 保留但标注兼容用途
|
||
|
|
|
||
|
|
- [ ] **Step 3: 修改 `schema/recipe_step.go`**
|
||
|
|
- 引入 `stepId` 作为业务步骤标识
|
||
|
|
- `nextStepDefault`、`nextStepBranches` 改为表达 step id
|
||
|
|
- 新增 `stepTimeout`
|
||
|
|
- 保留 `stepIndex` 仅用于排序
|
||
|
|
|
||
|
|
- [ ] **Step 4: 新增 `schema/task.go` 与 `schema/job_step_instance.go`**
|
||
|
|
- `task` 包含 `id/jobId/type/status/fromPos/toPos/assignedRobot/startedTime/completedTime/result`
|
||
|
|
- `job_step_instance` 包含 `jobId/stepId/attemptNo/status/equipmentId/startTime/endTime/result`
|
||
|
|
|
||
|
|
- [ ] **Step 5: 生成 ent 代码**
|
||
|
|
|
||
|
|
Run: `cd schema && rtk go generate`
|
||
|
|
Expected: `ent code generated successfully!`
|
||
|
|
|
||
|
|
- [ ] **Step 6: 编译确认新 schema 可生成**
|
||
|
|
|
||
|
|
Run: `rtk go test ./schema/...`
|
||
|
|
Expected: PASS 或输出“[no test files]”,但不能有 ent 生成/编译错误。
|
||
|
|
|
||
|
|
- [ ] **Step 7: 记录检查点**
|
||
|
|
- 标记“schema 已能表达第一阶段语义”
|
||
|
|
|
||
|
|
### Task 3: 收口旧字段到新字段的迁移入口
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Create: `internal/processor/repositories.go`
|
||
|
|
- Modify: `internal/processor/recipe_loader.go`
|
||
|
|
- Test: `internal/processor/task_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 2
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,锁定旧字段到新模型的映射**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestBuildPositionFromLegacyFieldsPrefersNewFields(t *testing.T) {
|
||
|
|
job := legacyJobFixture()
|
||
|
|
pos := buildPosition(job)
|
||
|
|
assert.Equal(t, "ON_BUFFER", pos.Type)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行单测确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: FAIL,提示缺少 `buildPosition` 或仍直接依赖旧字段。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 新建 `repositories.go`**
|
||
|
|
- 提供从 ent.Job 到新领域 `JobSnapshot` / `PositionRef` / `TaskRecord` 的转换
|
||
|
|
- 把 `currentStepIndex -> currentStepId`、旧位置字段 -> `positionType + positionRefId` 的兼容逻辑集中放这里
|
||
|
|
- `recipe_loader.go` 改为优先使用 `stepId`
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 processor 测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: 旧失败减少,映射逻辑测试通过。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 记录检查点**
|
||
|
|
- 标记“新旧字段映射已集中,不允许继续散落在业务代码里”
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Chunk 2: 工站接口对齐
|
||
|
|
|
||
|
|
### Task 4: 切换 `Station` 公开接口到文档语义
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `internal/station/interface.go`
|
||
|
|
- Create: `internal/station/adapter.go`
|
||
|
|
- Create: `internal/station/interface_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Chunk 1 完成
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,锁定新接口签名与行为**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestStationAdapterExposesDocumentInterface(t *testing.T) {
|
||
|
|
st := newFakeStationAdapter()
|
||
|
|
assert.Equal(t, "CNC_1", st.ID())
|
||
|
|
assert.Equal(t, constants.StationStatus_Idle, st.GetStatus())
|
||
|
|
assert.True(t, st.CanAccept("A6VM160"))
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行 station 测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/station/...`
|
||
|
|
Expected: FAIL,提示 `ID()` 返回 int、`GetStatus()` 签名不匹配或 `CanAccept` 缺参。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 修改 `interface.go` 并新增 `adapter.go`**
|
||
|
|
- `Station` 改为 `ID() string`、`GetStatus() constants.StationStatus`、`CanAccept(jobType string) bool`、`Execute(cmd StationCommand) (string, error)`
|
||
|
|
- 保留 `StationCommand` / `StationEvent`,但字段值语义切到文档语言
|
||
|
|
- 用适配层包住旧 `BaseStation` 的槽位实现
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 station 测试确认通过**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/station/...`
|
||
|
|
Expected: PASS 或仅剩 registry/具体站点适配失败。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 记录检查点**
|
||
|
|
- 标记“公开工站语义已与文档一致,槽位只留在内部实现”
|
||
|
|
|
||
|
|
### Task 5: 调整 Registry 与具体站点实现
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `internal/station/registry.go`
|
||
|
|
- Modify: `internal/station/base.go`
|
||
|
|
- Modify: `internal/station/cnc.go`
|
||
|
|
- Modify: `internal/station/washer.go`
|
||
|
|
- Modify: `internal/station/inspection.go`
|
||
|
|
- Modify: `internal/station/deburr.go`
|
||
|
|
- Modify: `internal/station/cleaning.go`
|
||
|
|
- Modify: `internal/station/sampling.go`
|
||
|
|
- Modify: `internal/station/dock.go`
|
||
|
|
- Create: `internal/station/registry_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 4
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,锁定 string ID registry 行为**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestRegistryGetByStringID(t *testing.T) {
|
||
|
|
reg := NewStationRegistry()
|
||
|
|
reg.Register(newFakeStation("CNC_1", "CNC"))
|
||
|
|
st, ok := reg.Get("CNC_1")
|
||
|
|
assert.True(t, ok)
|
||
|
|
assert.Equal(t, "CNC", st.Type())
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/station/...`
|
||
|
|
Expected: FAIL,提示 registry 仍使用 `map[int]Station`。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 改造 registry 和具体站点**
|
||
|
|
- `StationRegistry` 改为 `map[string]Station`
|
||
|
|
- `BuildProductionLine` 统一产出 `CNC_1`、`CNC_2` 等 string 标识
|
||
|
|
- `BaseStation` 内部继续维护槽位数组,但对外状态映射为 `IDLE/BUSY/WAITING/FAULT`
|
||
|
|
- 具体站点的 `Execute` 返回 string 型位点引用
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 station 测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/station/...`
|
||
|
|
Expected: PASS。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 运行依赖 station 的基础编译测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/svc/... ./internal/processor/...`
|
||
|
|
Expected: 可能有 processor 适配失败,但 station 引起的签名错误应明显减少。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Chunk 3: 编排模型与执行内核适配
|
||
|
|
|
||
|
|
### Task 6: 引入领域 Task 模型并拆分执行对象
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Create: `internal/processor/task_model.go`
|
||
|
|
- Modify: `internal/processor/task.go`
|
||
|
|
- Create: `internal/processor/task_test.go`
|
||
|
|
- Modify: `internal/processor/dispatcher.go`
|
||
|
|
|
||
|
|
**Depends on:** Chunk 2 完成
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,锁定 Task 状态机**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestTaskStatusFlow(t *testing.T) {
|
||
|
|
task := NewTaskRecord("task-1", 1001)
|
||
|
|
task.MarkDispatched()
|
||
|
|
task.MarkRunning()
|
||
|
|
task.MarkSuccess(map[string]any{"slot": "BUFFER_1"})
|
||
|
|
assert.Equal(t, TaskStatusSuccess, task.Status)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: FAIL,提示缺少领域 Task 结构或 `RobotTask` 混合职责。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 新建 `task_model.go` 并改 `task.go`**
|
||
|
|
- 在 `task_model.go` 定义 `TaskRecord`、`PositionRef`、`TaskStatus`
|
||
|
|
- `task.go` 保留 `RobotTask` / `TaskExecution`,只持有执行闭包与运行时引用
|
||
|
|
- `dispatcher.go` 改为消费执行对象,同时在回调中推进领域 Task 状态
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 processor 测试确认通过**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: Task 状态机测试通过。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 记录检查点**
|
||
|
|
- 标记“数据库/接口看 Task,dispatcher 看执行对象”
|
||
|
|
|
||
|
|
### Task 7: 统一 `JobRuntime` 的领域状态与 DB 状态
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Create: `internal/processor/job_runtime_status.go`
|
||
|
|
- Modify: `internal/processor/job_runtime.go`
|
||
|
|
- Modify: `internal/processor/job_runtime_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 6
|
||
|
|
|
||
|
|
- [ ] **Step 1: 改写失败测试为文档状态**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestRuntimeStateToDomainStatus(t *testing.T) {
|
||
|
|
assert.Equal(t, constants.JobStatus_OnBuffer, runtimeStateToDomainStatus(RuntimeStateOnBuffer))
|
||
|
|
assert.Equal(t, constants.JobStatus_WaitingUnload, runtimeStateToDomainStatus(RuntimeStateWaitingUnload))
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: FAIL,提示仍存在 `Running/Paused/Error` 这套旧状态映射。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 新建 `job_runtime_status.go` 并修改 `job_runtime.go`**
|
||
|
|
- 明确定义运行时内部状态与文档领域状态映射
|
||
|
|
- `Pause/Resume/Suspend/Cancel` 全部改为写文档状态
|
||
|
|
- 暂存位释放规则仅在 `COMPLETED/SCRAPPED` 时触发
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 processor 测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: 现有 `job_runtime_test.go` 与新状态测试通过。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 记录检查点**
|
||
|
|
- 标记“JobRuntime 已降级为执行会话对象,不再自带公开业务世界观”
|
||
|
|
|
||
|
|
### Task 8: 将 `JobProcessor` 改为第一阶段编排门面
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `internal/processor/interface.go`
|
||
|
|
- Modify: `internal/processor/job_processor.go`
|
||
|
|
- Modify: `internal/svc/service_context.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 7
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试或编译断言,锁定新编排输出语义**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestOrderProcessorUsesDocumentStatusVocabulary(t *testing.T) {
|
||
|
|
// 编译期约束:接口返回值与监控结构只能使用文档状态
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行 processor / svc 测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/... ./internal/svc/...`
|
||
|
|
Expected: FAIL,提示 `StationRegistry.Get`、恢复逻辑、状态输出仍依赖旧接口。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 修改 `interface.go`、`job_processor.go`、`service_context.go`**
|
||
|
|
- `OrderProcessorInterface` 对外只暴露文档语义
|
||
|
|
- `JobProcessor` 内部用 repository、recipe resolver、runtime factory 的方式收口职责
|
||
|
|
- `restoreActiveOrders()` 保持现有入口,但恢复读取新 `currentStepId/position` 模型
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行核心测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/... ./internal/svc/...`
|
||
|
|
Expected: PASS 或仅剩数据迁移/fixture 不一致问题。
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Chunk 4: 数据迁移、回归验证与收尾
|
||
|
|
|
||
|
|
### Task 9: 实现一次性数据回填与兼容读取验证
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `internal/processor/repositories.go`
|
||
|
|
- Modify: `internal/processor/recipe_loader.go`
|
||
|
|
- Test: `internal/processor/task_test.go`
|
||
|
|
- Test: `internal/processor/replenisher_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Chunk 3 完成
|
||
|
|
|
||
|
|
- [ ] **Step 1: 写失败测试,覆盖旧数据兼容读取**
|
||
|
|
|
||
|
|
```go
|
||
|
|
func TestLegacyJobFieldsBackfillToCurrentStepID(t *testing.T) {
|
||
|
|
snap := buildJobSnapshot(legacyJobFixture())
|
||
|
|
assert.Equal(t, "30", snap.CurrentStepID)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行测试确认失败**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: FAIL,提示仍依赖 `currentStepIndex` 或旧位置字段。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 实现回填与兼容读取**
|
||
|
|
- 优先读取 `currentStepId/positionType/positionRefId`
|
||
|
|
- 仅当新字段为空时回退旧字段
|
||
|
|
- 将回填逻辑收口到 repository / loader,不让上层分散判断
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行 processor 测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/processor/...`
|
||
|
|
Expected: PASS。
|
||
|
|
|
||
|
|
### Task 10: 完成端到端回归验证
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `internal/processor/job_runtime_test.go`
|
||
|
|
- Modify: `internal/processor/replenisher_test.go`
|
||
|
|
- Modify: `internal/processor/temp_slot_allocator_test.go`
|
||
|
|
- Test: `internal/station/interface_test.go`
|
||
|
|
- Test: `internal/station/registry_test.go`
|
||
|
|
|
||
|
|
**Depends on:** Task 9
|
||
|
|
|
||
|
|
- [ ] **Step 1: 补充关键路径测试**
|
||
|
|
- 工单启动进入首个文档状态
|
||
|
|
- 扫码成功进入 `ON_BUFFER`
|
||
|
|
- 扫码失败进入 `SUSPENDED`
|
||
|
|
- 加工完成进入 `WAITING_UNLOAD`
|
||
|
|
- `COMPLETED/SCRAPPED` 释放暂存位
|
||
|
|
- Task 生命周期完整走通
|
||
|
|
|
||
|
|
- [ ] **Step 2: 运行定向测试**
|
||
|
|
|
||
|
|
Run: `rtk go test ./internal/station/... ./internal/processor/...`
|
||
|
|
Expected: PASS。
|
||
|
|
|
||
|
|
- [ ] **Step 3: 运行更大范围回归**
|
||
|
|
|
||
|
|
Run: `rtk go test ./...`
|
||
|
|
Expected: PASS;如果现有无关模块失败,需记录为已有问题并隔离确认不由本计划引入。
|
||
|
|
|
||
|
|
- [ ] **Step 4: 运行覆盖率检查**
|
||
|
|
|
||
|
|
Run: `rtk go test -cover ./internal/processor/... ./internal/station/...`
|
||
|
|
Expected: 输出覆盖率,新增逻辑路径有明确测试覆盖。
|
||
|
|
|
||
|
|
- [ ] **Step 5: 人工验收清单**
|
||
|
|
- `Station` 对外不再暴露槽位操作 API
|
||
|
|
- `Job`/`Task`/`RecipeStep` 术语与文档一致
|
||
|
|
- `JobProcessor` 仍可作为运行入口
|
||
|
|
- 没有引入 Redis SSOT、事件重放或三层调度的半成品实现
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## 风险点
|
||
|
|
|
||
|
|
1. **`StationRegistry` 从 int ID 切到 string ID 会波及 processor 与恢复逻辑**
|
||
|
|
- 处理:先落适配层,再统一替换调用点。
|
||
|
|
2. **`currentStepIndex` 到 `currentStepId` 的切换容易引入 recipe 跳转错误**
|
||
|
|
- 处理:在 `recipe_loader` 中集中转换,先保留 `stepIndex` 做排序。
|
||
|
|
3. **`RobotTask` 与领域 Task 拆分后可能出现状态不同步**
|
||
|
|
- 处理:所有状态推进统一经由 `TaskRecord` 方法,不允许 dispatcher 直接写裸字段。
|
||
|
|
4. **终态释放暂存位的规则可能和现有补料测试冲突**
|
||
|
|
- 处理:先改测试,确保只有 `COMPLETED/SCRAPPED` 释放。
|
||
|
|
|
||
|
|
## 验证点
|
||
|
|
|
||
|
|
- `internal/station/...` 全量测试通过
|
||
|
|
- `internal/processor/...` 全量测试通过
|
||
|
|
- `cd schema && rtk go generate` 成功
|
||
|
|
- `rtk go test ./...` 无新增回归
|
||
|
|
- 关键状态词只出现文档语义:`IDLE/BUSY/WAITING/FAULT`、`CREATED/IN_HANDLING/...`
|
||
|
|
|
||
|
|
## 执行说明
|
||
|
|
|
||
|
|
- 当前目录不是 git 仓库,跳过“频繁 commit”步骤,改为每个任务结束记录检查点。
|
||
|
|
- 当前环境无法使用基于 worktree 的审阅代理;执行时如需审阅,采用当前会话内人工检查或普通子代理替代。
|
||
|
|
- 严格遵守项目规则:只改 `schema/*.go` 后再生成 ent;不要手改 `ent/`。
|
||
|
|
|
||
|
|
Plan complete and saved to `docs/superpowers/plans/2026-05-01-system-design-alignment-phase1.md`. Ready to execute?
|