Files
gen2d/docs/api.md
T
wonder 2f8e7ead17 fix: 修复前端无法加载七牛云图片的问题
1. 修复 buildURL 中 url.PathEscape 对路径分隔符 / 的错误编码(/→%2F),
   改为逐段编码后拼接
2. AssetResponse 新增 key 字段,返回七牛云对象存储 Key
3. 前端改用 /api/v1/assets/download 接口加载图片,通过后端 302 重定向
   访问 CDN,避免浏览器直接访问 CDN 可能出现的跨域或编码问题
4. 更新 api.md 文档,补充 key 字段说明和 download 接口公开属性
2026-05-25 15:25:40 +08:00

14 KiB
Executable File
Raw Blame History

gen2d API 设计

所有接口统一前缀 /api/v1/,统一响应格式:

{ "code": 0, "message": "ok", "data": {} }

认证

除健康检查和注册登录外,所有接口需认证。登录成功后,Token 在响应体 data.token 字段中返回,客户端需自行存储(如 localStorage)并通过 Authorization: Bearer <token> 请求头携带。

Token 过期或无效时返回:

{ "code": 401, "message": "未登录或 Token 已过期", "data": null }

权限不足时返回:

{ "code": 403, "message": "无权访问该资源", "data": null }

错误码

code 含义 场景
0 成功 —
400 请求参数错误 缺少必填字段、格式不合法
401 未认证 Token 缺失、过期、无效
403 无权访问 访问不属于自己的资源
404 资源不存在 工程/任务/素材 ID 不存在
409 冲突 用户名或邮箱已注册
429 请求过于频繁 触发速率限制
500 服务器内部错误 未预期异常

状态说明

  • 已完成
  • 规划中

基础设施

状态 方法 路径 说明
[x] GET /api/v1/health 健康检查

用户认证

