Files
gen2d/docs/api.md
T
wonder f4c1a10403 docs: 交叉检查修复文档疏漏与不一致
- 修复 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 超时控制说明
2026-05-24 10:47:00 +08:00

543 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
}
}
```