docs(M-AI-02-GATE): update ADR-002 lifecycle spec + @map strategy
Some checks failed
Deploy API Server / build-and-deploy (push) Failing after 24s

- lifecycleStatus enum: created|queued|running|retrying|succeeded|failed|cancel_requested|cancelled
- Legacy mapping: pending→queued, processing→running, completed→succeeded, failed→failed
- @map approach documented for physical column compatibility

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
wangdl 2026-06-20 12:40:26 +08:00
parent 98e442e666
commit 4c2b8d02d7

View File

@ -31,6 +31,7 @@ M-AI-02-01 审计确认:
| AiRuntimeJob 表 | **不删除、不修改、不迁移** | 0 行生产数据M-AI-08 退场时一并清理 |
| AiRuntimeResult 表 | **不修改** | M-AI-03 统一 Job Engine 后用新的 Result 模型 |
| AiAnalysisResult 表 | **不修改** | 保留旧系统 A 的结果存储M-AI-04 迁移后评估删除 |
| 列重命名策略 | **Prisma 逻辑名用新名,物理列用 `@map` 保持旧名** | `internalErrorMessage @map("errorMessage")``finishedAt @map("completedAt")`。旧版本应用通过物理列名兼容,新代码通过逻辑名访问 |
### 2. 统一 AiJob Schema
@ -188,30 +189,42 @@ model AiJob {
| `sessionId` | `sessionId` | VARCHAR(191) | ✅ | - | 100% (9/9) | 保留但废弃(旧系统 AM-AI-05 删除 |
| `answerId` | `answerId` | VARCHAR(191) | ✅ | - | 78% (7/9) | 保留但废弃(旧系统 AM-AI-05 删除 |
### 3. 状态映射表
### 3. 统一生命周期状态
两套旧状态统一为单一 `lifecycleStatus`
新 lifecycleStatus 枚举8 个值)
| 旧 AiAnalysisJob.status | 旧 AiRuntimeJob.status | 新 lifecycleStatus | 含义 | 进入条件 | 退出条件 |
|------------------------|----------------------|-------------------|------|---------|---------|
| `pending` | `pending` | **`pending`** | 等待消费 | API 创建 / retryable fail 回退 | 被 Runtime lock / BullMQ claim |
| - | `locked` | **`locked`** | 已被 Runtime 获取,等待执行确认 | Runtime POST /lock 成功 | heartbeat → running / lockUntil 超时 |
| `processing` | `running` | **`running`** | 正在执行 | heartbeat 确认(新)/ Worker process() 开始(旧) | 完成 → succeeded / 失败 → failed |
| `completed` | `succeeded` | **`succeeded`** | 执行成功 | API POST /result | 终态 |
| `failed` | `failed` | **`failed`** | 执行失败 | 不可重试错误 / 超过 maxAttempts | 终态Admin 可重跑) |
| - | `cancelled` | **`cancelled`** | 已取消 | 用户/Admin 取消 pending job / cancelRequested 后 Runtime 确认 | 终态 |
| - | `expired` | **取消**(合并入 retry 逻辑) | - | lockUntil 超时 → back to `pending`Reaper 处理) | - |
| 状态 | 含义 |
|------|------|
| `created` | Job 记录已创建,尚未入队 |
| `queued` | 已入队,等待 Worker 消费 |
| `running` | Worker 正在执行 |
| `retrying` | 执行失败,等待重试 |
| `succeeded` | 执行成功(终态) |
| `failed` | 执行失败,超过最大重试(终态) |
| `cancel_requested` | 用户/Admin 请求取消,等待 Worker 确认 |
| `cancelled` | 已取消(终态) |
> **为什么 `processing` → `running` 而非 `locked`**:旧系统的 `processing` 表示 Worker 正在执行(`ai-analysis.worker.ts:48`),语义等同于新系统 heartbeat 确认后的 `running`。新系统的 `locked` 是「已被 claim 但尚未确认执行开始」的中间态,在旧系统中由 BullMQ 内部管理,无显式状态对应。
>
> **Reaper 安全影响**:若映射为 `locked`JobReaperService 会将旧 Worker 正在执行的任务视为「无 heartbeat 的 locked job」而在 `lockUntil` 过期后错误清扫回 `pending`,导致重复执行。映射为 `running` 可避免此问题Reaper 对 `running` 的清扫阈值远长于 `locked`)。
**Legacy → 统一映射**Shadow Write + 回填):
| 旧 AiAnalysisJob.status | 旧 AiRuntimeJob.status | 新 lifecycleStatus |
|------------------------|----------------------|-------------------|
| `pending` | `pending` | **`queued`** |
| `processing` | `running` | **`running`** |
| `completed` | `succeeded` | **`succeeded`** |
| `failed` | `failed` | **`failed`** |
| - | `locked` | N/A新系统内部状态旧系统无对应 |
| - | `cancelled` | **`cancelled`** |
> **为什么 `pending` → `queued`**:旧系统的 `pending` 表示「已创建且在 BullMQ 队列中等待」,语义更接近新系统的 `queued`。新系统的 `created` 预留给未来「创建记录」和「入队」分离的场景。
> **`lifecycleStatus` 过渡期 nullable**:旧代码写入时通过 Shadow Write 同步填充M-AI-02-10 回填历史 NULLM-AI-05 切换完成后改为 NOT NULL。
**迁移回填 SQL**(旧 `status` → 新 `lifecycleStatus`
```sql
UPDATE `AiAnalysisJob`
SET `lifecycleStatus` = CASE `status`
WHEN 'pending' THEN 'pending'
WHEN 'pending' THEN 'queued'
WHEN 'processing' THEN 'running'
WHEN 'completed' THEN 'succeeded'
WHEN 'failed' THEN 'failed'