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 超时控制说明
543 lines
12 KiB
Markdown
543 lines
12 KiB
Markdown
# gen2d API 设计
|
||
|
||
所有接口统一前缀 `/api/v1/`,统一响应格式:
|
||
|
||
```json
|
||
{ "code": 0, "message": "ok", "data": {} }
|
||
```
|
||
|
||
## 认证
|
||
|
||
除健康检查和注册登录外,所有接口需在请求头携带 Bearer Token:
|
||
|
||
```
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
Token 通过登录接口获取,过期时间由 `GEN2D_JWT_EXPIRE` 控制(默认 7200 秒)。Token 过期或无效时返回:
|
||
|
||
```json
|
||
{ "code": 401, "message": "未登录或 Token 已过期", "data": null }
|
||
```
|
||
|
||
权限不足时返回:
|
||
|
||
```json
|
||
{ "code": 403, "message": "无权访问该资源", "data": null }
|
||
```
|
||
|
||
## 错误码
|
||
|
||
| code | 含义 | 场景 |
|
||
|------|------|------|
|
||
| 0 | 成功 | — |
|
||
| 400 | 请求参数错误 | 缺少必填字段、格式不合法 |
|
||
| 401 | 未认证 | Token 缺失、过期、无效 |
|
||
| 403 | 无权访问 | 访问不属于自己的资源 |
|
||
| 404 | 资源不存在 | 工程/任务/素材 ID 不存在 |
|
||
| 409 | 冲突 | 用户名或邮箱已注册 |
|
||
| 429 | 请求过于频繁 | 触发速率限制 |
|
||
| 500 | 服务器内部错误 | 未预期异常 |
|
||
|
||
## 状态说明
|
||
|
||
- [x] 已完成
|
||
- [ ] 规划中
|
||
|
||
---
|
||
|
||
## 基础设施
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [x] | GET | `/api/v1/health` | 健康检查 |
|
||
|
||
## 用户认证
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [ ] | POST | `/api/v1/auth/register` | 用户注册 |
|
||
| [ ] | POST | `/api/v1/auth/login` | 用户登录 |
|
||
| [ ] | GET | `/api/v1/auth/me` | 获取当前用户信息 |
|
||
| [ ] | PUT | `/api/v1/auth/password` | 修改密码 |
|
||
|
||
### POST /api/v1/auth/register
|
||
|
||
```json
|
||
{
|
||
"username": "player1",
|
||
"email": "player1@example.com",
|
||
"password": "s3cretP@ss"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `username` | string | 是 | 3-64 字符,字母数字下划线 |
|
||
| `email` | string | 是 | 邮箱地址 |
|
||
| `password` | string | 是 | 8-128 字符 |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"id": "user_a1B2c3D4",
|
||
"username": "player1",
|
||
"email": "player1@example.com",
|
||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||
"expiresAt": "2026-05-24T12:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
错误响应:
|
||
|
||
| 场景 | code | message |
|
||
|------|------|---------|
|
||
| 用户名格式不合法(非 3-64 字符或含特殊字符) | 400 | 用户名格式不合法 |
|
||
| 密码长度不足 | 400 | 密码长度不能少于 8 位 |
|
||
| 邮箱格式不合法 | 400 | 邮箱格式不正确 |
|
||
| 用户名已存在 | 409 | 用户名已被注册 |
|
||
| 邮箱已存在 | 409 | 邮箱已被注册 |
|
||
|
||
### POST /api/v1/auth/login
|
||
|
||
```json
|
||
{
|
||
"username": "player1",
|
||
"password": "s3cretP@ss"
|
||
}
|
||
```
|
||
|
||
支持 `username` 或 `email` 登录:
|
||
|
||
```json
|
||
{
|
||
"email": "player1@example.com",
|
||
"password": "s3cretP@ss"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"id": "user_a1B2c3D4",
|
||
"username": "player1",
|
||
"email": "player1@example.com",
|
||
"token": "eyJhbGciOiJIUzI1NiIs...",
|
||
"expiresAt": "2026-05-24T12:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/v1/auth/me
|
||
|
||
需认证。响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"id": "user_a1B2c3D4",
|
||
"username": "player1",
|
||
"email": "player1@example.com",
|
||
"createdAt": "2026-05-24T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### PUT /api/v1/auth/password
|
||
|
||
需认证。
|
||
|
||
```json
|
||
{
|
||
"oldPassword": "s3cretP@ss",
|
||
"newPassword": "n3wS3cretP@ss"
|
||
}
|
||
```
|
||
|
||
错误响应:
|
||
|
||
| 场景 | code | message |
|
||
|------|------|---------|
|
||
| 原密码错误 | 400 | 原密码不正确 |
|
||
|
||
## 工程管理
|
||
|
||
需认证。以下接口均需携带 Bearer Token,工程归属于当前登录用户。
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [ ] | GET | `/api/v1/projects` | 获取当前用户的工程列表 |
|
||
| [ ] | POST | `/api/v1/projects` | 创建工程 |
|
||
| [ ] | GET | `/api/v1/projects/:projectId` | 获取工程信息 |
|
||
| [ ] | DELETE | `/api/v1/projects/:projectId` | 删除工程(级联删除任务和素材) |
|
||
| [ ] | GET | `/api/v1/projects/:projectId/tasks` | 获取工程下的任务列表 |
|
||
|
||
### GET /api/v1/projects
|
||
|
||
获取当前用户的工程列表。
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `page` | int | 页码,默认 1 |
|
||
| `pageSize` | int | 每页条数,默认 20 |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"total": 3,
|
||
"projects": [
|
||
{
|
||
"id": "proj_abc123",
|
||
"name": "我的像素游戏",
|
||
"createdAt": "2026-05-24T10:00:00Z"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### POST /api/v1/projects
|
||
|
||
```json
|
||
{
|
||
"name": "我的像素游戏"
|
||
}
|
||
```
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"id": "proj_abc123",
|
||
"name": "我的像素游戏",
|
||
"style": { "kvPairs": {} },
|
||
"createdAt": "2026-05-24T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
### GET /api/v1/projects/:projectId
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"id": "proj_abc123",
|
||
"name": "我的像素游戏",
|
||
"createdAt": "2026-05-24T10:00:00Z",
|
||
"updatedAt": "2026-05-24T10:00:00Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
错误响应:
|
||
|
||
| 场景 | code | message |
|
||
|------|------|---------|
|
||
| projectId 不存在 | 404 | 工程不存在 |
|
||
| 工程不属于当前用户 | 403 | 无权访问该工程 |
|
||
|
||
### DELETE /api/v1/projects/:projectId
|
||
|
||
级联删除工程下的所有任务、素材及 OSS 文件。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### GET /api/v1/projects/:projectId/tasks
|
||
|
||
| 参数 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `page` | int | 页码,默认 1 |
|
||
| `pageSize` | int | 每页条数,默认 20 |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"total": 42,
|
||
"tasks": [
|
||
{
|
||
"id": "task_xyz789",
|
||
"prompt": "一个拿剑的小人",
|
||
"assetType": "sprite",
|
||
"status": "completed",
|
||
"createdAt": "2026-05-24T10:05:00Z"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
## 工程风格
|
||
|
||
需认证。风格归属于工程,仅工程所属用户可操作。
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [ ] | GET | `/api/v1/projects/:projectId/style` | 获取工程风格 |
|
||
| [ ] | PUT | `/api/v1/projects/:projectId/style` | 更新工程风格 |
|
||
|
||
请求体示例 (PUT /api/v1/projects/:projectId/style):
|
||
|
||
```json
|
||
{
|
||
"kvPairs": {
|
||
"artStyle": "pixel",
|
||
"palette": "warm",
|
||
"lineWeight": "thin",
|
||
"lighting": "bright"
|
||
}
|
||
}
|
||
```
|
||
|
||
## 素材生成
|
||
|
||
需认证。任务归属于当前用户的工程,跨用户访问返回 403。
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [ ] | POST | `/api/v1/generate` | 提交生成任务,返回 taskId |
|
||
| [ ] | GET | `/api/v1/tasks/:taskId` | 查询任务状态与进度 |
|
||
| [ ] | GET | `/api/v1/tasks/:taskId/assets` | 获取生成结果(素材列表 + 元数据) |
|
||
| [ ] | WS | `/api/v1/tasks/:taskId/ws` | WebSocket 实时进度推送 |
|
||
|
||
### POST /api/v1/generate
|
||
|
||
请求体对应后端 `PipelineInput`:
|
||
|
||
```json
|
||
{
|
||
"projectId": "proj_abc123",
|
||
"prompt": "一个拿剑的小人",
|
||
"assetType": "sprite",
|
||
"taskStyle": {
|
||
"scene": "dungeon",
|
||
"mood": "dark"
|
||
},
|
||
"params": {
|
||
"resolution": 64,
|
||
"frames": { "directions": 8, "framesPerDirection": 4 },
|
||
"format": "spritesheet"
|
||
}
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 必填 | 说明 |
|
||
|------|------|------|------|
|
||
| `projectId` | string | 是 | 工程 ID,用于获取工程风格 |
|
||
| `prompt` | string | 是 | 用户原始文本,后端 PromptBuilder 负责三段式重写 |
|
||
| `assetType` | string | 是 | 素材类型:`sprite` / `background` / `ui` / `animation` |
|
||
| `taskStyle` | object | 否 | 任务级别风格覆盖(同名键覆盖工程风格) |
|
||
| `params.resolution` | int | 否 | 分辨率,默认 64 |
|
||
| `params.frames` | object | 否 | 帧参数(仅 sprite/animation) |
|
||
| `params.frames.directions` | int | 否 | 方向数,默认 4 |
|
||
| `params.frames.framesPerDirection` | int | 否 | 每方向帧数,默认 4 |
|
||
| `params.format` | string | 否 | 输出格式:`spritesheet` / `individual`,默认 `spritesheet` |
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"taskId": "task_xyz789",
|
||
"status": "pending"
|
||
}
|
||
}
|
||
```
|
||
|
||
去重:相同 `prompt + assetType + params` 的并发请求返回已有 taskId,不重复创建任务。
|
||
|
||
错误响应:
|
||
|
||
| 场景 | code | message |
|
||
|------|------|---------|
|
||
| projectId 不存在或不属于当前用户 | 403 | 无权访问该工程 |
|
||
| prompt 为空 | 400 | prompt 不能为空 |
|
||
|
||
### GET /api/v1/tasks/:taskId
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"taskId": "task_xyz789",
|
||
"projectId": "proj_abc123",
|
||
"prompt": "一个拿剑的小人",
|
||
"assetType": "sprite",
|
||
"taskStyle": null,
|
||
"params": { "resolution": 64, "format": "spritesheet" },
|
||
"status": "running",
|
||
"stage": "asset_generator",
|
||
"progress": 45,
|
||
"retryCount": 0,
|
||
"error": null,
|
||
"createdAt": "2026-05-24T10:05:00Z",
|
||
"updatedAt": "2026-05-24T10:05:30Z"
|
||
}
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| `status` | string | 任务状态:`pending` / `running` / `completed` / `failed` |
|
||
| `stage` | string | 当前管线阶段,`running` 时有值 |
|
||
| `progress` | int | 0-100,整体进度 |
|
||
| `retryCount` | int | 质检重试次数 |
|
||
| `error` | string | 失败原因,仅 `failed` 时有值 |
|
||
|
||
状态机与管线阶段详见 [异步任务](async-tasks.md)。
|
||
|
||
错误响应:
|
||
|
||
| 场景 | code | message |
|
||
|------|------|---------|
|
||
| taskId 不存在 | 404 | 任务不存在 |
|
||
| 任务不属于当前用户 | 403 | 无权访问该任务 |
|
||
|
||
### GET /api/v1/tasks/:taskId/assets
|
||
|
||
任务状态为 `completed` 时返回素材列表,其他状态返回空数组。
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"assets": [
|
||
{
|
||
"id": "asset_001",
|
||
"url": "https://cdn.example.com/users/user_a1B2c3D4/projects/proj_abc123/tasks/task_xyz789/output/spritesheet.png",
|
||
"format": "png",
|
||
"width": 256,
|
||
"height": 64,
|
||
"metadata": {
|
||
"frameWidth": 64,
|
||
"frameHeight": 64,
|
||
"frameCount": 4,
|
||
"directions": 1
|
||
}
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### WebSocket 消息格式
|
||
|
||
连接路径:`ws://host/api/v1/tasks/:taskId/ws?token=<jwt>`(通过 query 参数传递 Token)
|
||
|
||
```typescript
|
||
interface PipelineProgress {
|
||
stage: 'prompt_builder' | 'asset_generator' | 'quality_supervisor' | 'format_adapter';
|
||
status: 'running' | 'completed' | 'failed';
|
||
progress: number; // 0-100
|
||
message?: string; // 阶段描述
|
||
retryCount?: number; // 质检重试次数(仅 quality_supervisor 阶段)
|
||
rejectReason?: string; // 质检不通过原因(仅 quality_supervisor fail 时)
|
||
result?: { // 仅在 format_adapter completed 时返回
|
||
assets: Asset[];
|
||
};
|
||
error?: string; // 仅在 failed 时返回
|
||
}
|
||
```
|
||
|
||
消息示例(正常流程):
|
||
|
||
```json
|
||
{"stage":"prompt_builder","status":"running","progress":0}
|
||
{"stage":"prompt_builder","status":"completed","progress":100}
|
||
{"stage":"asset_generator","status":"running","progress":0}
|
||
{"stage":"asset_generator","status":"completed","progress":100}
|
||
{"stage":"quality_supervisor","status":"running","progress":0}
|
||
{"stage":"quality_supervisor","status":"completed","progress":100}
|
||
{"stage":"format_adapter","status":"running","progress":0}
|
||
{"stage":"format_adapter","status":"completed","progress":100,"result":{"assets":[...]}}
|
||
```
|
||
|
||
消息示例(质检重试):
|
||
|
||
```json
|
||
{"stage":"quality_supervisor","status":"running","progress":0}
|
||
{"stage":"quality_supervisor","status":"failed","progress":100,"retryCount":1,"rejectReason":"风格不一致"}
|
||
{"stage":"prompt_builder","status":"running","progress":0}
|
||
{"stage":"asset_generator","status":"running","progress":0}
|
||
{"stage":"quality_supervisor","status":"completed","progress":100}
|
||
{"stage":"format_adapter","status":"completed","progress":100,"result":{"assets":[...]}}
|
||
```
|
||
|
||
## 缓存管理
|
||
|
||
需认证。用于管理应用层去重缓存(详见 [数据存储 — 去重策略](database.md#去重策略)),主要用于调试和运维场景。
|
||
|
||
| 状态 | 方法 | 路径 | 说明 |
|
||
|------|------|------|------|
|
||
| [ ] | DELETE | `/api/v1/cache/:key` | 清除特定去重缓存条目 |
|
||
| [ ] | POST | `/api/v1/cache/clear` | 批量清除所有去重缓存 |
|
||
|
||
### DELETE /api/v1/cache/:key
|
||
|
||
清除指定去重键的缓存条目。`:key` 为 `hash(prompt + assetType + params)` 生成的去重键,仅在需要手动解除去重锁定时使用。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### POST /api/v1/cache/clear
|
||
|
||
清除所有去重缓存条目,不影响正在执行的任务。
|
||
|
||
响应:
|
||
|
||
```json
|
||
{
|
||
"code": 0,
|
||
"message": "ok",
|
||
"data": {
|
||
"cleared": 5
|
||
}
|
||
}
|
||
```
|