# wxmp_backend API 契约 v1 本文档是小程序前端 `wechat_wc` 与后端 `wxmp_backend` 共同遵守的接口契约。 后端 Swagger 只展示实现,本文档才是前后端开发、评审、联调的依据。 契约变更必须升版本号(v2...),禁止在同一版本内悄悄改字段或语义。 ## 1. 通用约定 - 基础路径:`/api`,例如 `POST /api/auth/login`。 - 默认鉴权:除 `@Public()` 外所有接口要求 `Authorization: Bearer `。 - 统一响应:`{ 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?: { 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(禁止 `wxfile://`/`tmp`)、保留 `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)。 ### 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;失败时前端降级本地灰度。 ### POST /api/orders/:id/dispatch(R4 新增,下单后 WCD 派单触发) 幂等触发把订单对应设计的 `.wcd` 投递到词云平台形成生产任务。 请求体:`{}`(幂等键 `requestId` 可选)。 响应 `data`: ```ts { 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`。