# 后端 API 说明 本文档按 `backend/service/app.py` 和 `backend/service/schemas.py` 当前代码整理。 ## 基础约定 - 默认后端地址:`http://localhost:8000` - 请求体中上传文件使用 `multipart/form-data` - `params` 字段是 JSON 字符串,顶层必须是对象 - 任务状态存在内存中(`JobManager`),服务重启后状态会丢失;文件仍保留在 `service_workspace` - CORS 已开启,允许所有来源 ## Jobs ### GET `/api/health` 健康检查。 ```json {"ok": true} ``` ### GET `/api/jobs` 返回任务状态列表,最新任务在前。除内存中登记的任务外,还会扫描 `service_workspace` 补充磁盘上已完成但未登记的任务(有 `wordcloud_hd.db` 即认为是成功可查找)。 ### POST `/api/jobs` 创建词云任务。 Form 字段: | 字段 | 必填 | 说明 | |------|------|------| | `name_list` | 是 | `.xlsx` 名单文件 | | `mask_image` | IMAGE 模式必填 | `.png` / `.jpg` / `.jpeg` 掩膜 | | `font_file` | 否 | 临时上传字体(`.ttf` / `.ttc` / `.otf`) | | `font_id` | 否 | 使用已上传字体 | | `params` | 否 | JSON 字符串,合并到任务配置 | `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` | 可选;为空返回全部,非空时按 `mode` 匹配 | | `mode` | 可选;`exact`(默认,精确匹配)/ `contains`(子串包含,转义通配符后 `LIKE %name%`) | 返回: ```json { "job_id": "...", "query": "", "mode": "exact", "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}/find` 单任务内查找名字的**受保护接口**,供独立查找页使用(与 `/locations` 响应结构相同)。 - 需要鉴权:`Authorization: Bearer `,token 由 `POST /api/login` 获取 - 查询参数与 `/locations` 一致:`name`、`mode`(`exact` / `contains`) - 无 token 或 token 错误返回 `403` ### POST `/api/login` 管理口令登录,返回查找页 / 订单页共用的鉴权 token。 请求体(JSON): ```json {"password": "管理口令"} ``` - 口令错误返回 `401` - 成功返回:`{"token": "..."}`;后续请求带 `Authorization: Bearer ` - 默认口令 `zhihui2024`,生产可用环境变量 `ORDERS_ADMIN_PASSWORD` 覆盖(见 `backend/service/app.py`) ### 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` | 环间距 | ### POST `/api/exports/ai` 将应用生成的 SVG 转换为 Illustrator 5 兼容的 `.ai` 文件。该接口仅转发到 Compose 内部的 `ai-converter` 服务,不暴露转换容器端口。 请求体: ```json { "svg": "...", "filename": "wordcloud.ai" } ``` - 最大 SVG 大小:25 MB。 - 当前支持路径、`rect`、`circle`、`ellipse`、`line`、纯色填充/描边和仿射变换。 - 不支持图片、普通文字、SVG 图案/裁剪/透明效果时会返回 `422`,不会产生可能失真的 AI 文件。 ## Templates ### GET `/api/templates` 返回后端硬编码模板列表: - `poster_1x2`(竖版手机海报,1080×2160) - `poster_4x5`(社交媒体图,1080×1350) - `poster_1x1`(方形封面,1080×1080) - `poster_3x4`(竖版广告,1080×1440) - `poster_16x9`(横版电商,1920×1080) ## 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` 返回字体列表,包含默认字体项(`__default__`)。 ### POST `/api/fonts` 上传字体。Form 字段: | 字段 | 必填 | 说明 | |------|------|------| | `file` | 是 | 字体文件(`.ttf` / `.ttc` / `.otf`) | | `name` | 否 | 字体名 | ### DELETE `/api/fonts/{font_id}` 删除已上传字体。默认字体不能删除。 ## Line Spacing Analysis ### POST `/api/jobs/{job_id}/analyze-line-spacing` 分析 SVG 词云路径的线距,返回 `LineSpacingAnalysisSummary`。 请求体(JSON): ```json { "percentile": 3, "elementWidth": 100.0, "elementHeight": 100.0, "sampleStep": 2.0 } ``` 响应字段: | 字段 | 说明 | |------|------| | `percentile` | 线距百分位 | | `spacingPx` | 采样线距(像素) | | `spacingMm` | 采样线距(毫米) | | `minSpacingPx` | 最小线距(像素) | | `minSpacingMm` | 最小线距(毫米) | | `curveCount` | 曲线数量 | | `segmentCount` | 线段数量 | | `sourceWidth` / `sourceHeight` | SVG 原始尺寸 | | `elementWidth` / `elementHeight` | 目标元素尺寸 | ## 常见错误 | 场景 | 状态码 | 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` | | 掩膜文件不存在 | 400 | `mask_image must be png/jpg/jpeg` | | job 不存在 | 404 | `job not found` | | 产物未就绪 | 404 | `artifact not ready` | | 文件类型未知 | 404 | `unknown artifact kind` |