Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions _PR说明.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Generation + SSE Adapter

Refs #78

Issue #78 已补 `mile3` 标签并关联 `milestone/4`。本变更只实现前端 Generation
实体适配器与 SSE 传输封装,并同步修正相关接口文档;不修改后端、页面、Controller
或构建产物。

## 变更范围

- 将创建、按项目查询和状态订阅统一收口到 `GenerationApis`。
- `createGenerationApis` 必须由宿主注入 `userId` 与 `transport`;模块不写死用户身份,
也不直接持有 `fetch` 或 `EventSource`。
- 新增业务无关的 `shared/api/stream.ts`。后端 SSE 需要 Bearer Token,因此使用
`fetch + ReadableStream`,由宿主注入 token provider;支持命名事件、主动取消、
终态关闭和网络断线重连,不恢复 2 秒业务轮询。
- 查询与订阅要求调用方传入 WorkflowRun 已知的阶段期望;动作阶段还必须带上
`actionType`,避免把其他动作的帧误接到当前任务。
- 角色母版由宿主通过 `resolveImageSize(projectId)` 提供项目画布尺寸;Generation 不直接
依赖 Project,也不会回退到可能冲突的 1024 默认值。

## 三阶段合同

| 前端阶段 | 后端请求 | 固定/可配置数量 | 结果映射 |
| -------------------- | ------------------------- | ------------------------- | ----------------------------------- |
| `character_template` | `POST /generation/image` | 固定 `num_images: 4` | 严格校验并映射 4 个候选 |
| `first_frame` | `POST /generation/action` | 固定 `num_frames: 1` | 严格校验并映射 1 帧动作首帧 |
| `complete_animation` | `POST /generation/action` | 当前固定 `num_frames: 16` | 按 `index` 排序并保留 `duration_ms` |

`CompleteAnimationGenerationInput` 当前没有 `frameCount` 字段,因此无法由调用输入表达
帧数。本次保守沿用后端合同默认值 16;后续若合同加入可配置字段,应改为透传输入并补充
边界校验,而不是继续保留常量。

## SSE 行为

- 端点:`/generation/tasks/{taskId}/stream?project_id=...`。
- 只监听 `task_update` 命名事件,事件 DTO 在 Generation 边界解析和校验。
- 后端完整任务事件使用数字 `id`;适配器在边界转换为前端字符串 `taskId`。
- 每次建连前重新读取 token 并设置 `Authorization: Bearer ...`,token 刷新后重连会使用新值。
- `completed`、`failed` 都视为终态;事件先交付调用方,再由传输层关闭连接。
- `onError` 必传,非法事件关闭连接时不能静默留下永远等待的工作流。
- 显式取消通过 `AbortController` 中止当前 fetch 请求。
- 非法 JSON、非法 DTO 或调用方事件处理异常会报告错误并关闭连接。
- 网络中断会报告错误并按配置重连;HTTP 错误、非法响应和业务 DTO 错误停止重连。
没有定时 GET 或其他业务轮询。

## DTO 校验

- 校验响应 envelope 的 `code/message/data`,业务错误不会被当作成功数据。
- 校验任务与事件的正整数 ID、项目归属、用户归属、输入、任务类型和四个合法状态。
- 未知状态直接抛 `GenerationApiError`,绝不降级为 `pending`。
- 校验完成结果的判别字段、图片 URL、候选/首帧数量、动作类型、16 帧数量与连续索引、
时长格式与状态/错误一致性;完整动画保留逐帧时长,非完成任务携带结果同样视为非法合同。

## 测试覆盖

- 三阶段请求体映射与注入用户身份。
- 角色母版四候选、动作一首帧、完整动画帧排序。
- 未知状态与非法完成结果 DTO。
- SSE URL、Bearer Token、重连时刷新 token、`id → taskId`、主动取消、终态关闭、
事件解析错误和断线恢复语义。

## 验证结果

- `npx oxfmt --check` 定向检查本次前端合同与 TypeScript 文件:通过。
- `npm run format:check` 全量检查:已执行,但被基线中 41 个本次所有权外文件阻断;
未批量重写这些文件,以免覆盖其他工作者的修改。
- `npm run lint`:通过。
- `npm run typecheck`:通过。
- `npm test`:在最新 `main` 合并基线上通过,20 个测试文件、91 个测试。
- `npm run build`:通过,Vite 8.1.5 共转换 105 个模块。

后端 `/generation/tasks/{taskId}/stream` 以 DireSoul PR #34 的已合并实现为准;本 PR
按其完整 GenerationTask 事件和鉴权要求对齐。测试覆盖前端传输与映射,真实部署环境的
端到端联调仍应在集成分支执行。
19 changes: 18 additions & 1 deletion frontend/API_CONTRACT.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# 前后端接口对齐清单

本实现以尚未合并的后端 PR #75 为目标契约,并要求按 **#75 → 本前端 PR** 的顺序合并。`upstream/main` 当前尚未挂载这些接口。
Project / Character 以尚未合并的后端 PR #75 为目标契约;Generation SSE 以
DireSoul 已合并的 PR #34 为目标契约。`upstream/main` 当前尚未挂载 PR #75 的接口。

## 一、本轮已接入

Expand Down Expand Up @@ -54,6 +55,22 @@ Character

Outfit、Action、Frame 没有独立端点。`outfit.characterId` 与 `action.outfitId` 仅由嵌套关系推导;修改任一子项时通过 `PATCH Character` 提交完整 `character_data`。

### Generation 异步任务

| 前端阶段 | 后端请求 | 后端任务类型 | 数量与结果 |
|---|---|---|---|
| `character_template` | `POST /generation/image` | `character_image` | 固定 4 个候选 |
| `first_frame` | `POST /generation/action` | `character_action` | 固定 1 帧 |
| `complete_animation` | `POST /generation/action` | `character_action` | 当前固定 16 帧,按 `index` 排序并保留 `duration_ms` |

- 创建和查询通过 `GenerationApis.create/get`;状态变化通过
`GET /generation/tasks/{task_id}/stream?project_id=...` 的 `task_update` 事件订阅。
- SSE 路由需要 Bearer Token,前端使用 `fetch + ReadableStream`,不使用无法设置
`Authorization` 请求头的原生 `EventSource`。
- 后端事件发送完整 GenerationTask,标识字段是数字 `id`;适配器在边界转换为前端
字符串 `taskId`,并校验用户、项目、输入、状态及结果。
- `completed` / `failed` 后关闭流;网络中断重连并重新读取 token,非法 DTO 停止连接。

## 二、本轮明确不实现

- Workflow Editor 与生成流程:不在 Projects / 资产库模块内创建弹窗或复制生成逻辑。
Expand Down
Loading