docs: add API and wordcloud contract v1

This commit is contained in:
2026-08-10 02:08:50 +08:00
parent 1d946046d2
commit d39c619c41
2 changed files with 415 additions and 0 deletions
+306
View File
@@ -0,0 +1,306 @@
# wxmp_backend API 契约 v1
本文档是小程序前端 `wechat_wc` 与后端 `wxmp_backend` 共同遵守的接口契约。
后端 Swagger 只展示实现,本文档才是前后端开发、评审、联调的依据。
契约变更必须升版本号(v2...),禁止在同一版本内悄悄改字段或语义。
## 1. 通用约定
- 基础路径:`/api`,例如 `POST /api/auth/login`
- 默认鉴权:除 `@Public()` 外所有接口要求 `Authorization: Bearer <accessToken>`
- 统一响应:`{ code, message, data }`,成功 `code: 0`,失败 `code` 等于 HTTP 状态码。
- 请求体校验:ValidationPipe 白名单模式,多余字段直接 400。
- 金额:数据库用 `Decimal(10,2)`,对外 JSON 统一输出 `number`(两位小数精度的整数元)。
- 分页:列表接口统一响应 `{ list, total, page, pageSize }`
- 时间:统一 ISO 8601 UTC 字符串。
- 幂等:下单等写接口支持客户端 `requestId`,同一 `requestId` 只生效一次。
- 错误码约定:
| code | 含义 |
|---|---|
| 400 | 参数校验失败 |
| 401 | 未登录 / token 失效 |
| 403 | 已登录但无权限访问他人资源 |
| 404 | 资源不存在 |
| 409 | 唯一冲突 / 重复操作 |
| 503 | 微信或依赖服务暂不可用 |
## 2. 认证与用户(已实现)
### POST /api/auth/login
公开接口。请求体:`{ code: string }``wx.login` 的 code)。
响应 `data`
```json
{
"accessToken": "jwt",
"isNewUser": true,
"nickname": null,
"avatar": null
}
```
`isNewUser=true` 时前端引导补全昵称/头像。
### POST /api/auth/register
公开接口。企业主体可选:换手机号注册,返回 `{ accessToken }`
### GET /api/users/me
返回当前用户。`data` 至少包含 `id``openid``nickname``avatar`
### PATCH /api/users/me
更新当前用户资料。请求体:`{ nickname?: string, avatar?: string }`
## 3. 商品与分类(R1,待实现)
### GET /api/categories
公开接口。返回扁平分类列表,按 `sort` 升序。
### GET /api/products
公开接口。查询参数:
| 参数 | 类型 | 说明 |
|---|---|---|
| `page` | number | 默认 1 |
| `pageSize` | number | 默认 20,上限 100 |
| `categoryId` | string | 可选,分类过滤 |
| `keyword` | string | 可选,名称/描述模糊搜索 |
仅返回 `status=ON_SALE` 的商品。
### GET /api/products/:id
公开接口。返回单个在售商品;不存在返回 404。
### ProductDTO
```ts
{
id: string
name: string
categoryId?: string
price: number
originalPrice?: number
leadTime: string
subtitle?: string
description?: string
story?: string
scene?: string
tags: string[]
specs: [string, string][]
tone: [number, number, number]
mask: { shape: 'rect' | 'circle', width: number, height: number, borderRadius?: number }
images: string[]
iconImg?: string
status: 'ON_SALE'
sort: number
}
```
## 4. 收货地址(R2,待实现完整 CRUD)
### GET /api/addresses
返回当前用户地址列表,默认地址在前。
### POST /api/addresses
请求体:
```ts
{
name: string
phone: string
province: string
city: string
district: string
detail: string
isDefault?: boolean
}
```
`isDefault=true` 时事务内取消其他地址默认标记。
### PATCH /api/addresses/:id
更新本人地址。`isDefault` 同样走默认地址事务。
### PATCH /api/addresses/:id/default
设为默认;非本人资源返回 403。
### DELETE /api/addresses/:id
删除本人地址;若删除的是默认地址,自动将最新一条设为默认。
注意:前端 `AddressItem.region: string[]` 与后端 `province/city/district` 的转换
只允许出现在前端 `src/utils/api/address.ts`
## 5. 设计清单(R2,待实现完整 CRUD)
### GET /api/design-list
返回当前用户设计清单,按 `createdAt` 倒序。
### POST /api/design-list
请求体:
```ts
{
title: string
items: {
productId: string
productName: string
unitPrice: number
count: number
designData?: {
stickers?: unknown[]
category?: unknown
imageSrc?: string
imagePos?: { x: number; y: number; scale: number }
}
}[]
}
```
`items` 为服务端 JSON,需做结构白名单与大小校验(单条 ≤ 1MB)。
### PATCH /api/design-list/:id
更新本人清单:`items``title`、状态迁移。
### DELETE /api/design-list/:id
删除本人清单。批量删除建议 `POST /api/design-list/batch-delete` 请求体 `{ ids: string[] }`
### 状态映射
| 前端显示 | 前端状态码 | 后端 DesignListStatus |
|---|---|---|
| 待设计 | `undesigned` | `DRAFT` |
| 设计中 | `designing` | `SUBMITTED` |
| 生产中 | `processing` | `PROCESSING` |
| 已下单 | `ordered` | `DONE` |
`ordered``orderId != null` 派生,前端不提交该状态。
## 6. 订单(R3,待实现)
### POST /api/orders
请求体:
```ts
{
designListId?: string
addressId: string
requestId?: string
items: [{ productId: string, quantity: number }]
}
```
服务端职责:
- 按 Product 真实价格与设计清单快照重算 `totalAmount`,忽略客户端金额。
- `prisma.$transaction` 创建 Order + OrderItem + Payment(PENDING)。
- 生成唯一 `orderNo``requestId` 幂等,重复请求返回已创建订单。
- 收货地址同步快照到 `addressSnapshot`,订单创建后不受地址变更影响。
响应 `data`
```ts
{
id: string
orderNo: string
status: 'PENDING'
totalAmount: number
items: [{ productId, name, price, quantity }]
addressSnapshot: object
createdAt: string
}
```
### GET /api/orders
参数:`status`(可选)、`page``pageSize`。只返回本人订单。
### GET /api/orders/:id
仅本人可查,越权 403,不存在 404。
### PATCH /api/orders/:id/confirm
确认收货,`SHIPPED -> COMPLETED`
### POST /api/orders/:id/cancel
`PENDING` 可取消,`PENDING -> CANCELLED`
### 订单状态机
`PENDING -> PAID -> PROCESSING -> SHIPPED -> COMPLETED``PENDING -> CANCELLED`
前端只读展示,状态迁移由后端与支付回调驱动。
## 7. 支付(R3,占位)
### POST /api/payments/:orderId/pay
支付密钥未配置时返回:
```json
{ "configured": false, "message": "支付未配置" }
```
不调用微信、不写成功记录、不使用默认商户参数。
### POST /api/payments/notify
公开接口,保留原始 body 与 headers 签名;本版本不实现验签与落库。
## 8. 上传 / 词云 / 线稿(R4,待实现)
### GET /api/upload/credentials?key=...
返回 COS STS 临时凭证或预签名 URL。小程序不得持有永久密钥。
### POST /api/wordcloud/generate
multipart 请求:`image`(底图)+ `names`(文本名单)+ `params?`
返回:
```ts
{ jobId: string }
```
### GET /api/wordcloud/jobs/:id
```ts
{
id: string
status: 'queued' | 'running' | 'success' | 'failed'
progress: number
imageUrl?: string
error?: string
}
```
仅本人可查;小程序使用轮询,不使用 SSE。
### POST /api/sketch
multipart`image`。返回处理后图片 URL;失败时前端降级本地灰度。
## 9. 契约维护规则
- 前端与后端同名分支成对开发:`feat/r1-catalog``feat/r2-address-design``feat/r3-order-pay``feat/r4-upload-wordcloud`
- 每个 PR 必须同步更新本文档对应章节与前端 `src/types`
- 契约字段只能增加(optional),不能改名、改类型、改语义;否则升版本。
- 与 wordcloud 之间的契约见 `docs/wordcloud-contract.md`
+109
View File
@@ -0,0 +1,109 @@
# wordcloud 外部服务契约 v1
本文档冻结 `wxmp_backend``/Users/broccoli/Project/wordcloud` 之间依赖的最小接口。
wordcloud 项目内部仍在大量变更,但只要不违反本文档,`wxmp_backend` 的适配层不受影响。
## 1. 版本与状态
- 契约版本:`v1`
- 状态:*.xlsx 名单模式冻结;`names` 直接文本/JSON 模式为推荐扩展点,尚未冻结。
- 服务地址:由 `wxmp_backend` 环境变量配置,禁止硬编码到代码或小程序前端。
- 小程序前端永远不直接访问 wordcloud,只访问 `wxmp_backend`
## 2. 冻结接口
### POST /api/jobs
创建词云任务,`multipart/form-data`
| 字段 | 必填 | 说明 |
|---|---|---|
| `name_list` | 是 | `.xlsx` 名单文件,v1 必填 |
| `mask_image` | IMAGE 模式必填 | `.png/.jpg/.jpeg` 掩膜 |
| `font_file` | 否 | `.ttf/.ttc/.otf` 临时字体 |
| `font_id` | 否 | 已上传字体 id |
| `params` | 否 | JSON 字符串,顶层必须是对象 |
`params` 关键值:
```json
{
"MODE": "IMAGE",
"DATA_COL_INDEX": 1,
"SEED": 42,
"N_REPETITIONS": 20,
"ENABLE_STROKE_WEIGHTS": false,
"FONT_COLOR": "#000000"
}
```
响应:`{ "job_id": "..." }`
### GET /api/jobs/{job_id}
返回:
```json
{
"job_id": "...",
"status": "queued|running|success|failed",
"stage": "...",
"progress_percent": 0,
"message": "...",
"created_at": "...",
"updated_at": "...",
"artifacts": {},
"error": ""
}
```
### GET /api/jobs/{job_id}/result
成功时返回产物地址:
```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`。wxmp_backend 只消费 `png` 并把结果转存 COS。
## 3. 状态语义
| status | 含义 | 小程序映射 |
|---|---|---|
| `queued` | 已入队,未开始 | `queued` |
| `running` | 生成中 | `running` |
| `success` | 完成 | `success` |
| `failed` | 失败 | `failed` |
进度以 `progress_percent` 为准,`wxmp_backend` 原样透传;小程序轮询间隔 1-2 秒。
## 4. 错误语义
- 4xx:参数错误,错误信息在 `detail``message`
- 5xx:服务/任务异常,wxmp_backend 将任务标记 `failed` 并在超时后清理。
- wxmp_backend 适配层应设置请求超时(例如 30s),任务超时上限(例如 10 分钟)。
## 5. 稳定性规则
- 上述 4 个端点的路径、字段名、状态枚举、分页/轮询语义冻结为 v1。
- wordcloud 内部重构、新增 canvas/assets/projects/templates 能力不影响本契约。
- 契约变更流程:定义 v2 -> 更新本文档 -> wxmp_backend 在 R4 分支升级适配器 -> 双版本并存一个发布周期。
- 若 wordcloud 希望新增“直接传名字列表”能力,默认视为 v1 兼容扩展,字段必须是 optional。
## 6. 集成要点
- `wxmp_backend` 负责把小程序文本名单转换为 `.xlsx` 再调用 `POST /api/jobs`
- `wxmp_backend` 保存 `job_id -> userId` 映射,轮询与结果查询必须校验归属。
- 结果图由 wxmp_backend 下载并转存 COS,返回给小程序的是 COS 公网 URL。
- wordcloud 若对外暴露公网,需要加访问 token 或网络白名单,防止被直接刷任务。