注意: 后端代码实际注册路径为 /auth/*(非 /api/v1/auth/*),前端 Vite 代理及 nginx 均已配置 /auth 转发。

状态 方法 路径 说明
[x] POST /auth/register 用户注册
[x] POST /auth/login 用户登录
[ ] GET /api/v1/auth/me 获取当前用户信息
[ ] PUT /api/v1/auth/password 修改密码

POST /auth/register

{
  "username": "player1",
  "email": "player1@example.com",
  "password": "s3cretP@ss"
}
字段 类型 必填 说明
username string 是 3-32 字符
email string 否 邮箱地址
password string 是 6-64 字符

响应(HTTP 状态码 201):

{
  "code": 0,
  "message": "ok",
  "data": {
    "id": 1,
    "username": "player1"
  }
}

错误响应:

场景 code message
用户名格式不合法(非 3-32 字符) 400 参数错误
密码长度不足 400 参数错误
邮箱格式不合法 400 参数错误
用户名已存在 409 用户名已被注册

POST /auth/login

{
  "username": "player1",
  "password": "s3cretP@ss"
}

响应(Token 在 body 中返回):

{
  "code": 0,
  "message": "ok",
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "expiresIn": 7200,
    "user": {
      "id": 1,
      "username": "player1",
      "email": "player1@example.com",
      "createdAt": "2026-05-24T10:00:00Z",
      "updatedAt": "2026-05-24T10:00:00Z"
    }
  }
}

GET /api/v1/auth/me

需认证。响应:

{
  "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

需认证。

{
  "oldPassword": "s3cretP@ss",
  "newPassword": "n3wS3cretP@ss"
}

错误响应:

场景 code message
原密码错误 400 原密码不正确

提示词优化

将用户标签和原始描述送入 LLM,生成规范化三段式提示词。前端组件收集用户标签和原始提示词后,调用本接口获取优化结果,用于预览或直接发起素材生成。

状态 方法 路径 说明
[x] POST /api/v1/prompt/optimize 提示词优化

POST /api/v1/prompt/optimize

{
  "tags":      ["像素", "战士", "黑暗"],
  "assetType": "sprite",
  "prompt":    "我要一个拿着大剑的战士角色,背景是黑暗地牢",
  "userNote":  "需要武器发光特效"
}
字段 类型 必填 说明
tags string[] 是 用户选择的风格标签,至少 1 个
assetType string 是 素材类型:sprite / background / ui / animation
prompt string 否 用户输入的原始提示词描述
userNote string 否 额外补充说明

响应:

{
  "code": 0,
  "message": "ok",
  "data": {
    "prompt": "【主题】一个融合像素、战士元素的游戏角色精灵图...\n【风格】色彩鲜明;细节丰富...\n【技术】输出格式: spritesheet...",
    "rawText": "【主题】一个融合像素..."
  }
}

链路:

POST /api/v1/prompt/optimize
  → handler.PromptOptimize 解析请求
    → service.RunPromptAgent 执行 Eino Chain
      → formatMetaPrompt:  构建元提示词(角色设定 + 标签 + 原始描述 + 素材类型)
      → llmRefine:        调用 OpenAI 兼容 Chat Completions API
                            └── 无 API key → 回退模板生成
    → 返回 PromptAgentOutput { prompt, rawText }

工程管理

需认证。以下接口均需通过 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

响应:

{
  "code": 0,
  "message": "ok",
  "data": {
    "total": 3,
    "projects": [
      {
        "id": "proj_abc123",
        "name": "我的像素游戏",
        "createdAt": "2026-05-24T10:00:00Z"
      }
    ]
  }
}

POST /api/v1/projects

{
  "name": "我的像素游戏"
}

响应:

{
  "code": 0,
  "message": "ok",
  "data": {
    "id": "proj_abc123",
    "name": "我的像素游戏",
    "style": { "kvPairs": {} },
    "createdAt": "2026-05-24T10:00:00Z"
  }
}

GET /api/v1/projects/:projectId

响应:

{
  "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

级联删除工程下的所有任务、素材及七牛云对象。

响应:

{
  "code": 0,
  "message": "ok",
  "data": null
}

GET /api/v1/projects/:projectId/tasks

参数 类型 说明
page int 页码,默认 1
pageSize int 每页条数,默认 20

响应:

{
  "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):

{
  "kvPairs": {
    "artStyle": "pixel",
    "palette": "warm",
    "lineWeight": "thin",
    "lighting": "bright"
  }
}

素材生成

需认证。任务归属于当前用户的工程,跨用户访问返回 403。

状态 方法 路径 说明
[x] POST /api/v1/generate 提交生成任务,返回 taskId
[x] GET /api/v1/tasks/:taskId 查询任务状态与进度
[x] GET /api/v1/tasks/:taskId/assets 获取生成结果(素材列表 + 元数据)
[ ] WS /api/v1/tasks/:taskId/ws WebSocket 实时进度推送
[x] GET /api/v1/assets/download 下载素材(重定向到 CDN)

POST /api/v1/generate

请求体对应后端 PipelineInput:

{
  "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 是 用户原始文本,后端 PromptOptimizer/PromptAgent 负责提示词优化
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

响应:

{
  "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

响应:

{
  "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 时有值

状态机与管线阶段详见 异步任务。

错误响应:

场景 code message
taskId 不存在 404 任务不存在
任务不属于当前用户 403 无权访问该任务

GET /api/v1/tasks/:taskId/assets

任务状态为 completed 时返回素材列表,其他状态返回空数组。

{
  "code": 0,
  "message": "ok",
  "data": {
    "assets": [
      {
        "key": "generation/proj_abc123/task_xyz789/0.png",
        "url": "https://cdn.example.com/generation/proj_abc123/task_xyz789/0.png",
        "format": "png",
        "width": 256,
        "height": 64,
        "metadata": {
          "frameWidth": 64,
          "frameHeight": 64,
          "frameCount": 4,
          "directions": 1
        }
      }
    ]
  }
}
字段 类型 说明
key string 七牛云对象存储 Key
url string CDN 直接访问 URL
format string 图片格式(png 等)

GET /api/v1/assets/download

公开接口(无需认证)。返回 302 重定向到七牛云 CDN URL。前端可通过此接口加载素材图片,作为 CDN 直接访问的替代方案。

参数 类型 说明
key string 对象存储 Key,查询参数

响应:

  • HTTP 302,Location 头指向 CDN URL
  • 400:缺少 key 参数
  • 500:生成下载链接失败

WebSocket 消息格式

连接路径:ws://host/api/v1/tasks/:taskId/ws(通过 httpOnly Cookie 自动携带认证信息)

interface PipelineProgress {
  stage: 'prompt_optimizer' | '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 时返回
}

消息示例(正常流程):

{"stage":"prompt_optimizer","status":"running","progress":0}
{"stage":"prompt_optimizer","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":[...]}}

消息示例(质检重试):

{"stage":"quality_supervisor","status":"running","progress":0}
{"stage":"quality_supervisor","status":"failed","progress":100,"retryCount":1,"rejectReason":"风格不一致"}
{"stage":"prompt_optimizer","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":[...]}}