hl-api-changelog/changelogs/2026-05/14_feat_admin_product-v2-node-description-season-highlights-fallback.md
API Changelog Bot 50841c218f feat: 节点 description 按产品线 seasons[0] 回填季节亮点 changelog (PR #2250 / Issue #2243)
紧接 #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>
2026-05-14 15:11:24 +08:00

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 优先 + 回落本体" 最新

九、相关文档