docs(v3): 选产品改用专用 picker(含热销+发团日期),已测试服验证 (#3159/PR #3164)

这个提交包含在:
API Changelog Bot 2026-05-28 11:47:18 +08:00
父节点 dfa833cb34
当前提交 e41d65e1c5

查看文件

@ -48,47 +48,50 @@ GET /admin/product/line/order-picker
- `skuCount` / `fromPrice` / `hot` → 同名直取 - `skuCount` / `fromPrice` / `hot` → 同名直取
- 顶部统计「在售主题数」= 列表长度;「SKU总数」= Σ`skuCount`;「最低人均」= min(`fromPrice`) - 顶部统计「在售主题数」= 列表长度;「SKU总数」= Σ`skuCount`;「最低人均」= min(`fromPrice`)
### 2. 第 2 步「选产品」— 用已有接口(无新增),已在测试服实测返回结构 ### 2. 第 2 步「选产品」— 新增专用接口PR #3164 / #3159,已测试服验证)
> 注:原先说复用 `/admin/product/item/list`,但 SKU 卡还要「热销」「发团日期」两个字段,通用列表没有;为不拖慢产品管理列表,**改为新建专用选产品 picker**(与选主题对称)。**前端 Step2 改调下面这个**,不要用 `/item/list`
``` ```
GET /admin/product/item/list?lineId={lineId}&status=PUBLISHED GET /admin/product/item/order-picker?lineId={lineId}&page=1&pageSize=20&keyword=
``` ```
- 选定主题后传其 `lineId`,只取已上架产品;返回 `PageResult<ProductListRespVO>``data.records[]` + `data.total` - `lineId` 必填(选定主题的 `lineId`);只返该产品线**已上架**产品+档位;返回 `PageResult<OrderPickerProductVO>``data.records[]` + `data.total`
- 数据权限同上(带 @DataScope超管全部 / CORE·GROUP 全员 / CUSTOM 仅本人) - 数据权限同选主题(@DataScope超管全部 / CORE·GROUP 全员 / CUSTOM 仅本人)
- 下单提交需要`productId + tierSeq` 从这里取(雪花 `productId` 按字符串透传,勿 Number() - 下单提交的 `productId + tierSeq` 从这里取(雪花 `productId` 按字符串透传,勿 Number()
返回 `records[]` 实测字段(与 Step2 SKU 卡片显示元素映射): 返回 `records[]` 实测字段(与 Step2 SKU 卡片元素映射):
```jsonc ```jsonc
{ {
"productId": 2043595351268519937, // 产品ID(字符串透传) "productId": 2043595351268519937, // 产品ID(字符串透传)
"name": "6天5晚旷野版", // 卡片大标题 "name": "6天5晚旷野版", // 卡片大标题
"subtitle": "装甲车穿越·帐篷营地…", // 副标题 "subtitle": "装甲车穿越·帐篷营地…", // 副标题
"tags": ["亲子","露营"], // 标签行(· tag · tag) "tags": ["亲子","露营"], // 标签行
"tripDays": 6, "tripNights": 5, // "6天5晚" "tripDays": 6, "tripNights": 5, // "6天5晚"
"coverImageUrl": "https://...", "coverImageUrl": "https://...",
"startPrice": 10.00, // 产品级最低起价(可能 null) "startPrice": 10.00, // 产品级最低起价(可能 null → 显示 ¥--)
"tierCount": 2,
"tierPrices": [ // 档位数组(档次过滤 chips = 去重 tierName) "tierPrices": [ // 档位数组(档次过滤 chips = 去重 tierName)
{"tierSeq":1, "tierName":"舒适", "tierDescription":"…", "startPrice":10.00}, {"tierSeq":1, "tierName":"舒适", "tierDescription":"…", "startPrice":10.00},
{"tierSeq":2, "tierName":"豪华", "tierDescription":"…", "startPrice":null} // 该档未设价 {"tierSeq":2, "tierName":"豪华", "tierDescription":"…", "startPrice":null}
] ],
"hot": true, // 【新】热销徽标 = 该产品近90天下单量较多(本主题内前30%且>0)
"nextSaleDate": "2026-07-01" // 【新】发团日期 = 价格日历/班期里最近的未来可售日(无可售为 null)
} }
``` ```
**SKU 卡渲染**mock 里每张卡=一个产品的一个档位;真实是一个产品(`productId`)含多档位(`tierPrices[]`)。 **SKU 卡渲染**mock 里每张卡=一个产品的一个档位;真实是一个产品(`productId`)含多档位(`tierPrices[]`)。
建议按「产品 × 档位」平铺成卡(每个 `tierPrices[i]` 一张卡),卡上: 建议按「产品 × 档位」平铺成卡(每个 `tierPrices[i]` 一张卡),卡上:
- 档位标签 ← `tierPrices[i].tierName`"尊享档/挑战档" - 档位标签 ← `tierPrices[i].tierName`"尊享档/挑战档"
- 价格 ← `tierPrices[i].startPrice`**为 null 时显示 ¥-- / 灰**,别显示 0 - 价格 ← `tierPrices[i].startPrice`**null 显示 ¥-- / 灰**,别显示 0
- 「热销」徽标 ← 产品级 `hot`(同一产品的各档位卡都显示)
- 「发团日期」← `nextSaleDate`最近可售日;null 则不显示该行)
- 提交带 `productId` + `tierPrices[i].tierSeq` - 提交带 `productId` + `tierPrices[i].tierSeq`
**⚠️ 两个 mock 元素后端暂无对应字段(同「地区」性质,需前端确认怎么处理):**
- **「热销」徽标(每个 SKU**`/item/list` 无产品/档位级热门字段(热门目前只做到了主题级,在选主题接口的 `hot`)。→ 选产品卡片**先不显示热销徽标**;若确实要按"该产品下单较多"标热,需后端再加一个产品级下单量字段(同主题 hot 的做法,可另开工单)。
- **「发团日期」(如 5-10 月)**`/item/list` 无此字段,它是班期/价格日历的可售日期,不是单一文案。→ **先省略**;若要展示,需后端补"可售日期范围"或前端另调班期/价格日历接口。
### 3. 提交(不变) ### 3. 提交(不变)
`POST /v3/admin/order`,入参 `productId` + `tierSeq` 等,已就绪。 `POST /v3/admin/order`,入参 `productId` + `tierSeq` 等,已就绪。
--- ---
## 验证 ## 验证
测试服 `web.test.1814.love:9443` 真 admin token 实测order-picker 返 9 个在售主题(库内 ACTIVE 29 → 在售 9 精确吻合),字段齐全,hot 跨服务统计生效。 测试服 `web.test.1814.love:9443` 真 admin token 实测:
- 选主题 `line/order-picker`:返 9 个在售主题(库内 ACTIVE 29 → 在售 9 精确吻合),字段齐全,主题级 hot 跨服务统计生效
- 选产品 `item/order-picker`HTTP 200,字段齐全;产品级 `hot`(热门线下 4 产品命中 1 个 hot=true`nextSaleDate`(价格日历最近可售日,如 2026-07-01均生效