# job.current_step_id 设计 ## 目标 `job.currentStepIndex` (int) → `job.currentStepId` (string),以 stepId 为 job 当前步骤的运行时标识,消除 `AdvanceStep` 中 stepId↔stepIndex 的来回转换。 ## 动机 `recipe_step` 已采用 stepId 为主的设计:`nextStepDefault` 和 `nextStepBranches` 存储的都是 stepId(如 "OP20"),但 `job` 表仍用 `currentStepIndex` (int)。导致 `AdvanceStep` 中存在冗余转换: ``` nextStepDefault(stepId) → 二次查表转 stepIndex → 写 currentStepIndex resolveBranch(stepId) → GetStepIndexByStepID → 写 currentStepIndex ``` 同时 stepIndex 不稳定:工序重排时 index 会变,运行中的 job 会指错步骤。stepId(OP10-OP120)是稳定的业务标识。 ## 设计 ### Schema job 表 (`schema/job.go`): - 新增 `field.String("currentStepId").MaxLen(20).Default("")`,替代 `currentStepIndex` 作为运行时步骤标识 - 旧字段 `currentStepIndex` 暂保留但不写入(默认 0),给前端平滑过渡窗口 - 新字段不需要额外索引(仅作为数据列读取,不作为查询条件) ### stepId 排序 12 个步骤加载到内存按 stepIndex 排序(数量少,开销可忽略),必要时用辅助函数提取 stepId 中的数字: ```go func sortedStepIDs(ctx, recipeID) []string { // SELECT stepId FROM recipe_step WHERE recipeId=? ORDER BY stepIndex } ``` ### AdvanceStep 简化 **旧流程**: ``` nextStepDefault(stepId) → 二次查表转 stepIndex → 写 currentStepIndex resolveBranch(stepId) → GetStepIndexByStepID → 写 currentStepIndex 线性推进 → currentStepIndex + 1 ``` **新流程**: ``` nextStepDefault → 本身就是 stepId → 直接写 currentStepId resolveBranch → 本身就是 stepId → 直接写 currentStepId 线性推进 → 当前 stepId 的 stepIndex + 1 → 按 stepIndex 找回 stepId ``` "是否最后一步":取 currentStepId 对应的 stepIndex 与 maxStepIndex 比较。 ### DBState 方法调整 需修改的方法: - `AdvanceStep` (`dbstate.go:162`) — `SetCurrentStepIndex` → `SetCurrentStepId`,`resolveBranch` 传 stepId - `CompleteStep` (`dbstate.go:34`) — 同上 | 旧方法 | 新方法 | 说明 | |--------|--------|------| | `GetRecipeStep(recipeID, stepIndex)` | 保留,Generator 遍历用 | 按 stepIndex 依次取 | | — | `GetRecipeStepByID(recipeID, stepID)` | 核心读路径用 stepId | | `GetNextStepIndex` | 废弃 | nextStepDefault 即 stepId | | `resolveBranch(recipeID, stepIndex, ctxMap)` | `resolveBranch(recipeID, stepID, ctxMap)` | 分支目标即 stepId | | `GetStepIndexByStepID` | 保留 | 排序/比较 | | — | `GetStepIDByIndex(recipeID, stepIndex)` | 线性推进用 | | — | `getSortedStepIDs(recipeID)` | 加载全步骤排序 | ### 对外类型 ```go type JobView struct { CurrentStepIndex int `json:"currentStepIndex"` // deprecated CurrentStepId string `json:"currentStepId"` } ``` `RuntimeSnapshot` 同理加 `StepID`,`StepIndex` 保留。 ### 消费方改动 | 位置 | 改动 | |------|------| | `internal/eventloop/dbstate.go` | `AdvanceStep` + `CompleteStep`:`SetCurrentStepIndex` → `SetCurrentStepId` | | `internal/eventloop/scheduler_bridge.go` | `GetRecipeStep(stepIndex)` → `GetRecipeStepByID(stepId)` | | `internal/eventloop/candidate_handlers.go` | 同上 | | `internal/eventloop/loop.go` | `RuntimeSnapshot` 加 `StepID` 字段 | | `internal/scheduler/generator.go` | 遍历全步骤用 stepIndex(不变),候选任务用 stepId | | `internal/processor/job_processor.go:254` | `SetCurrentStepIndex` → `SetCurrentStepId`(恢复/跳步场景) | | `internal/processor/recipe_runtime.go` | `GetNextStepIndex` 标记废弃(processor 层作为兜底) | | `internal/types/types.go` | `JobView`/`RuntimeSnapshot`/API 响应类型加 `CurrentStepId` | | `cmd/mockrun/main.go:110` | `SetCurrentStepIndex` → `SetCurrentStepId` | | `frontend/src/api/temp-station/index.ts:9` | 类型加 `currentStepId` | | `frontend/src/api/dock/index.ts:9` | 类型加 `currentStepId` | ### 迁移 ```sql UPDATE job SET currentStepId = rs.stepId FROM recipe_step rs WHERE job.recipeId = rs.recipeId AND job.currentStepIndex = rs.stepIndex; ``` 迁移脚本放入 `schema/tools/`,在 `EnsureDataInitialized` 中执行。