docs: add API and wordcloud contract v1
This commit is contained in:
@@ -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`。
|
||||
@@ -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 或网络白名单,防止被直接刷任务。
|
||||
Reference in New Issue
Block a user