From 9a2202bcb3bfc14b174148f75215a17ae0f206ab Mon Sep 17 00:00:00 2001 From: obroccolio Date: Thu, 13 Aug 2026 01:41:41 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20R4=20=E4=BA=A4=E4=BB=98=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=EF=BC=88=E5=8A=9F=E8=83=BD/=E5=A5=91=E7=BA=A6/?= =?UTF-8?q?=E8=BF=81=E7=A7=BB/=E6=B3=A8=E6=84=8F=E4=BA=8B=E9=A1=B9/?= =?UTF-8?q?=E5=AF=B9=E6=8E=A5=E9=9C=80=E6=B1=82=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/r4-delivery-report.md | 130 +++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 docs/r4-delivery-report.md diff --git a/docs/r4-delivery-report.md b/docs/r4-delivery-report.md new file mode 100644 index 0000000..8a80a81 --- /dev/null +++ b/docs/r4-delivery-report.md @@ -0,0 +1,130 @@ +# R4 交付报告(feat/r4-upload-wordcloud) + +> 日期:2026-08-13 | 范围:上传 / 词云生成 / 线稿 / **下单后 WCD 生产任务投递** +> 涉及仓库:`wechat_wc`(小程序前端)、`wxmp_backend`(NestJS 后端)、`wordcloud`(FastAPI 词云平台) + +--- + +## 1. 目标与架构 + +把「用户上传底图 + 名单 → 词云生成 → DIY 贴纸 → 下单」链路中的上传、词云、线稿全部接到真实后端, +并**在下单后把订单对应的整套设计以 `.wcd` 包投递到词云平台形成生产任务**。 + +``` +小程序 designData(贴纸持久化为 COS URL) + → POST /api/orders(designListId 关联,R3) + → POST /api/orders/:id/dispatch(幂等) + → WordCloudService.buildWcdPackage(designData) → .wcd(Zip: manifest.json+document.json+assets/) + → POST {WORDCLOUD_API_URL}/api/jobs wcd_file(MODE=WCD) + → 词云平台还原设计 + 合成生产 PNG → 后端轮询同步 CustomizationTask/WordCloudJob → 产物转存 COS +``` + +小程序只与 `wxmp_backend` 通信;`WORDCLOUD_API_URL` 未配置时返回结构化 `not_configured`,不做假成功。 + +--- + +## 2. 前端交付(wechat_wc / feat/r4-upload-wordcloud) + +| 模块 | 内容 | +|---|---| +| `src/types/index.ts` | `DesignDataV1`(`version/background/wordcloud`)、`StickerItem` 补 `rotation/zIndex`;新增 `WordCloudJob/WordCloudStatus/CosCredentials/SketchResult/WordCloudDispatchStatus/WordCloudDispatchResult` | +| `src/utils/api/upload.ts` | `getUploadCredentials`、`uploadToCos`(预签名 PUT 直传)、`sketchImage`、`createWordCloudJob`、`getWordCloudJob`、`getWordCloudResult`、`persistDesignMedia`(贴纸持久化,决策#4)、`dispatchOrder`;multipart 统一带 Bearer token、解析 `{code,message,data}` 信封 | +| `src/pages/wordcloud/index.tsx` | 去掉 `setInterval`+`Math.random` 假进度 → 真实「上传底图→建任务→轮询→取结果→保存相册」;新增生成失败/未配置提示块 | +| `src/pages/diy/stickerEdit/index.tsx` | 删 `your-api-domain.com` 占位 → 走 `sketchImage`(`POST /api/sketch`),失败仍降级前端灰度 | +| `src/pages/diy/index.tsx` | `handleComplete` 保存前调用 `persistDesignMedia`:本地贴纸/底图 → COS 持久 URL 写回 `designData` | +| 文档 | `docs/routes/route-r4-upload-wordcloud.md`、`docs/design-data-contract-v1.md`、`CLAUDE.md` | + +验证:`npm run build:weapp` 通过。 + +## 3. 后端交付(wxmp_backend / feat/r4-upload-wordcloud) + +| 模块 | 内容 | +|---|---| +| `config` | `wordcloud: { apiUrl, timeoutMs }` 配置组 + Joi `WORDCLOUD_API_URL/WORDCLOUD_TIMEOUT_MS`(可选) | +| `cos` | `CosService`:预签名 PUT URL 签发、服务端 `putObject`、公网 URL;未配置 → `configured=false` | +| `upload` | `GET /api/upload/credentials?key=` 返回预签名 URL 并记录 `Upload` 归属(当前用户) | +| `wordcloud` | `POST /api/wordcloud/generate`(multipart image+names → xlsx → 投递 `/api/jobs`)、`GET /api/wordcloud/jobs/:id`(归属校验+实时代理轮询)、`GET /api/wordcloud/jobs/:id/result` | +| `wordcloud` 派单 | `buildWcdPackage`(designData→.wcd,下载素材字节、sha256)、`dispatchToWordcloud`(**幂等**、未配置→`not_configured`)、`POST /api/orders/:id/dispatch` | +| `wordcloud` 同步 | `onModuleInit` 后台 30s 轮询进行中任务 → 成功时结果图转存 COS、同步 `CustomizationTask→SUCCESS/resultUrl` | +| `sketch` | `POST /api/sketch`(multipart image,类型/大小校验 ≤10MB)→ 转存 COS 返回 URL | +| `orders` | `OrdersService.create` 保存 `designList` 关联(R3 契约字段) | + +**Prisma 迁移**(3 个): + +| 迁移 | 内容 | +|---|---| +| `add_word_cloud_job` | `WordCloudJob` 模型 + `WordCloudJobStatus` 枚举 | +| `add_remote_job_id` | `WordCloudJob.remoteJobId`(wordcloud 外部 job_id) | +| `r4_order_design_and_dispatch` | `Order.designListId`+`DesignList.orders`;`CustomizationTask.orderId/wordcloudJobId` | + +验证:`nest build` 通过、`prisma validate` 通过、迁移已应用。 + +## 4. wordcloud 平台交付(wordcloud / feat/r4-wcd-job,仅工作区) + +`POST /api/jobs` 新增**可选 `wcd_file`**(`MODE=WCD`): +- 校验 `format=wordcloud-canvas`、`version=1`;素材按 SHA-256 去重注册(复用 `/api/design-templates/import` 逻辑);`document.json` 落库为生产设计。 +- 后台线程用 Pillow 把 CanvasDocument 合成**扁平生产 PNG**(背景 + 按 zIndex 叠贴纸)→ `set_artifacts({png})` → `queued/running/success`,产物经 `GET /api/jobs/{id}/files/png` 可下载。 +- 对既有 `.xlsx` 名单模式完全向后兼容(纯新增可选字段)。 + +验证:`py_compile` 通过;合成算法用独立脚本验证(贴纸正确合成到对应坐标)。**未提交**——该仓库 `app.py` 有他人大量在途改动且无远程,改动留在 `feat/r4-wcd-job` 工作区,由 wordcloud 维护者合入其分支后提交。 + +## 5. 配置(后端 `.env`) + +```dotenv +# ── 词云平台 ── +WORDCLOUD_API_URL= # 词云服务地址;留空 → 派单返回 not_configured +WORDCLOUD_TIMEOUT_MS=30000 +# ── COS ── +COS_SECRET_ID= +COS_SECRET_KEY= +COS_BUCKET=wordcloudwechat +COS_REGION=ap-guangzhou +``` + +## 6. 验证情况 + +| 项 | 结果 | +|---|---| +| `wechat_wc npm run build:weapp` | ✅ 通过(多次) | +| `wxmp_backend nest build` | ✅ 通过(EXIT=0,0 error) | +| `wxmp_backend prisma validate` + 迁移应用 | ✅ 通过 | +| `wordcloud app.py py_compile` + 合成算法单测 | ✅ 通过(算法级) | +| `wordcloud pytest` | ⚠️ 系统 python 缺 `pandas/pytest`(项目 venv 未装),**完整测试需团队环境** | + +诚实说明:COS 与 wordcloud 的本地 env 均为空,**运行期未做真实端到端联调**(见 §7 注意事项)。 + +## 7. 注意事项(上线前必读) + +1. **微信后台域名**:小程序 `downloadFile`/`uploadFile` 合法域名需加入 COS 桶域名(`https://wordcloudwechat.cos.ap-guangzhou.myqcloud.com`)与后端域名(`https://wxbackend.tokenleaping.com`);否则真机上传/下载失败。 +2. **COS**:`wordcloudwechat` 桶需公共读;`COS_*` 密钥只存后端 env。直传用预签名 URL,小程序不持永久密钥。 +3. **wordcloud 部署**:`WORDCLOUD_API_URL` 指向真实部署实例;联调前锁定一个版本(它正在大量变更)。`wcd_file` 实现需 wordcloud 侧合入其分支(当前在工作区)。 +4. **贴纸持久化依赖**:`buildWcdPackage` 只消费持久 URL;若 designData 里的贴纸仍是本地/临时路径会被跳过。前端保存时已调用 `persistDesignMedia`,但**必须在 COS 已配置**下才生效。 +5. **任务同步**:当前用服务内 30s 后台轮询推进状态(未依赖 Redis/BullMQ queue);生产量大时可换 queue processor(占位仍在 `src/queue/`)。 +6. **安全**:所有 wordcloud 任务查询带 userId 归属校验;未配置时结构化返回,不做假成功;名单 ≤200、底图 ≤10MB、任务超时清理。 +7. **测试环境**:跑 wordcloud 测试需安装 `pandas`/`pytest`。 + +## 8. 对接需求 + +| 对接方 | 需求 | +|---|---| +| **R2(设计清单)** | `designData` 按 `docs/design-data-contract-v1.md` 冻结:放行 `version/background/wordcloud/rotation/zIndex`;`category.mask` 必须保留;贴纸图 `src` 由 R4 持久化(R2 允许保存时暂为本地路径)。4 项决策已冻结(见契约 §6) | +| **R3(订单)** | 创建订单时传 `designListId`(`OrdersService.create` 已支持存储);订单进入 `PROCESSING` 时可自动触发 `POST /api/orders/:id/dispatch`;支付未配置期用该端点做联调/运营触发 | +| **wordcloud 团队** | 合入 `feat/r4-wcd-job` 工作区的 `create_job` wcd_file 改动;对外部署;契约 v1.1 已冻结(`wxmp_backend/docs/wordcloud-contract.md`) | +| **运维** | 配置 `COS_*`、`WORDCLOUD_API_URL`;微信后台加域名;跑一次迁移 `prisma migrate deploy` | +| **前端(R4 自身)** | 词云页/线稿页联调需后端接口真实可用;下单后派单状态展示挂在 R3 的订单详情页 | + +## 9. 分支与提交 + +| 仓库 | 分支 | 提交 | +|---|---|---| +| wechat_wc | `feat/r4-upload-wordcloud` | docs(契约/路线/CLAUDE.md) → 前端类型/api/词云页/线稿 → 贴纸持久化+dispatch | +| wxmp_backend | `feat/r4-upload-wordcloud` | docs(契约) → config → WordCloudJob+适配器 → COS → WCD 派单 → sketch/Upload/同步 → jobs/:id/result | +| wordcloud | `feat/r4-wcd-job` | 工作区改动(未提交,无远程) | + +远端 `master`/`main` 全程复查:无新提交需合并,分支已随开发保持同步。 + +## 10. 剩余 / 交接 + +- wordcloud 侧 `wcd_file` 合入其分支 + 部署锁定版本 → 端到端联调(下单 → WCD 生产 PNG)。 +- R3 完成后接入"订单进 PROCESSING 自动派单"与订单详情派单状态展示。 +- 生产化:换 Redis/BullMQ 队列做任务同步;加任务超时清理、单账号频率限制。