# 后端 API 说明 本文档按 `backend/service/app.py` 和 `backend/service/schemas.py` 当前代码整理。 ## 基础约定 - 默认后端地址:`http://localhost:8000` - 请求体中上传文件使用 `multipart/form-data` - `params` 字段是 JSON 字符串,顶层必须是对象 - 任务状态存在内存中,服务重启后状态会丢失 ## Jobs ### GET `/api/health` 返回: ```json {"ok": true} ``` ### GET `/api/jobs` 返回内存中的任务状态列表,最新任务在前。 ### POST `/api/jobs` 创建词云任务。 Form 字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `name_list` | 是 | `.xlsx` 名单文件 | | `mask_image` | IMAGE 模式必填 | `.png` / `.jpg` / `.jpeg` 掩膜 | | `font_file` | 否 | 临时上传字体,支持后端 `_FONT_EXTENSIONS` 中的格式 | | `font_id` | 否 | 使用已上传字体 | | `params` | 否 | JSON 字符串,合并到任务配置 | 字体格式当前支持 `.ttf`、`.ttc`、`.otf`。 `params` 示例: ```json { "MODE": "IMAGE", "DATA_COL_INDEX": 1, "SEED": 42, "N_REPETITIONS": 20, "ENABLE_STROKE_WEIGHTS": false, "FONT_COLOR": "#000000" } ``` 响应: ```json {"job_id": "..." } ``` ### GET `/api/jobs/{job_id}` 返回 `JobStatus`: ```json { "job_id": "...", "status": "queued|running|success|failed", "stage": "...", "progress_percent": 0, "message": "...", "created_at": "...", "updated_at": "...", "artifacts": {}, "error": "" } ``` ### GET `/api/jobs/{job_id}/detail` 返回状态和最近事件: ```json { "status": {}, "recent_events": [] } ``` ### GET `/api/jobs/{job_id}/events` SSE 事件流。事件数据模型: ```json { "type": "log|status", "stage": "placing_words", "progress_percent": 65, "message": "...", "timestamp": "..." } ``` ### GET `/api/jobs/{job_id}/result` 返回可下载产物 URL: ```json { "job_id": "...", "status": "success", "image_url": "/api/jobs/{job_id}/files/png", "svg_url": "/api/jobs/{job_id}/files/svg", "svg_stroke_url": "/api/jobs/{job_id}/files/svg_stroke", "db_url": "/api/jobs/{job_id}/files/db", "metrics_url": "/api/jobs/{job_id}/files/metrics" } ``` ### GET `/api/jobs/{job_id}/files/{kind}` 下载产物。`kind` 支持: - `png` - `svg` - `svg_stroke` - `db` - `metrics` ### GET `/api/jobs/{job_id}/locations` 查询词语位置。查询参数: | 参数 | 说明 | | --- | --- | | `name` | 可选;为空返回全部,非空精确匹配 | 返回: ```json { "job_id": "...", "query": "", "total": 1, "canvas_width": 8000, "canvas_height": 4000, "matches": [ { "id": 1, "name": "张三", "x": 100, "y": 200, "font_size": 64, "color": "#000000", "orientation": "horizontal", "box_x": 100, "box_y": 200, "box_width": 120, "box_height": 50 } ] } ``` ### GET `/api/jobs/{job_id}/occupancy_mask` 返回 PNG,显示每个已放置词语的 bounding box。 ### GET `/api/jobs/{job_id}/custom.svg` 按已生成 DB 重新导出 SVG。 查询参数: | 参数 | 默认 | 说明 | | --- | --- | --- | | `fill` | `fill` | `fill` / `dot` / `line` / `ring` | | `stroke` | `0` | 是否描边 | | `spacing` | `10` | 点阵间距 | | `radius` | `2` | 点阵半径 | | `color` | `#000000` | 输出颜色 | | `line_spacing` | `6` | 线填充间距 | | `line_width` | `1` | 线宽 | | `line_angle` | `0` | 线角度 | | `ring_radius` | `3` | 环半径 | | `ring_width` | `1` | 环线宽 | | `ring_spacing` | `8` | 环间距 | ## Templates ### GET `/api/templates` 返回后端硬编码模板列表: - `poster_1x2` - `poster_4x5` - `poster_1x1` - `poster_3x4` - `poster_16x9` ## Assets ### POST `/api/assets` 上传素材。Form 字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `file` | 是 | 素材文件 | | `name` | 否 | 名称 | | `type` | 否 | 默认 `upload` | ### POST `/api/assets/from-job/{job_id}` 从任务产物导入素材。Form 字段: | 字段 | 默认 | 说明 | | --- | --- | --- | | `kind` | `png` | 产物类型 | | `name` | 空 | 素材名称 | | `type` | `wordcloud` | 素材类型 | ### GET `/api/assets` 列出素材。支持查询参数: - `type` - `job_id` ### GET `/api/assets/{asset_id}` 获取素材元数据。 ### GET `/api/assets/{asset_id}/download` 下载素材文件。 ### DELETE `/api/assets/{asset_id}` 删除素材。 ## Projects ### POST `/api/projects` Form 字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `name` | 是 | 工程名 | | `template_id` | 是 | 模板 ID | | `background_color` | 是 | `#RRGGBB` | | `stickers` | 否 | JSON 数组字符串 | ### GET `/api/projects` 返回工程摘要列表。 ### GET `/api/projects/{project_id}` 返回工程完整数据。 ### PATCH `/api/projects/{project_id}` 按传入 Form 字段部分更新工程。 ### DELETE `/api/projects/{project_id}` 删除工程。 ## Fonts ### GET `/api/fonts` 返回字体列表,包含默认字体项。 ### POST `/api/fonts` 上传字体。Form 字段: | 字段 | 必填 | 说明 | | --- | --- | --- | | `file` | 是 | 字体文件 | | `name` | 否 | 字体名 | ### DELETE `/api/fonts/{font_id}` 删除已上传字体。默认字体不能删除。 ## 常见错误 | 场景 | 状态码 | detail | | --- | --- | --- | | `params` 不是合法 JSON | 400 | `params must be valid JSON` | | `params` 不是对象 | 400 | `params must be JSON object` | | `name_list` 非 `.xlsx` | 400 | `name_list must be xlsx` | | IMAGE 模式缺少掩膜 | 400 | `mask_image is required when MODE=IMAGE` | | job 不存在 | 404 | `job not found` | | 产物未就绪 | 404 | `artifact not ready` | | 文件类型未知 | 404 | `unknown artifact kind` |