# gen2d API 设计 所有接口统一前缀 `/api/v1/`,统一响应格式: ```json { "code": 0, "message": "ok", "data": {} } ``` ## 认证 除健康检查和注册登录外,所有接口需通过 httpOnly Cookie 携带 JWT。登录/注册成功后,服务端通过 `Set-Cookie` 响应头写入 Token,后续请求自动携带。 Cookie 属性: - `Name`: `token` - `HttpOnly`: true - `SameSite`: Lax - `Path`: `/` - `MaxAge`: 由 `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` | 健康检查 | ## 用户认证 | 状态 | 方法 | 路径 | 说明 | |------|------|------|------| | [x] | POST | `/api/v1/auth/register` | 用户注册 | | [x] | 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 字符 | 响应(Token 通过 `Set-Cookie` 响应头写入,不在 body 中返回): ```json { "code": 0, "message": "ok", "data": { "id": "user_a1B2c3D4", "username": "player1", "email": "player1@example.com" } } ``` 错误响应: | 场景 | 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" } ``` 响应(Token 通过 `Set-Cookie` 响应头写入,不在 body 中返回): ```json { "code": 0, "message": "ok", "data": { "id": "user_a1B2c3D4", "username": "player1", "email": "player1@example.com" } } ``` ### 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 | 原密码不正确 | ## 工程管理 需认证。以下接口均需通过 Cookie 携带 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 级联删除工程下的所有任务、素材及本地文件。 响应: ```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": "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`(通过 httpOnly Cookie 自动携带认证信息) ```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":[...]}} ```