10 KiB
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:
{
"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 的商品。
响应 data:{ list: ProductDTO[], total: number, page: number, pageSize: number }。
GET /api/products/:id
公开接口。返回单个在售商品;不存在返回 404。
ProductDTO
{
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
请求体:
{
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 倒序。
GET /api/design-list/:id
返回单条设计清单(后端已实现 @Get(':id');R3 结算页 fetchDesign 消费)。仅本人可查,越权 403 / 不存在 404。
POST /api/design-list
请求体:
{
title: string
items: {
productId: string
productName: string
unitPrice: number
count: number
designData?: {
version?: 1
category?: { id: string; mask: object; tone?: number[] }
background?: { src: string; color?: string; pos?: { x: number; y: number; scale: number } }
wordcloud?: { jobId?: string; imageUrl: string; names: string[] }
stickers?: unknown[]
imageSrc?: string // 兼容旧版
imagePos?: { x: number; y: number; scale: number } // 兼容旧版
}
}[]
}
designData 结构遵循
wechat_wc/docs/design-data-contract-v1.md(R2/R4 冻结契约)。 该契约保证 R4 下单后能据此构造.wcd投递到词云平台。要点: 贴纸图src在派单前必须为 COS 持久 URL;R2 保存/更新清单时允许暂存wxfile:///tmp本地路径,由 R4 在下单/派单前上传 COS 并回写 (design-data-contract-v1.md 约束#1、决策#4,2026-08-12 冻结); 保留wordcloud分组、保留category.mask;后端该 JSON 白名单须放行version/background/wordcloud/rotation/zIndex。items为服务端 JSON,需做结构白名单与大小校验(单条 ≤ 1MB)。实现补充(R2,非契约变更):服务端 JSON body 传输上限为 2MB(
main.ts,Nest 默认 100KB 会使 1MB 业务限制不可达)。三层边界:≤1MB 正常受理;1MB~2MB 由 design-list service 返回 400「单条设计数据超过 1MB 上限」;>2MB 返回 413「请求体过大」。文件上传(R4 multipart) 不走此限制,沿用各模块独立校验(如底图 ≤10MB)。实现补充(DIY 修正,非契约变更):
stickers[].width/height语义为「画布显示像素」 (画布坐标空间 =category.mask尺寸,x/y允许超出画布边界,渲染端裁切、WCD 按原值还原), 渲染、碰撞检测、WCD 打包三方按同一语义消费; 历史数据中的原始像素值由读取端按 mask 归一化兼容。详见 design-data-contract-v1.md 注记。
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
请求体:
{
designListId?: string
addressId: string
requestId?: string
items: [{ productId: string, quantity: number }]
}
服务端职责:
- 按 Product 真实价格与设计清单快照重算
totalAmount,忽略客户端金额。 prisma.$transaction创建 Order + OrderItem + Payment(PENDING)。- 生成唯一
orderNo;requestId幂等,重复请求返回已创建订单。 - 收货地址同步快照到
addressSnapshot,订单创建后不受地址变更影响。
响应 data:
{
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
支付密钥未配置时返回:
{ "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?。
返回:
{ jobId: string }
GET /api/wordcloud/jobs/:id
{
id: string
status: 'queued' | 'running' | 'success' | 'failed'
progress: number
imageUrl?: string
error?: string
}
仅本人可查;小程序使用轮询,不使用 SSE。
POST /api/sketch
multipart:image。返回处理后图片 URL;失败时前端降级本地灰度。
POST /api/orders/:id/dispatch(R4 新增,下单后 WCD 派单触发)
幂等触发把订单对应设计的 .wcd 投递到词云平台形成生产任务。
请求体:{}(幂等键 requestId 可选)。
响应 data:
{
orderId: string
status: 'queued' | 'running' | 'success' | 'failed' | 'not_configured'
wordcloudJobId?: string
message?: string
}
- 同一订单重复调用只投递一次(以
CustomizationTask.orderId唯一或状态约束)。 WORDCLOUD_API_URL未配置时返回status: 'not_configured'与可读message,不做假成功。- 常规路径:订单进入
PROCESSING时由后端队列自动触发;本接口作为支付未配置期的联调/运营手段。 - 词云平台侧契约见
docs/wordcloud-contract.md(POST /api/jobs可选wcd_file)。
环境变量(R4 新增)
| 变量 | 必填 | 说明 |
|---|---|---|
WORDCLOUD_API_URL |
否(为空视为未配置) | 词云平台(FastAPI)地址;未配置时派单返回 not_configured |
WORDCLOUD_TIMEOUT_MS |
否 | 请求 wordcloud 超时(毫秒),默认 30000 |
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。