diff --git a/docs/r2-workflow.md b/docs/r2-workflow.md index aebd2bf..584e045 100644 --- a/docs/r2-workflow.md +++ b/docs/r2-workflow.md @@ -42,6 +42,36 @@ `productId` R2 不做存在性校验(R1 商品表可能未 seed);手机号只做非空字符串校验; 越权 403 / 不存在 404(契约既定)。 +### WCD 格式硬性规定(R2 全程适用,依据 `docs/design-data-contract-v1.md` 冻结版 2026-08-12) + +> R2 是 `designData` 的**生产者**;R4 下单后把它打成 `.wcd` 包(Zip:`manifest.json` + +> `document.json` + `assets/`)投递给词云平台还原整套画布。还原依赖三样东西全部在 +> designData 里:① 画布尺寸 ← `category.mask`;② 贴纸布局 ← `stickers[]` +> (x/y/scale/width/height/rotation/zIndex);③ 贴纸图片字节 ← 每个贴纸的**持久 URL**。 +> R2 保存的每一份 designData 都要为这三样负责,否则 R4 打包直接失败或画布走样。 + +**结构规定(后端白名单已按此实现):** + +1. designData 顶层只允许 **7 个键**:`version` / `category` / `background` / `wordcloud` / + `stickers` / `imageSrc` / `imagePos`(后两个为旧版兼容)。多余顶层键 → 后端 400 + (forbidNonWhitelisted + service 白名单双重拦截)。 +2. `category` 只提交 `{ id, mask, tone? }`(阶段0 决策#2)。`mask` 是 WCD 画布尺寸来源, + **禁止裁掉**——缺 mask 的数据 WCD 只能按产品默认尺寸兜底,画布会走样(契约约束#3)。 +3. `stickers[]` 内字段后端不深校验、原样透传。其中 `rotation` / `zIndex` 本期必须随 DIY + 保存持久化(契约决策#2 冻结,WCD 按 zIndex 排图层,约束#4;前端类型已具备); + `edits`(brightness/hue/contrast/sketchSrc)同样透传,虽然本期不进 WCD document.json。 +4. 贴纸 `src` 允许暂存 `wxfile://` / `tmp` 本地路径——契约约束#1 明确允许 + (R4 在下单/派单前上传 COS 并回写 src)。**R2 任何环节禁止把它当脏数据清洗掉**。 +5. `wordcloud` 分组由 R4 词云生成后写入,R2 任何写入路径**不得丢弃**(约束#2): + 后端 PATCH 已做部分更新语义(不传 items 不清 designData);但前端 `updateDesign` + 是 items 全量替换——页面必须基于服务端最新 designData 合并改动后再整包提交。 +6. 状态机不受本契约影响:`ordered` 仍由 `orderId != null` 派生,前端只读(约束#6)。 + +**边界(本期不做,契约 §4):** 贴纸 edits/线稿不进 WCD `document.json`(仅记 `manifest.meta`); +字体不随包携带;`.wcd` 只做「订单 → 词云平台」单向投递。 + +**涉及阶段:** 阶段 3(三个写入点)、阶段 4(缓存回写),注意事项已插入下文对应位置。 + ## 二、工作流程(按顺序) ### 原阶段 0 任务清单(存档) @@ -102,15 +132,17 @@ |---|---| | `pages/address/index.tsx` | 增删改查、设默认全部走 API;401 由 request 层统一抛出并引导登录;API 失败降级读本地缓存并提示 | | `pages/designList/index.tsx` | 列表/筛选/批量删除走 API;状态筛选基于服务端返回的 `status` 计算,消灭魔法字符串 | -| `pages/product/index.tsx`(加入清单) | `addDesign` 写 API(需登录),失败降级本地 | -| `pages/diy/index.tsx`(保存 designData) | `updateDesign` 走 API;`designData.category` 裁剪为 `{id, mask, tone}`(决策#2);贴纸本地图路径允许暂存(R4 派单前才持久化,契约约束 #1) | -| `pages/diy/stickerEdit/index.tsx` | 同上,保存贴纸改动走 API | +| `pages/product/index.tsx`(加入清单) | `addDesign` 写 API(需登录),失败降级本地。此时尚无 designData,创建后保持 DRAFT | +| `pages/diy/index.tsx`(保存 designData) | `updateDesign` 走 API;`designData.category` 裁剪为 `{id, mask, tone}`(决策#2);贴纸本地图路径允许暂存(R4 派单前才持久化,契约约束 #1)。**WCD 红线**:`category.mask` 必须保留;`stickers[].rotation/zIndex` 随保存持久化;items 是全量替换——提交前必须基于服务端最新 designData(含 R4 写入的 `wordcloud` 分组)合并改动后整包提交,不得只传改动片段(见「WCD 格式硬性规定」) | +| `pages/diy/stickerEdit/index.tsx` | 同上,保存贴纸改动走 API;同样受 WCD 红线约束(合并后整包提交,rotation/zIndex 带全) | ### 阶段 4:本地 store 降级改造 `utils/store/address.ts`、`design.ts` 保留,但语义变为"离线兜底缓存": - API 成功 → 把服务端数据写回本地缓存(下次冷启动先展示缓存再刷新); + **designData 原样存储**:不得清洗 `wxfile://` 等本地贴纸路径、不得裁剪任何字段 + (WCD 硬性规定 #4/#5); - API 失败/断网 → 页面读缓存并可正常浏览,写操作提示失败; - 登录后首次进入:只读服务端,**不自动合并**本地旧数据(`smart_design_list_` 与服务端并存的问题按路线文档风险节处理:忽略或提供一次性导入,默认忽略)。