diff --git a/docs/superpowers/specs/2026-08-28-product-family-merge-design.md b/docs/superpowers/specs/2026-08-28-product-family-merge-design.md index d8a8762..0d077a3 100644 --- a/docs/superpowers/specs/2026-08-28-product-family-merge-design.md +++ b/docs/superpowers/specs/2026-08-28-product-family-merge-design.md @@ -1,9 +1,10 @@ # 产品族(SPU)合并 — 后端设计 -- 日期:2026-08-28(同日修订:价格允许人工覆盖、支持人工添加自定义商品入族) +- 日期:2026-08-28(同日修订:价格允许人工覆盖、支持人工添加自定义商品入族;公开接口零新增全复用;admin 前端纳入本期) - 状态:已评审(讨论稿定稿,待实施计划) - 分支:`feature/product-link-merge-yeuimu` -- 范围:仅后端(apps/api);前台展示层(apps/admin、apps/website)仅列出依赖的接口契约 +- 范围:后端(apps/api)+ 后台管理前端(apps/admin);官网(apps/website)不在本期, + 但公开接口保持 100% 兼容复用(见 §10.2) ## 1. 背景与问题 @@ -37,7 +38,7 @@ SDS(InkPOD)上游会为同一个实体商品下发多条近似链接,名 - 不引入价格阶梯/折扣表(订单量定价已被明确否决);价格默认按链接推导透传, 但**允许人工按格改价(覆盖表)**,并支持**人工添加自定义商品入族**(见 §5.5、§5.6、§8); - 不改动同步任务的核心抓取逻辑与安全护栏; -- 不在本设计内实现 admin/website 的界面(仅约定接口契约)。 +- 不改动 apps/website(官网)——公开接口零新增、原样复用,官网可无感灰度。 ## 3. 决策记录 @@ -46,6 +47,8 @@ SDS(InkPOD)上游会为同一个实体商品下发多条近似链接,名 | D1 | 合并落在哪一层 | **新增 SPU 产品族层**(介于 OriginGood 与 Good 之间),而非强化 Good 级合并或读取时动态聚合 | | D2 | 价格形态 | **默认按链接透传 + 人工覆盖**(2026-08-28 修订):无阶梯折扣、无全手动明码矩阵;价格默认 = 成员链接变体的 SDS 原价,允许按格子人工改价(覆盖表),并允许人工添加自定义商品入族 | | D3 | 买家选价粒度 | **五维全选**:详情页提供尺码/颜色/工艺(印花数量)/物流选择器,价格实时联动 | +| D4 | 公开接口 | **零新增、全复用**:不新建任何公开端点,族数据以增量字段嵌入既有 `/public/*` 响应;`goodId=sdsGoodId` 与"任意成员命中同一族"语义不变 | +| D5 | 前端范围 | **admin 后台前端纳入本期**(原产品树按族分组、族管理、人工改价、自定义商品四块界面);官网不做 | ## 4. 架构 @@ -259,6 +262,9 @@ model FamilyPriceOverride { ### 10.2 公开(无鉴权) +**复用原则(D4):不新增任何公开端点、不改变路径与参数语义**。族数据全部以增量字段 +嵌入既有响应,官网现有代码继续工作;详情页消费 `family` 块属于纯增量升级。 + `GET /public/goods/:goodId`(goodId 仍为 sdsGoodId,主源或副源命中均可,语义不变)详情新增: ```jsonc @@ -281,7 +287,45 @@ model FamilyPriceOverride { - 不可用组合由矩阵行的**缺席**表达(前端禁用对应选项); - 读路径切换加配置开关(如 `PUBLIC_DETAIL_FROM_FAMILY`)灰度,可秒回退。 -## 11. 迁移与灰度 +## 11. 后台管理前端(apps/admin,D5) + +### 11.1 现状与入口 + +- 外层壳 `views/product-management/ProductManagementView.vue` 以标签页组织: + 商品配置(`views/goods/GoodsView.vue`,约 2655 行)、数据同步(`views/sync/SyncView.vue`); +- 本期新增第三个标签页"**产品族**",并改造 GoodsView 的原产品树。 + +### 11.2 新增"产品族"标签页 + +新建 `views/product-family/` 目录,按职责拆组件(避免复刻 GoodsView 的巨型单文件): + +- `FamilyList.vue`:族列表——关键词/分页/成员数、`stale` 红点、`autoManaged` 状态; +- `FamilyDetail.vue`:族详情——canonical 字段编辑(名称/主图/国家/分类/主链接/autoManaged)、 + 成员管理(SDS 链接增删、创建自定义成员)、手动重算; +- `AutoGroupDialog.vue`:自动建族——按 3 段规则展示候选分组预览,确认后应用; +- `PriceOverridePanel.vue`:人工改价矩阵——按 (工艺 × 物流) 切换页签,表格为尺码 × 颜色, + 单元格显示有效价,人工格高亮并展示与推导价的差额,行内编辑即调 + `PUT / DELETE price-overrides`; +- 图片上传复用现有 `components/ImageUpload.vue`。 + +### 11.3 GoodsView 改造 + +- 右侧原产品树分组依据从**前端名称截断**(`utils/origin-name.ts`,本期后退役)切换为 + `GET /origin-goods/tree` 返回的真实族分组; +- 原"合并到 Good"交互改为"挂到族":勾选兄弟链接 → 加入既有族或新建族; +- Good 配置表单增加 `familyId`(选族替代选主源+副源),保留"主链接"概念用于跳转兜底。 + +### 11.4 API client + +- 新增 `src/api/product-families.ts`(族 CRUD / auto-group / members / overrides / recompute); +- `src/api/origin-goods.ts`、`src/api/goods.ts` 随契约更新(familyId 字段)。 + +### 11.5 前端测试 + +关键交互(auto-group 预览、覆盖编辑、自定义成员创建、树分组切换)随仓库现有测试设施 +补充组件/交互测试;全量现有测试保持通过。 + +## 12. 迁移与灰度 1. **建表加列**:Prisma migration(product_families、OriginGood 四解析列 + familyId、Good.familyId); 2. **解析回填**:脚本解析全部存量 `OriginGood.goodName` 填四列,输出不可解析清单; @@ -295,10 +339,10 @@ model FamilyPriceOverride { 7. **自定义商品与覆盖**:存量 `source=CUSTOM` 的 OriginGood 解析列保持为空,由管理员 按需补填并挂族;覆盖表初始为空,随运营逐步产生,无需数据迁移。 -分期建议:一期(族 + 解析 + 自动建族 + 后台树改造),二期(并集物化 + 公开详情切换 + Good.familyId), -避免单次变更过大。 +分期建议:一期后端(族 + 解析 + 自动建族 + 树接口改造),二期 admin 前端(产品族页 + GoodsView 改造), +三期(并集物化 + 公开详情灰度切换 + Good.familyId),避免单次变更过大。 -## 12. 测试要点 +## 13. 测试要点 - 解析器单元测试:golden 样本覆盖真实 552 条中的代表格式 + 畸形样本; - 并集合并:同尺码冲突裁决、缺尺码补齐、包装规则合并、warning 记录; @@ -311,7 +355,7 @@ model FamilyPriceOverride { - 公开接口契约:开关两态下的响应形状、任何成员 sdsGoodId 均命中同一族; - 全量现有测试保持通过(回归红线)。 -## 13. 权限评估(按 AGENTS.md 要求) +## 14. 权限评估(按 AGENTS.md 要求) 新增后台操作需纳入现有权限体系(User/Role + JWT): @@ -321,7 +365,7 @@ model FamilyPriceOverride { 上线前需在 `docs/references/authority-matrix-ui.md` 同步权限矩阵。 -## 14. 默认值与待确认项 +## 15. 默认值与待确认项 以下规则已按默认值设计,实施前如需推翻请在此记录: