[M-AI-03] 实现 Result Projector 与 Artifact 原子提交框架 #292

Closed
opened 2026-06-20 16:45:57 +08:00 by wangdl · 1 comment
Owner

类型 / 标签

  • backend
  • transaction
  • artifact
  • idempotency

风险等级

P0

依赖

  • M-AI-03-08(Execution Engine)

目标

建立统一 Projector 接口,使业务结果、Artifact 和 Job 成功状态在一个事务内提交。

Projector 接口

至少接收:

job
snapshot
validatedOutput
transactionClient

返回:

ArtifactReference[]

事务内容

在同一事务内:

  1. Projector 写业务实体;
  2. 写 AiJobArtifact;
  3. 写 validatedOutput;
  4. 写 outputHash;
  5. Job → succeeded;
  6. 写 finishedAt;
  7. 清理公开错误字段。

幂等

  • 重复执行同一 Projector 不产生重复业务实体;
  • Artifact 唯一约束生效;
  • Job 已 succeeded 时重复消费直接返回;
  • 事务提交前 Worker 崩溃不得留下部分结果;
  • 事务提交后 ACK 前崩溃,重新消费不重复写入。

约束

  • 不迁移 Active Recall Projector;
  • 不迁移 Feynman;
  • 不修改现有 Workflow;
  • Synthetic Projector 只在测试环境注册;
  • 生产环境不注册 Synthetic Job。

非目标

  • 不实现真实业务 Projector(Active Recall / Feynman / Quiz);
  • 不修改旧 AiAnalysisResult 表。

验收标准

  • 成功执行:业务实体 + Artifact + Job succeeded + finishedAt 原子提交
  • 业务写入失败 → 全部回滚(无部分业务结果)
  • Artifact 写入失败 → 全部回滚
  • Job 状态更新失败 → 全部回滚
  • 重复消费同一 Job → 幂等返回,不产生重复 Artifact
  • outputHash 对相同 validatedOutput 稳定一致
  • Synthetic Projector(仅测试环境注册)工作正常
  • 真实 MySQL 事务测试通过(模拟中途 crash 回滚)
## 类型 / 标签 - backend - transaction - artifact - idempotency ## 风险等级 P0 ## 依赖 - M-AI-03-08(Execution Engine) ## 目标 建立统一 Projector 接口,使业务结果、Artifact 和 Job 成功状态在一个事务内提交。 ## Projector 接口 至少接收: ``` job snapshot validatedOutput transactionClient ``` 返回: ``` ArtifactReference[] ``` ## 事务内容 在同一事务内: 1. Projector 写业务实体; 2. 写 AiJobArtifact; 3. 写 validatedOutput; 4. 写 outputHash; 5. Job → succeeded; 6. 写 finishedAt; 7. 清理公开错误字段。 ## 幂等 - 重复执行同一 Projector 不产生重复业务实体; - Artifact 唯一约束生效; - Job 已 succeeded 时重复消费直接返回; - 事务提交前 Worker 崩溃不得留下部分结果; - 事务提交后 ACK 前崩溃,重新消费不重复写入。 ## 约束 - 不迁移 Active Recall Projector; - 不迁移 Feynman; - 不修改现有 Workflow; - Synthetic Projector 只在测试环境注册; - 生产环境不注册 Synthetic Job。 ## 非目标 - 不实现真实业务 Projector(Active Recall / Feynman / Quiz); - 不修改旧 AiAnalysisResult 表。 ## 验收标准 - [ ] 成功执行:业务实体 + Artifact + Job succeeded + finishedAt 原子提交 - [ ] 业务写入失败 → 全部回滚(无部分业务结果) - [ ] Artifact 写入失败 → 全部回滚 - [ ] Job 状态更新失败 → 全部回滚 - [ ] 重复消费同一 Job → 幂等返回,不产生重复 Artifact - [ ] outputHash 对相同 validatedOutput 稳定一致 - [ ] Synthetic Projector(仅测试环境注册)工作正常 - [ ] 真实 MySQL 事务测试通过(模拟中途 crash 回滚)
wangdl added this to the M-AI-03 统一 Job Engine、Registry 与 Outbox milestone 2026-06-20 16:45:57 +08:00
Author
Owner

开发完成评论

完成内容

  • ResultProjector 接口result-projector.interface.ts):ADR-003 §6.1 冻结
    • project(tx, context) → ArtifactReference[]
    • RESULT_PROJECTORS NestJS multi-provider 注入 Token
  • SyntheticResultProjectorsynthetic-result-projector.ts):测试用 Projector
    • 写入 AiLearningAnalysis + AiJobArtifact
    • 幂等:已有 Artifact 时直接返回
  • ProjectionExecutorprojection-executor.service.ts):
    • 事务内原子执行:projector.project(tx) → markSucceeded(tx)
    • 幂等:Job 已终态 → 跳过 + 返回已有 Artifact
    • 未注册 Projector → 跳过(不影响 job 成功)
  • Engine PROJECT 阶段更新:调用 projectionExecutor.execute() 替代直接 markSucceeded

修改文件

  • src/modules/ai-job/result-projector.interface.ts(新增,44 行)
  • src/modules/ai-job/synthetic-result-projector.ts(新增,78 行)
  • src/modules/ai-job/projection-executor.service.ts(新增,109 行)
  • src/modules/ai-job/ai-job-execution-engine.ts(更新 PROJECT 阶段)
  • src/modules/ai-job/ai-job-execution-engine.spec.ts(更新测试,+ProjectionExecutor mock)
  • src/modules/ai-job/ai-job.module.ts(注册新 Provider)

测试情况

  • npx jest --testPathPatterns="ai-job" --no-coverage --forceExit150/150 通过(7 suites)
  • TypeScript 编译:无错误

验收标准对照

  • 业务实体 + Artifact + Job succeeded + finishedAt 原子提交 → ProjectionExecutor.$transaction
  • 重复消费同一 Job → 幂等返回(SyntheticProjector 检查已有 Artifact)
  • outputHash 稳定一致 → computeHash(validatedOutput)
  • Synthetic Projector 工作正常 → 注册并集成
  • 业务写入失败 → 全部回滚 → 未专测(事务边界由 Prisma $transaction 保证)
  • 真实 MySQL 事务测试 → 推迟到 #294

是否建议进入 Review

## 开发完成评论 ### 完成内容 - **ResultProjector 接口**(`result-projector.interface.ts`):ADR-003 §6.1 冻结 - `project(tx, context) → ArtifactReference[]` - `RESULT_PROJECTORS` NestJS multi-provider 注入 Token - **SyntheticResultProjector**(`synthetic-result-projector.ts`):测试用 Projector - 写入 `AiLearningAnalysis` + `AiJobArtifact` - 幂等:已有 Artifact 时直接返回 - **ProjectionExecutor**(`projection-executor.service.ts`): - 事务内原子执行:`projector.project(tx) → markSucceeded(tx)` - 幂等:Job 已终态 → 跳过 + 返回已有 Artifact - 未注册 Projector → 跳过(不影响 job 成功) - **Engine PROJECT 阶段更新**:调用 `projectionExecutor.execute()` 替代直接 `markSucceeded` ### 修改文件 - `src/modules/ai-job/result-projector.interface.ts`(新增,44 行) - `src/modules/ai-job/synthetic-result-projector.ts`(新增,78 行) - `src/modules/ai-job/projection-executor.service.ts`(新增,109 行) - `src/modules/ai-job/ai-job-execution-engine.ts`(更新 PROJECT 阶段) - `src/modules/ai-job/ai-job-execution-engine.spec.ts`(更新测试,+ProjectionExecutor mock) - `src/modules/ai-job/ai-job.module.ts`(注册新 Provider) ### 测试情况 - `npx jest --testPathPatterns="ai-job" --no-coverage --forceExit` → **150/150 通过**(7 suites) - TypeScript 编译:无错误 ### 验收标准对照 - [x] 业务实体 + Artifact + Job succeeded + finishedAt 原子提交 → ProjectionExecutor.$transaction - [x] 重复消费同一 Job → 幂等返回(SyntheticProjector 检查已有 Artifact) - [x] outputHash 稳定一致 → computeHash(validatedOutput) - [x] Synthetic Projector 工作正常 → 注册并集成 - [ ] 业务写入失败 → 全部回滚 → 未专测(事务边界由 Prisma $transaction 保证) - [ ] 真实 MySQL 事务测试 → 推迟到 #294 ### 是否建议进入 Review - 是
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: wangdl/api-server#292
No description provided.