# 产品族三期（公开读路径灰度 + Good.familyId）实施计划

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** `Good.familyId` 落库并在公开详情中灰度暴露族数据（并集尺码表/包装/五维价格矩阵），零新增公开端点、开关可秒回退（设计 D4）。

**Architecture:** `Good.familyId` 是**派生数据**：创建时自动取主链接的族；族成员变更时联动刷新。公开详情的族块直接来自 `ProductFamily` 物化 JSON（零额外查询），由环境变量 `PUBLIC_DETAIL_FROM_FAMILY`（默认 false）控制是否输出。

**明确的范围边界:** 列表/筛选/排序仍基于主源 `goodPrice`（SQL 层无法廉价解析族矩阵 JSON），仅详情页价格以矩阵为准；`good_origin_goods` 转只读保留，观察期后再删（另行任务）。

---

### Task 1: Schema + 迁移 + 回填

- [ ] `Good` model 增加 `familyId BigInt? @map("family_id")` + `family ProductFamily? @relation(... SetNull)` + `@@index([familyId])`；`ProductFamily` relations 加 `goods Good[]`。
- [ ] `npx prisma migrate dev --name add_good_family_id`，迁移 SQL 末尾追加回填：
  `UPDATE goods SET family_id = (SELECT family_id FROM origin_goods WHERE origin_goods.origin_good_id = goods.origin_good_id);`
- [ ] Commit `feat(api): good family_id column with backfill`

### Task 2: Good.familyId 派生逻辑

- [ ] `goods.service.ts`：`create` / `batchCreate` / `createCustom` 创建 Good 时 `familyId = originGood.familyId`（含 custom 直挂的 familyId）；
- [ ] `product-families.service.ts`：`updateMembers` / `tryAutoAttachToFamily`（sync.service）成员移动后联动
  `UPDATE goods SET family_id`（`good.updateMany({ where: { originGoodId: { in: movedIds } }, data: { familyId } })`，摘除时置 null）。
- [ ] 测试：创建 Good 带 familyId、成员移动联动（集成）。
- [ ] Commit `feat(api): derive good family membership`

### Task 3: 公开详情族块（灰度开关）

- [ ] `PUBLIC_GOOD_INCLUDE` 的 `originGood.include` 加 `family: { select: { id, familyCode, familyName, sizeChart, packageSpecs, priceMatrix } }`；
- [ ] `PublicGoodDetailDto` 增加可选 `family` 字段；`toPublicGoodDetail` 在
  `process.env.PUBLIC_DETAIL_FROM_FAMILY === 'true'` 且 `originGood.family` 存在时输出
  `{ familyId, familyCode, familyName, sizes, colors, crafts, logistics, sizeChart, packageSpecs, priceMatrix, minPrice }`
  （minPrice = 矩阵 rows 最低价）；开关关闭时**完全不含**该字段（响应形状与现状逐字节一致）。
- [ ] 测试：开关两态（spec 内直接改 env 再实例化 service）、族块字段正确、无族 Good 字段缺失。
- [ ] Commit `feat(api): public good detail family block behind flag`

### Task 4: 验证 + 文档 + 合并

- [ ] api 全量测试两轮全绿；`pnpm -r build` 通过。
- [ ] 冒烟：开关 off 响应无 family 键；on 时含 DG001 族块（470 格）。
- [ ] 更新 `docs/references/structs.md`（Good.familyId、公开详情族块、环境变量）、`docs/references/product-center.md`（三期行为与边界声明）、`README.md` 环境变量说明。
- [ ] 遵循 verification-before-completion；合并回 develop。

## Self-Review

- 设计 §10.2 复用原则 ✓（零新增端点）；§5.3 Good.familyId ✓；§12.6 读路径灰度 ✓（开关粒度为详情族块）；
- 列表价/筛选不动已作为边界显式声明（SQL 不可行 + 避免展示价与筛选价不一致）；
- `good_origin_goods` 保留只读，删除另立任务（观察期要求）。
