375 lines
8.3 KiB
Markdown
375 lines
8.3 KiB
Markdown
# 后端 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>`,token 由 `POST /api/login` 获取
|
||
- 查询参数与 `/locations` 一致:`name`、`mode`(`exact` / `contains`)
|
||
- 无 token 或 token 错误返回 `403`
|
||
|
||
### POST `/api/login`
|
||
|
||
管理口令登录,返回查找页 / 订单页共用的鉴权 token。
|
||
|
||
请求体(JSON):
|
||
|
||
```json
|
||
{"password": "管理口令"}
|
||
```
|
||
|
||
- 口令错误返回 `401`
|
||
- 成功返回:`{"token": "..."}`;后续请求带 `Authorization: Bearer <token>`
|
||
- 默认口令 `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": "<svg ...>...</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` |
|