紧接 #2229/#2232/#2096, 节点 description 为空时回填来源由"本体 highlights" 升级为"scenic_season/activity_season.highlights(按 productLine.seasons[0]) → 回落本体"。 独立快照保留: 非空 description 不会被覆盖, 用户手填永远保留。 notify @mmg Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.7 KiB
product-v2: 节点 description 按产品线 seasons[0] 自动回填季节亮点
服务: hl-product-service-v2 (端口 8083) + hl-resource-service (端口 8082) PR: #2250 Issue: #2243 日期: 2026-05-14 影响范围: 管理端 产品编辑 → 补充信息 → 产品卖点(节点 description 字段)
⚠️ 关键变化
紧接 #2229(景区季节)/#2232(游玩项目季节)/#2096(节点 description 默认从资源 highlights 回填)。
之前: 节点 description 为空时, 后端自动回填 = 资源本体 highlights。
现在: 节点 description 为空时, 后端自动回填 = scenic_season/activity_season.highlights(按产品线 seasons[0])→ 缺失再回落本体 highlights。
保持不变(独立快照): 节点 description 非空时不动, 用户手填的卖点永远保留, 不会被后端覆盖。
一、回填规则
节点 description = (
( scenic_season.highlights WHERE scenic_id=X AND season_type=seasons[0] ) -- SCENIC 优先
|| scenic_spot.highlights -- SCENIC 回落
|| ( activity_season.highlights WHERE activity_id=X AND season_type=seasons[0] ) -- ACTIVITY 优先
|| activity.highlights -- ACTIVITY 回落
|| null
)
季节匹配规则: 取产品对应产品线 productLine.seasons[0](首个季节), 与封面切换 #2229/#2234 行为一致。
触发时机: 仅在保存行程节点(POST /admin/product/item/{id}/itinerary 或同等接口)且节点 description 为空或空白字符时触发。已经填了内容的节点保存时不会被覆盖。
二、影响接口
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 保存行程(快速理解节点) | POST/PUT | /admin/product/item/{id}/itinerary |
内部行为变更 | 入参/响应字段无变化 |
| 2 | 产品详情 | GET | /admin/product/{id} |
数据来源变化 | itineraryDays[].nodes[].description 字段值来源由"本体 highlights"改为"季节 highlights 优先" |
| 3 | 资源详情批量(internal) | POST | /internal/resource/batch-details |
入参加 season |
透明改动, 调用方为 product-v2 |
跨服务 DTO ResourceDetailDTO 新增字段 seasonHighlights(可为 null), 老消费者(Jackson 默认 ignore unknown)兼容, 不需要 redeploy。
三、契约约束 - 前端无需任何改动
- 保存接口 payload 字段不变
- 详情响应字段不变(还是
description) - 前端在编辑页填的内容永远不会被后端覆盖, 不需要任何防御逻辑
四、边界行为
| 场景 | 节点 description 自动回填值 |
|---|---|
productLine.seasons = ["spring"] + 节点指向有 spring 配置的景区 + description 空 |
scenic_season(spring).highlights |
productLine.seasons = ["winter"] + 节点指向无 winter 配置的景区 + description 空 |
回落 scenic_spot.highlights(本体) |
productLine.seasons = null/[] + description 空 |
回落本体 highlights(向前兼容, 与 #2096 行为一致) |
productLine.seasons = ["spring","autumn"](多季节) + description 空 |
取 seasons[0]=spring 匹配 |
| ACTIVITY 节点 | 同款行为(activity_season.highlights 优先, 回落 activity.highlights) |
| 节点 description 非空 | 不回填, 保留原值(任何场景下都不覆盖) |
| 资源详情查询失败/异常 | 静默降级(seasonHighlights=null), 走本体 highlights |
五、老数据 / 存量产品
重要: 此次改动不会主动遍历存量产品节点修改 description。
- 老产品节点 description 都是非空的(之前由 #2096 用本体 highlights 自动填了, 或用户手填的) → 保存时不会触发回填, 看起来"季节回落没生效"是正常的
- 想让某节点切换到季节亮点 → 编辑时清空 description 文本框再保存即可
- 不接受"自动覆盖所有老节点"的需求(已与 wx 确认: 独立快照原则保护用户手填内容)
六、不影响范围
- 仅影响: 管理端产品编辑保存时的节点 description 默认值
- 零影响:
- 小程序端 C 端展示(读 description 字段, 来源透明)
- 订单创建/详情/退款
- 算价 / 价格日历
- 历史产品节点的现存 description 值
七、测试环境已验证
GET /admin/scenic/spots?page=1&pageSize=5&status=1 → 200 + seasons[] 字段 ✓ (PR #2229)
GET /admin/activity/items?page=1&pageSize=5 → 200 + seasons[] 字段 ✓ (PR #2232)
GET /admin/product/2045345825172639746 → 200 + itinerary.nodes[].description ✓
测试样本: 产品"游牧的森林-短途版"(productLineId=2045004175602868226, seasons=["autumn"])。3 个手填过 description 的 SCENIC 节点保持原值不变, 符合独立快照设计。
八、相关历史 PR
| PR | Issue | 说明 | 是否仍有效 |
|---|---|---|---|
| #2076 | #2068 | 资源 3 类 + 行程节点新增「图标」「卖点简介」2 字段 | ✅ 有效 |
| #2091 | #2090 | 复用 description 删冗余 sellingPoint | ✅ 有效 |
| #2096 | #2095 | 节点选资源时, 资源本体 highlights 默认带到节点 description | ✅ 有效, 被本 PR 增强 |
| #2229 | #2227 | 产品设计选景区按产品线季节过滤 + 节点 seasons 标签 | ✅ 有效 |
| #2234 | #2232 | 游玩项目同款季节过滤 + 标签 | ✅ 有效 |
| 本 PR #2250 | #2243 | 节点 description 默认值由"本体 highlights"升级为"季节 highlights 优先 + 回落本体" | ✅ 最新 |
九、相关文档
- Issue: wx/HL#2243
- PR: wx/HL#2250