Files
wechat_wc/docs/routes/route-r4-upload-wordcloud.md
T

226 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# R4 上传 + 词云生成 + 线稿 + 下单后 WCD 生产任务(feat/r4-upload-wordcloud
**Goal:** 打通用户图片上传、词云任务生成、DIY 贴纸线稿处理、异步任务状态轮询,
并在**下单后把订单对应的设计以 WCD 格式投递到词云平台**形成生产任务;
这是四条路线中唯一复用 `wordcloud` 项目的路线,必须通过冻结接口契约的方式隔离它的大量变更。
**Architecture:** 小程序只与 `wxmp_backend` 通信;wxmp_backend 实现 COS 上传凭证、词云适配器、
sketch 接口、任务记录/轮询,以及**由设计数据构造 `.wcd` 包并投递给词云平台**的派单服务;
`wordcloud` FastAPI 服务作为外部引擎,只暴露并冻结最小契约。
小程序页面对词云使用轮询进度,不使用 SSE/EventSource。
**Tech Stack:** Taro 3.6 + React 18 + TypeScriptNestJS 11 + Prisma/PostgreSQL + Redis/BullMQ
腾讯云 COSwordcloud FastAPI + EfficientWordCloud。
## 1. 本分支要开发的内容
### 前端 `wechat_wc`
| 文件 | 职责 |
|---|---|
| `src/utils/api/upload.ts` | `getUploadCredentials/uploadToCos/sketchImage/createWordCloudJob/getWordCloudJob/getWordCloudResult` |
| `src/pages/wordcloud/index.tsx` | 真实上传底图、提交名字清单、创建任务、轮询进度、展示结果图并保存相册 |
| `src/pages/diy/stickerEdit/index.tsx` | 把占位域名 `https://your-api-domain.com/api/sketch` 换成 `BASE_URL/api/sketch`;保留前端灰度降级 |
| `src/types/index.ts``src/types/upload.ts` | `WordCloudJob/WordCloudStatus/SketchResult/CosCredentials` |
| `src/utils/request.ts` 或新增 `upload.ts` | 上传文件统一带 token 与业务 header |
| `src/types/index.ts`designData | 按 `docs/design-data-contract-v1.md` 冻结结构补充字段(详见下文 R2 依赖) |
> 下单后的 WCD 投递完全发生在后端,**前端不新增页面与接口**;前端只需保证
> `designData` 按冻结契约携带持久图片 URL、布局与名单快照。
### 后端 `wxmp_backend`
| 模块 | 内容 |
|---|---|
| `upload` | `GET /api/upload/credentials?key=...` 返回 STS 临时凭证或 COS 预签名 URL;`Upload` 表记录 |
| `wordcloud`(新增) | `POST /api/wordcloud/generate``GET /api/wordcloud/jobs/:id``GET /api/wordcloud/jobs/:id/result` |
| `wordcloud` 适配器 | 把小程序传入的底图+名字列表转成 wordcloud 契约请求;任务状态入库,用户归属校验 |
| **`wordcloud` 派单服务(新增)** | `buildWcdPackage(designData)` 构造 `.wcd``dispatchToWordcloud(orderId)` 投递 → 落 `CustomizationTask` → 轮询产物并转存 COS(见 §3) |
| `sketch`(新增) | `POST /api/sketch` 接收图片,返回处理后图片 URL;内部可先做基础处理或接外部 AI |
| `queue` | BullMQ 消费者把 wordcloud/sketch/生产派单任务状态同步到 `CustomizationTask`/`WordCloudJob` |
| `docs/wordcloud-contract.md` | 冻结 wordcloud 接口,含**可选的 WCD 任务输入**,含版本号 |
| `config` | `.env` 增加 `WORDCLOUD_API_URL`(词云平台地址),见 §3.4 |
| `prisma` | `CustomizationTask` 增加 `orderId`/`wordcloudJobId`migration |
### 词云项目 `wordcloud`
- 需要冻结的接口:`POST /api/jobs`(现有:`name_list(.xlsx)` 必填;**新增可选 `wcd_file`**)、
`GET /api/jobs/{id}``GET /api/jobs/{id}/result``GET /api/jobs/{id}/files/{kind}`
- 其余 canvas、assets、projects、templates 等接口继续按它自己的节奏演进,小程序后端不依赖。
- 若契约不变,wordcloud 内部重构无需通知 R4;契约变更时先升版本号,R4 单独出适配器更新。
## 2. 词云任务模型
小程序侧采用异步任务模型:
```ts
interface WordCloudJob {
id: string
status: 'queued' | 'running' | 'success' | 'failed'
progress: number
imageUrl?: string
error?: string
}
```
流程:
1. 前端上传底图,拿到 COS URL 或临时文件。
2. `POST /api/wordcloud/generate` 提交底图 + 名字文本,后端创建任务并返回 `jobId`
3. 前端每 1-2 秒 `GET /api/wordcloud/jobs/:id` 轮询,`success` 后取 `imageUrl`
4. 结果图建议由 wxmp_backend 转存 COS 后返回微信可下载域名链接。
## 3. 下单后生产任务:WCD 投递(本分支新增功能)
> 场景:用户在 DIY 过程中生成的词云图、贴纸布局最终落在一条设计清单(`design-list`)里。
> 下单之后,订单对应的整套设计必须能**原样恢复到词云平台**,形成可追溯、可复用的
> **生产任务**(用于后续加工/激光雕刻)。传输格式采用词云平台已定义的 `.wcd`
> 画布导入导出包(Zip`manifest.json` + `document.json` + `assets/`)。
### 3.1 端到端链路
```
设计清单 designDataR2 按 design-data-contract-v1.md 冻结结构保存)
→ R3 POST /api/orders 创建订单
→ 订单进入生产(PENDING → PROCESSING;支付未配置期可走幂等触发,见 3.3)
→ queue 入队 customization 生产任务
→ WordCloudService.dispatchToWordcloud(orderId)
1. 读订单关联 design-list 的 designData
2. 贴纸图片持久化(R4 职责,契约束 #1):本地 `wxfile://`/`tmp` 图 → 上传 COS → 回写 `stickers[].src` 为持久 URL
3. buildWcdPackage(designData) → 内存构造 .wcd(布局 → document.json,图片字节 → assets/
4. POST {WORDCLOUD_API_URL}/api/jobs multipart wcd_file + params
5. 落 CustomizationTask{ orderId, wordcloudJobId, status },开轮询
6. 轮询 GET /api/jobs/{id}/result → 产物(PNG/SVG)转存 COS
→ 更新 CustomizationTask.status / resultUrl
```
小程序前端永不接触 wordcloudWCD 完全由 wxmp_backend 构造与投递。
### 3.2 WCD 包结构(wxmp_backend 构造,wordcloud 侧已能消费)
后端按 `designData` 构造以下 Zip
```
{orderNo}.wcd
├── manifest.json // format: "wordcloud-canvas", version: 1
│ // + canvas{width,height,background} + assets[ id/name/type/mimeType/sha256/size ]
├── document.json // CanvasDocumentwidth/height/background/layers/elements
└── assets/<assetId>.<ext> // 每个 sticker/底图的图片字节(从持久 URL 下载)
```
映射规则(以词云平台 `/api/design-templates/import` 现有实现为准):
- `document.canvas` ← 商品 `category.mask` 尺寸 + 背景色。
- `elements[]` ← 底图(`background.src`)作为一个 element;每个贴纸
`{ type:'sticker', assetId: asset-N, x, y, width, height, rotation?, opacity? }`
`assetId` 用包内临时 ID。
- `assets/` ← 每个贴纸图片 + 底图的字节,文件名以包内 `assetId` 开头
wordcloud 解包根据 `assets/<assetId>*` 匹配)。
- 名单快照(`designData.wordcloud.names`)写入 `manifest` 的自定义 `meta` 字段,
仅作记录,不影响导入还原。
### 3.3 触发与幂等
- 推荐:订单进入 `PROCESSING` 时(支付回调确认后)入队 `customization` 任务,
由处理器调用派单服务。
- 支付未配置期:提供 `POST /api/orders/:id/dispatch`(幂等)作为联调/运营触发手段,
同一订单只投递一次(以 `CustomizationTask.orderId` 唯一或状态机约束)。
### 3.4 环境变量(wxmp_backend
```dotenv
# ── 词云平台(WCD 生产任务)────────────────────────────
# 词云服务(FastAPI)地址;未配置时下单后的 WCD 派单返回“未配置”,不做假成功
WORDCLOUD_API_URL=
# 请求 wordcloud 超时(毫秒);可选
WORDCLOUD_TIMEOUT_MS=30000
```
`config/configuration.ts` 增加 `wordcloud: { apiUrl, timeoutMs }`
`config/validation.schema.ts` 增加 `WORDCLOUD_API_URL: Joi.string().allow('').default('')`
(允许为空,与"支付密钥未配置是合法状态"一致)。
### 3.5 词云平台需新增的能力
- `POST /api/jobs` 支持可选 `wcd_file`multipart `.wcd`):存在时跳过 `.xlsx` 名单
模式,进入"还原设计 → 生成生产任务"模式;状态机与产物管线复用现有 jobs。
- wordcloud 平台改动点(供其 R4 实现参考,契约先行冻结):
1. `name_list``wcd_file` 二选一(`wcd_file` 对已有调用向后兼容,纯新增)。
2. 解包 `.wcd` 校验 `format/version``assets` 按 SHA-256 去重(复用现有导入逻辑)。
3. `document.json` 归一化后落 `design_documents``sticker.assetId` 重映射为真实素材 ID。
4. 创建生产 job,产物沿用 `GET /api/jobs/{id}/result` + `/files/{kind}`
### 3.6 设计注意事项
#### DO
- DO 由 wxmp_backend 构造并投递 WCDwordcloud 地址、密钥、内部参数对小程序完全不可见。
- DO `wordcloudJobId → orderId/userId` 关联入库,轮询与结果查询带归属校验。
- DO `WORDCLOUD_API_URL` 未配置时返回结构化“未配置”错误(状态字面量 `not_configured`
`api-contract-v1.md` §8),前端给出可理解提示。
- DO 派单幂等:同一订单重复触发只产生一次投递。
- DO 产物由 wxmp_backend 下载转存 COS 后返回公网 URL,不向小程序暴露 wordcloud 内部地址。
- DO 名单/布局等设计快照写入 `designData`(冻结契约),保证下单后可重建 WCD。
- DO WCD 内部的贴纸图片必须来自持久 URL(COS),打包前先下载字节。
- DO 包大小与单账号投递频率限制(wordcloud 为 CPU 密集任务)。
#### DON'T
- DON'T 在小程序前端硬编码 wordcloud 基础地址或直接调用它。
- DON'T 让用户通过猜 `jobId` 读取他人任务结果,所有查询都带 userId 条件。
- DON'T 在密钥/`WORDCLOUD_API_URL` 未配置时静默跳过或假装成功。
- DON'T 用假的 `setInterval` 进度伪装下单后的任务结果;同样不模拟生产派单成功。
- DON'T 把 `.wcd` 文件当最终产物直接返回小程序,它只是 wordcloud 的交换容器。
- DON'T 接受本地临时路径(`wxfile://`/`tmp`)作为 `designData` 中的贴纸图,R4 打包拿不到字节。
### 3.7 验收标准
- 订单进入生产后,wordcloud 平台出现对应 job`queued → running → success`
产物 PNG 可下载并被 wxmp_backend 转存 COS。
- 同一订单重复触发只投递一次。
- `WORDCLOUD_API_URL` 未配置(留空)时,派单返回结构化“未配置”,不落假成功记录。
-`designData` 构造的 `.wcd` 能在 wordcloud `/api/design-templates/import`
同套逻辑下原样还原(素材去重、布局一致)。
- 后端 `npm run build`、前端 `npm run build:weapp` 通过;Swagger 与契约文档一致。
### 3.8 风险
- wordcloud 侧 `POST /api/jobs` 现强制 `name_list`;支持 `wcd_file` 二选一需要
wordcloud 排期改动。契约先冻结,wordcloud 实现可后置,wxmp_backend 按契约先写适配层。
- `designData` 中贴纸图片若仍为本地/临时 URL,WCD 打包会失败——**依赖 R2 冻结持久 URL 规则**。
- 支付未接入期没有自然触发点,需要一个幂等 `dispatch` 接口用于联调。
- `CustomizationTask``orderId`/`wordcloudJobId` 字段,需一次 migration。
- 贴纸 `edits`(亮度/色相/对比度/线稿)暂无法由 WCD `document.json` 表达,本期先不随包携带,
`manifest.meta` 记录并在契约中标注边界。
## 4. 对 R2 的依赖(接口先冻结)
R4 的 WCD 打包依赖 R2 设计清单里保存的 `designData` 结构。**接口需在本分支开工前冻结**,
相关契约见新文档 `docs/design-data-contract-v1.md`(跨路线共享,R2 为数据生产者,R4 为消费者)。
需 R2 配合冻结的要点:
| 项 | 要求 |
|---|---|
| `designData` 结构 | 按 `docs/design-data-contract-v1.md``version/background/wordcloud` 字段;`category.mask` 必须保存 |
| 贴纸图片持久化 | **R4 负责**:下单/派单前把贴纸本地路径上传为 COS 持久 URL 并回写 `src`R2 允许保存时 `src` 暂为本地路径 |
| 名单快照 | `designData.wordcloud.names` **为 optional,小程序可不带**;R4 生成词云后按实际写入,R2 保存/更新清单时保留该组字段,后端白名单放行 |
| 布局完整性 | 贴纸 `rotation`/`zIndex` 本期持久化(现有 `StickerItem` 缺 rotation,需补) |
| `design-list` 白名单 | 后端 items JSON 校验放行上述新字段,单条 ≤1MB 上限按新结构复核 |
决策记录与细则:见 `docs/design-data-contract-v1.md` §6。
## 5. 补充内容
### 合并与依赖
- 分支名:前端与后端均为 `feat/r4-upload-wordcloud`
- 与 R1-R3 并行,不依赖商品/订单链路;后端上传基础设施可为后续 R2 的贴纸图片持久化提供能力。
- 下单后 WCD 派单依赖 R2 冻结的 designData 结构与 R3 的订单状态机。
- wordcloud 契约若未冻结,R4 先完成“契约文档 + 后端适配层”,联调阶段再补真实 job。
### 测试要求
- 后端:上传凭证、大小/类型校验、wordcloud 适配器正常/失败/超时、**WCD 打包与派单、任务归属权限**、sketch 接口。
- 前端:词云上传、轮询进度、fail 重试、结果保存相册;线稿接口失败降级;**下单后派单状态展示**;无 COS/无 WORDCLOUD 配置提示。
- 集成:用固定 wordcloud 部署版本跑一次真实生成端到端 smoke + 一次下单派单 smoke。