docs: 后端采用 Eino 框架编排管线,完善前后端文档对齐
- 后端管线从手写编排改为 Eino compose.Graph,含质检重试分支 - 移除 CORS 中间件 - api.md 补充工程管理接口、任务状态机、管线阶段、WebSocket 消息示例 - frontend.md 组件树和交互流程改用 mermaid 图,stores 对齐 PipelineInput/Output - CLAUDE.md 修复死链,技术栈补充 Eino
This commit is contained in:
+160
-71
@@ -10,7 +10,7 @@ Vite 6 + React 18 + TypeScript + zustand + react-router-dom
|
||||
| UI 框架 | React 18 | 函数组件 + Hooks |
|
||||
| 状态管理 | zustand | 轻量,支持 devtools 中间件 |
|
||||
| 路由 | react-router-dom v6 | SPA 模式 |
|
||||
| 实时通信 | 原生 WebSocket | 连接后端 `/api/v1/generate/:jobId/ws` |
|
||||
| 实时通信 | 原生 WebSocket | 连接后端 `/api/v1/tasks/:taskId/ws` |
|
||||
| HTTP 请求 | fetch + 封装层 | 统一错误处理、响应解包 |
|
||||
|
||||
## 目录结构
|
||||
@@ -21,13 +21,13 @@ frontend/src/
|
||||
├── App.tsx # 根组件 + 路由配置
|
||||
├── api/ # API 封装层
|
||||
│ ├── client.ts # fetch 封装:baseURL、统一错误处理、响应解包
|
||||
│ ├── generate.ts # POST /generate、GET /generate/:jobId、GET /generate/:jobId/result
|
||||
│ ├── project.ts # GET/PUT /projects/:projectId/style
|
||||
│ └── types.ts # API 请求/响应类型定义(Task、Asset、Style、JobStatus 等)
|
||||
│ ├── project.ts # 工程 CRUD + 风格 GET/PUT
|
||||
│ ├── generate.ts # POST /generate、GET /tasks/:taskId、GET /tasks/:taskId/assets
|
||||
│ └── types.ts # API 请求/响应类型定义(Task、Asset、Style、PipelineProgress 等)
|
||||
├── stores/ # zustand stores
|
||||
│ ├── project.ts # 工程风格(从 API 加载 + 本地编辑 + 持久化回写)
|
||||
│ ├── project.ts # 当前工程(id、风格、任务列表)
|
||||
│ ├── task.ts # 当前任务草稿(用户文本、素材类型、任务风格覆盖、技术参数)
|
||||
│ └── generation.ts # 生成状态(jobId、进度、阶段、结果、错误)
|
||||
│ └── generation.ts # 生成状态(taskId、进度、阶段、结果、错误)
|
||||
├── pages/ # 页面级组件
|
||||
│ ├── ProjectPage.tsx # 工程首页:工程风格配置 + 任务列表
|
||||
│ ├── GeneratePage.tsx # 生成页:提示词构建 + 提交
|
||||
@@ -36,7 +36,7 @@ frontend/src/
|
||||
│ ├── StyleSelector.tsx # 风格选择器
|
||||
│ ├── PromptEditor.tsx # 三段式提示词编辑器
|
||||
│ ├── GenerateForm.tsx # 生成表单
|
||||
│ ├── ProgressBar.tsx # 管线进度条(显示当前阶段)
|
||||
│ ├── ProgressBar.tsx # 管线进度条(显示当前阶段 + 重试状态)
|
||||
│ └── AssetPreview.tsx # 素材预览(spritesheet 预览、单帧预览)
|
||||
├── hooks/ # 自定义 Hooks
|
||||
│ ├── useGenerate.ts # 提交生成任务 + WebSocket 订阅进度
|
||||
@@ -58,95 +58,104 @@ frontend/src/
|
||||
|
||||
## 组件树
|
||||
|
||||
```
|
||||
<App>
|
||||
<Router>
|
||||
<ProjectPage>
|
||||
<StyleSelector /> # 工程风格编辑
|
||||
<TaskList /> # 历史任务列表
|
||||
</ProjectPage>
|
||||
<GeneratePage>
|
||||
<GenerateForm>
|
||||
<AssetTypePicker /> # sprite / background / UI / animation
|
||||
<StyleSelector /> # 任务风格覆盖(基于工程风格,高亮差异)
|
||||
<PromptEditor /> # 三段式提示词预览 & 编辑
|
||||
<ParamsForm /> # 分辨率、帧数等技术参数
|
||||
</GenerateForm>
|
||||
<ProgressBar /> # 提交后显示管线进度
|
||||
</GeneratePage>
|
||||
<ResultPage>
|
||||
<AssetPreview /> # 素材预览
|
||||
<MetadataPanel /> # 元数据展示
|
||||
<DownloadButton /> # 下载素材
|
||||
</ResultPage>
|
||||
</Router>
|
||||
</App>
|
||||
```mermaid
|
||||
graph TD
|
||||
App --> Router
|
||||
Router --> ProjectPage
|
||||
Router --> GeneratePage
|
||||
Router --> ResultPage
|
||||
|
||||
ProjectPage --> StyleSelector["StyleSelector(工程风格编辑)"]
|
||||
ProjectPage --> TaskList["TaskList(历史任务列表)"]
|
||||
|
||||
GeneratePage --> GenerateForm
|
||||
GenerateForm --> AssetTypePicker["AssetTypePicker"]
|
||||
GenerateForm --> StyleSelector2["StyleSelector(任务风格覆盖)"]
|
||||
GenerateForm --> PromptEditor["PromptEditor(三段式预览)"]
|
||||
GenerateForm --> ParamsForm["ParamsForm(分辨率、帧数)"]
|
||||
GeneratePage --> ProgressBar["ProgressBar(管线进度)"]
|
||||
|
||||
ResultPage --> AssetPreview["AssetPreview(素材预览)"]
|
||||
ResultPage --> MetadataPanel["MetadataPanel(元数据)"]
|
||||
ResultPage --> DownloadButton["DownloadButton"]
|
||||
```
|
||||
|
||||
## 核心交互流程
|
||||
|
||||
### 生成流程
|
||||
|
||||
```
|
||||
1. 用户进入 GeneratePage
|
||||
2. 填写文本描述(prompt)
|
||||
3. 选择素材类型(sprite / background / UI / animation)
|
||||
4. StyleSelector 展示工程风格,用户可点选覆盖(任务风格)
|
||||
5. PromptEditor 实时预览三段式提示词:
|
||||
- 【主题】从用户文本提取
|
||||
- 【约束】从风格选择自动生成(含负面提示词)
|
||||
- 【内容】从素材类型 + 技术参数生成
|
||||
6. 用户确认后提交
|
||||
7. 前端 POST /api/v1/generate → 获取 jobId
|
||||
8. 建立 WebSocket 连接 /api/v1/generate/:jobId/ws
|
||||
9. ProgressBar 实时显示管线阶段:PromptBuilder → AssetGenerator → QualitySupervisor → FormatAdapter
|
||||
10. 管线完成后跳转 ResultPage
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant GP as GeneratePage
|
||||
participant API as 后端 API
|
||||
participant WS as WebSocket
|
||||
|
||||
User->>GP: 填写 prompt、选择素材类型
|
||||
User->>GP: StyleSelector 点选任务风格覆盖
|
||||
GP->>GP: PromptEditor 实时预览三段式提示词
|
||||
User->>GP: 点击提交
|
||||
GP->>API: POST /api/v1/generate
|
||||
API-->>GP: 返回 taskId
|
||||
GP->>WS: ws://host/api/v1/tasks/:taskId/ws
|
||||
loop 管线执行中
|
||||
WS-->>GP: PipelineProgress(stage + progress)
|
||||
GP->>GP: ProgressBar 更新阶段和进度
|
||||
end
|
||||
WS-->>GP: format_adapter completed + assets
|
||||
GP->>GP: 跳转 ResultPage
|
||||
```
|
||||
|
||||
### WebSocket 消息格式
|
||||
管线阶段对应 Eino Graph 节点,ProgressBar 展示:
|
||||
|
||||
```typescript
|
||||
interface PipelineProgress {
|
||||
stage: 'prompt_builder' | 'asset_generator' | 'quality_supervisor' | 'format_adapter';
|
||||
status: 'running' | 'completed' | 'failed';
|
||||
progress: number; // 0-100
|
||||
message?: string; // 阶段描述
|
||||
result?: { // 仅在 format_adapter completed 时返回
|
||||
assets: Asset[];
|
||||
};
|
||||
error?: string; // 仅在 failed 时返回
|
||||
}
|
||||
```
|
||||
| 阶段 | 说明 | ProgressBar 展示 |
|
||||
|------|------|-----------------|
|
||||
| `prompt_builder` | 合并风格,生成三段式提示词 | 第 1 步 |
|
||||
| `asset_generator` | 调用 AI 推理 API 出图 | 第 2 步 |
|
||||
| `quality_supervisor` | 视觉模型质检 | 第 3 步(可能回退到第 1 步) |
|
||||
| `format_adapter` | 格式转换、spritesheet 打包 | 第 4 步 |
|
||||
|
||||
质检重试时,ProgressBar 显示回退动画和重试次数。
|
||||
|
||||
### 风格编辑流程
|
||||
|
||||
```
|
||||
1. 用户进入 ProjectPage
|
||||
2. StyleSelector 从 API 加载工程风格(GET /projects/:id/style)
|
||||
3. 用户按分类点选风格键值对(美术风格、色调、线条、场景、光照、情绪)
|
||||
4. 实时展示当前风格配置
|
||||
5. 保存 → PUT /projects/:id/style
|
||||
6. 工程风格持久化到后端,后续生成任务自动继承
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
actor User
|
||||
participant SP as StyleSelector
|
||||
participant API as 后端 API
|
||||
|
||||
User->>SP: 进入 ProjectPage
|
||||
SP->>API: GET /api/v1/projects/:id/style
|
||||
API-->>SP: 返回 kvPairs
|
||||
SP->>SP: 按分类展示风格键值对
|
||||
User->>SP: 点选/修改风格
|
||||
SP->>SP: 实时展示当前配置
|
||||
User->>SP: 点击保存
|
||||
SP->>API: PUT /api/v1/projects/:id/style
|
||||
API-->>SP: 保存成功
|
||||
```
|
||||
|
||||
## 状态管理
|
||||
|
||||
### zustand stores
|
||||
|
||||
**project store** — 工程风格
|
||||
**project store** — 当前工程
|
||||
|
||||
```typescript
|
||||
interface ProjectStore {
|
||||
projectId: string;
|
||||
name: string;
|
||||
style: Record<string, string>; // kvPairs
|
||||
loading: boolean;
|
||||
loadProject: (projectId: string) => Promise<void>;
|
||||
loadStyle: (projectId: string) => Promise<void>;
|
||||
updateStyle: (kvPairs: Record<string, string>) => void;
|
||||
saveStyle: () => Promise<void>;
|
||||
}
|
||||
```
|
||||
|
||||
**task store** — 任务草稿
|
||||
**task store** — 任务草稿(对应后端 `PipelineInput`)
|
||||
|
||||
```typescript
|
||||
interface TaskStore {
|
||||
@@ -156,10 +165,10 @@ interface TaskStore {
|
||||
params: {
|
||||
resolution: number;
|
||||
frames?: { directions: number; framesPerDirection: number };
|
||||
format: string;
|
||||
format: 'spritesheet' | 'individual';
|
||||
};
|
||||
setPrompt: (text: string) => void;
|
||||
setAssetType: (type: string) => void;
|
||||
setAssetType: (type: TaskStore['assetType']) => void;
|
||||
toggleTaskStyle: (key: string, value: string) => void;
|
||||
setParams: (params: Partial<TaskStore['params']>) => void;
|
||||
reset: () => void;
|
||||
@@ -170,15 +179,23 @@ interface TaskStore {
|
||||
|
||||
```typescript
|
||||
interface GenerationStore {
|
||||
jobId: string | null;
|
||||
stage: PipelineProgress['stage'] | null;
|
||||
taskId: string | null;
|
||||
stage: PipelineStage | null;
|
||||
progress: number;
|
||||
status: 'idle' | 'submitting' | 'running' | 'completed' | 'failed';
|
||||
retryCount: number;
|
||||
rejectReason: string | null;
|
||||
assets: Asset[];
|
||||
error: string | null;
|
||||
submit: (projectId: string, task: TaskStore) => Promise<void>;
|
||||
reset: () => void;
|
||||
}
|
||||
|
||||
type PipelineStage =
|
||||
| 'prompt_builder'
|
||||
| 'asset_generator'
|
||||
| 'quality_supervisor'
|
||||
| 'format_adapter';
|
||||
```
|
||||
|
||||
## API 封装
|
||||
@@ -188,7 +205,79 @@ interface GenerationStore {
|
||||
- baseURL 从环境变量读取,开发模式默认 `/api/v1`
|
||||
- 所有响应按 `{ code, message, data }` 解包,`code !== 0` 时抛错
|
||||
- 统一 401/403/500 错误处理
|
||||
- WebSocket 连接封装为 `createJobSocket(jobId)` 返回可订阅对象
|
||||
- WebSocket 连接封装为 `createTaskSocket(taskId)` 返回可订阅对象
|
||||
|
||||
### API 类型定义(api/types.ts)
|
||||
|
||||
```typescript
|
||||
// 请求
|
||||
interface CreateProjectRequest {
|
||||
name: string;
|
||||
}
|
||||
|
||||
interface GenerateRequest {
|
||||
projectId: string;
|
||||
prompt: string;
|
||||
assetType: 'sprite' | 'background' | 'ui' | 'animation';
|
||||
taskStyle?: Record<string, string>;
|
||||
params?: {
|
||||
resolution?: number;
|
||||
frames?: { directions?: number; framesPerDirection?: number };
|
||||
format?: 'spritesheet' | 'individual';
|
||||
};
|
||||
}
|
||||
|
||||
interface UpdateStyleRequest {
|
||||
kvPairs: Record<string, string>;
|
||||
}
|
||||
|
||||
// 响应
|
||||
interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
style: { kvPairs: Record<string, string> };
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
interface Task {
|
||||
id: string;
|
||||
projectId: string;
|
||||
prompt: string;
|
||||
assetType: string;
|
||||
status: 'pending' | 'running' | 'completed' | 'failed';
|
||||
stage?: PipelineStage;
|
||||
progress?: number;
|
||||
retryCount?: number;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
interface Asset {
|
||||
id: string;
|
||||
url: string;
|
||||
format: string;
|
||||
width: number;
|
||||
height: number;
|
||||
metadata: {
|
||||
frameWidth?: number;
|
||||
frameHeight?: number;
|
||||
frameCount?: number;
|
||||
directions?: number;
|
||||
};
|
||||
}
|
||||
|
||||
// WebSocket 消息
|
||||
interface PipelineProgress {
|
||||
stage: PipelineStage;
|
||||
status: 'running' | 'completed' | 'failed';
|
||||
progress: number;
|
||||
message?: string;
|
||||
retryCount?: number;
|
||||
rejectReason?: string;
|
||||
result?: { assets: Asset[] };
|
||||
error?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## 预设风格键分类
|
||||
|
||||
|
||||
Reference in New Issue
Block a user