f4c1a10403
- 修复 api.md 修改密码接口混入注册场景的 409 错误 - 补充 api.md WebSocket 示例缺失的 quality_supervisor running 消息 - 补充 api.md 注册接口格式校验错误响应、缓存接口详细说明 - 补充 frontend.md Task 接口缺失的 error 字段 - 补充 frontend.md 错误处理章节(API 错误、WebSocket 断连、加载状态) - frontend.md 风格键表改为引用 style-keys.md,消除重复 - async-tasks.md 去重/文件存储改为引用 database.md,消除重复 - backend.md ER 图改为引用 database.md,消除重复 - 补充 backend.md 目录结构中缺失的 auth/project handler 和 user model - 补充 backend.md 缓存层设计说明 - 补充 database.md GEN2D_SERVER_PORT 环境变量和迁移文件归属说明 - 补充 multi-agent-pipeline.md 到 backend.md 和 async-tasks.md 的交叉引用 - 补充 async-tasks.md 超时控制说明
9.7 KiB
9.7 KiB
前端工程
技术栈
Vite 6 + React 18 + TypeScript + zustand + react-router-dom
| 类别 | 选型 | 说明 |
|---|---|---|
| 构建 | Vite 6 | 开发服务器端口 3000,/api 代理到 localhost:8080 |
| UI 框架 | React 18 | 函数组件 + Hooks |
| 状态管理 | zustand | 轻量,支持 devtools 中间件 |
| 路由 | react-router-dom v6 | SPA 模式 |
| 实时通信 | 原生 WebSocket | 连接后端 /api/v1/tasks/:taskId/ws |
| HTTP 请求 | fetch + 封装层 | 统一错误处理、响应解包 |
目录结构
frontend/src/
├── main.tsx # 入口
├── App.tsx # 根组件 + 路由配置
├── api/ # API 封装层
│ ├── client.ts # fetch 封装:baseURL、统一错误处理、响应解包
│ ├── 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 # 当前工程(id、风格、任务列表)
│ ├── task.ts # 当前任务草稿(用户文本、素材类型、任务风格覆盖、技术参数)
│ └── generation.ts # 生成状态(taskId、进度、阶段、结果、错误)
├── pages/ # 页面级组件
│ ├── ProjectPage.tsx # 工程首页:工程风格配置 + 任务列表
│ ├── GeneratePage.tsx # 生成页:提示词构建 + 提交
│ └── ResultPage.tsx # 结果页:素材预览 + 下载 + 元数据
├── components/ # 可复用组件
│ ├── StyleSelector.tsx # 风格选择器
│ ├── PromptEditor.tsx # 三段式提示词编辑器
│ ├── GenerateForm.tsx # 生成表单
│ ├── ProgressBar.tsx # 管线进度条(显示当前阶段 + 重试状态)
│ └── AssetPreview.tsx # 素材预览(spritesheet 预览、单帧预览)
├── hooks/ # 自定义 Hooks
│ ├── useGenerate.ts # 提交生成任务 + WebSocket 订阅进度
│ └── useProjectStyle.ts # 工程风格加载/保存
├── router/ # 路由定义
│ └── index.tsx
└── utils/ # 工具函数
└── style.ts # 风格合并逻辑(与后端保持一致的 merge 算法)
路由规划
| 路径 | 页面 | 说明 |
|---|---|---|
/ |
— | 重定向到默认工程 |
/projects/:projectId |
ProjectPage | 工程首页,配置工程风格,查看历史任务 |
/projects/:projectId/generate |
GeneratePage | 提示词构建 + 提交生成 |
/projects/:projectId/tasks/:taskId |
ResultPage | 任务结果页,预览素材、下载 |
组件树
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"]
核心交互流程
生成流程
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
管线阶段对应 Eino Graph 节点,ProgressBar 展示:
| 阶段 | 说明 | ProgressBar 展示 |
|---|---|---|
prompt_builder |
合并风格,生成三段式提示词 | 第 1 步 |
asset_generator |
调用 AI 推理 API 出图 | 第 2 步 |
quality_supervisor |
视觉模型质检 | 第 3 步(可能回退到第 1 步) |
format_adapter |
格式转换、spritesheet 打包 | 第 4 步 |
质检重试时,ProgressBar 显示回退动画和重试次数。
风格编辑流程
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: 保存成功
错误处理
API 请求错误
api/client.ts统一拦截code !== 0的响应,抛出业务异常- 401:自动清除本地 Token,跳转到登录页(或提示重新登录)
- 403:提示无权访问,不自动跳转
- 429:提示请求过于频繁,稍后重试
- 500:展示通用错误提示
WebSocket 断连
- 连接断开后自动重连(指数退避,最大间隔 10 秒)
- 重连失败超过 3 次后,降级为轮询模式(
GET /api/v1/tasks/:taskId,间隔 2-3 秒) - 连接恢复后自动切回 WebSocket
加载状态
- 页面级加载(如 ProjectPage 进入时):展示骨架屏或 Loading 指示器
- 操作级提交(如保存风格、提交生成):按钮置为 loading 态,防止重复提交
状态管理
zustand stores
project store — 当前工程
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 — 任务草稿(对应后端 PipelineInput)
interface TaskStore {
prompt: string;
assetType: 'sprite' | 'background' | 'ui' | 'animation';
taskStyle: Record<string, string>; // 仅覆盖的键
params: {
resolution: number;
frames?: { directions: number; framesPerDirection: number };
format: 'spritesheet' | 'individual';
};
setPrompt: (text: string) => void;
setAssetType: (type: TaskStore['assetType']) => void;
toggleTaskStyle: (key: string, value: string) => void;
setParams: (params: Partial<TaskStore['params']>) => void;
reset: () => void;
}
generation store — 生成状态
interface GenerationStore {
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 封装
api/client.ts 统一封装:
- baseURL 从环境变量读取,开发模式默认
/api/v1 - 所有响应按
{ code, message, data }解包,code !== 0时抛错 - 统一 401/403/500 错误处理
- WebSocket 连接封装为
createTaskSocket(taskId)返回可订阅对象
API 类型定义(api/types.ts)
// 请求
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;
error?: string | null;
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;
}
预设风格键分类
前端 StyleSelector 以分类标签组织,完整键值表见 预设风格键。
StyleSelector 两种使用场景:
- 工程风格(ProjectPage):全量编辑,保存到后端
- 任务覆盖(GeneratePage):基于工程风格展示,高亮已覆盖的键,仅记录差异