Files
wxmp_backend/docs/api-contract-v1.md
T
lhmin0604andClaude 0a3c8e834d feat(r2): 地址与设计清单完整 CRUD(事务/归属校验/状态机)
addresses:
- 补 PATCH /:id、DELETE /:id(契约 §4 全路由齐备)
- 默认地址唯一性事务:create/update/setDefault 先清旧默认再写入
- 首个地址自动设为默认;删除默认地址事务内补偿最新一条
- 归属校验统一:不存在 404,非本人 403

design-list:
- 补 PATCH /:id、DELETE /:id、POST /batch-delete(宽松语义返回 deleted 数)
- items 强制恰好 1 个元素(一条设计=一条清单,阶段0 决策#1)
- designData 白名单 7 键放行、单条 ≤1MB 校验(400)
- 单向状态机 DRAFT→SUBMITTED→PROCESSING→DONE,回退/跳级 400
- PATCH 部分更新语义,不传 items 不清 designData(R4 wordcloud 保护)

infra:
- main.ts: JSON body 上限 100KB→2MB,使契约 1MB 设计数据可达(>2MB 返回 413)
- exceptions filter: body-parser entity.too.large 映射 413
- api-contract-v1.md §5 补实现说明(非契约变更)

验证:nest build 通过;本地 3091 实例 + mock 登录实测 35 项全过
(CRUD/默认地址补偿/越权 403/404/状态机/1MB 边界/413/未登录 401)

Co-Authored-By: Claude <noreply@anthropic.com>
2026-09-12 02:37:53 +08:00

9.6 KiB
Raw Blame History

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 至少包含 idopenidnicknameavatar

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

{
  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 倒序。

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.mdR2/R4 冻结契约)。 该契约保证 R4 下单后能据此构造 .wcd 投递到词云平台。要点: 贴纸图 src 必须为 COS 持久 URL(禁止 wxfile:///tmp)、保留 wordcloud 分组、 保留 category.mask;后端该 JSON 白名单须放行 version/background/wordcloud/rotation/zIndexitems 为服务端 JSON,需做结构白名单与大小校验(单条 ≤ 1MB)。

实现补充(R2,非契约变更):服务端 JSON body 传输上限为 2MBmain.tsNest 默认 100KB 会使 1MB 业务限制不可达)。三层边界:≤1MB 正常受理;1MB~2MB 由 design-list service 返回 400「单条设计数据超过 1MB 上限」;>2MB 返回 413「请求体过大」。文件上传(R4 multipart) 不走此限制,沿用各模块独立校验(如底图 ≤10MB)。

PATCH /api/design-list/:id

更新本人清单:itemstitle、状态迁移。

DELETE /api/design-list/:id

删除本人清单。批量删除建议 POST /api/design-list/batch-delete 请求体 { ids: string[] }

状态映射

前端显示 前端状态码 后端 DesignListStatus
待设计 undesigned DRAFT
设计中 designing SUBMITTED
生产中 processing PROCESSING
已下单 ordered DONE

orderedorderId != 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)。
  • 生成唯一 orderNorequestId 幂等,重复请求返回已创建订单。
  • 收货地址同步快照到 addressSnapshot,订单创建后不受地址变更影响。

响应 data

{
  id: string
  orderNo: string
  status: 'PENDING'
  totalAmount: number
  items: [{ productId, name, price, quantity }]
  addressSnapshot: object
  createdAt: string
}

GET /api/orders

参数:status(可选)、pagepageSize。只返回本人订单。

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 -> COMPLETEDPENDING -> 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

multipartimage。返回处理后图片 URL;失败时前端降级本地灰度。

POST /api/orders/:id/dispatchR4 新增,下单后 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.mdPOST /api/jobs 可选 wcd_file)。

环境变量(R4 新增)

变量 必填 说明
WORDCLOUD_API_URL 否(为空视为未配置) 词云平台(FastAPI)地址;未配置时派单返回 not_configured
WORDCLOUD_TIMEOUT_MS 请求 wordcloud 超时(毫秒),默认 30000

9. 契约维护规则

  • 前端与后端同名分支成对开发:feat/r1-catalogfeat/r2-address-designfeat/r3-order-payfeat/r4-upload-wordcloud
  • 每个 PR 必须同步更新本文档对应章节与前端 src/types
  • 契约字段只能增加(optional),不能改名、改类型、改语义;否则升版本。
  • 与 wordcloud 之间的契约见 docs/wordcloud-contract.md