feat: 季节月份自动赋值 + 节点 description 详情季节动态覆盖 (PR #2295/#2296/#2301)

A. 季节月份自动 (PR #2295, Closes #2291): SaveRequest 删 monthStart/monthEnd,
   后端按 seasonType 自动赋 (spring 3-5 / summer 6-8 / autumn 9-11 / winter 12-2)

B. 节点 description 季节动态覆盖 (PR #2296+#2301, Closes #2293+#2298):
   admin/mp/internal 三路径详情装配时, seasonHighlights 非空 → 覆盖 description
   节点表 DB 字段保留不动 (用户手填永久存)

前端 mmg 删 SeasonDrawer 月份选择器即可。节点 description 字段名不变, 零前端改动。

notify @mmg

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
API Changelog Bot 2026-05-14 19:07:51 +08:00
父节点 b06a5fc058
当前提交 823d5e2629

查看文件

@ -0,0 +1,148 @@
# resource + product-v2: 季节月份自动赋值 + 节点 description 详情按 seasonHighlights 动态覆盖
> **服务**: hl-resource-service (8082) + hl-product-service-v2 (8083)
> **PR**: #2295 (Closes #2291) + #2296 (Closes #2293) + #2301 (Closes #2298)
> **日期**: 2026-05-14
> **影响范围**: 管理端季节内容编辑表单 + 节点 description 详情展示 (admin / mp / internal facade 三路径)
---
## ⚠️ 关键变化
### A. 季节月份自动赋值 (PR #2295)
- 之前: 运营每次配季节内容都要填 monthStart/monthEnd
- 现在: SaveRequest 删 monthStart/monthEnd, 后端按 seasonType 自动赋固定月份 (spring 3-5 / summer 6-8 / autumn 9-11 / winter 12-2 跨年)
### B. 节点 description 详情按季节动态覆盖 (PR #2296 MP/internal + PR #2301 admin)
- 之前: PR #2250 走"独立快照", 节点表 description 非空时不覆盖 → 即使配了季节亮点也看不到
- 现在: admin/mp/internal 三路径详情接口装配 NodeItem 时, 若 ResourceDetailDTO.seasonHighlights 非空 → 覆盖 description (季节优先); 否则保留节点表快照 (本体兜底)
- **节点表 description DB 字段保留不动** (用户手填永久存)
---
## A 部分 - 季节月份自动赋值
### 接口变化
| 接口 | 方法 | 路径 | 变更 |
|---|---|---|---|
| 景区季节保存 | PUT | `/admin/scenic/spot/{scenicId}/season/{seasonType}` | body 删 `monthStart` / `monthEnd` 字段 |
| 游玩项目季节保存 | PUT | `/admin/activity/item/{activityId}/season/{seasonType}` | 同款 |
### 后端固定映射 (SeasonMonthRangeUtil)
```
spring → monthStart=3, monthEnd=5
summer → monthStart=6, monthEnd=8
autumn → monthStart=9, monthEnd=11
winter → monthStart=12, monthEnd=2 (跨年)
```
### VO 保留 monthStart/monthEnd
GET 季节内容接口仍返这两字段, 供前端只读展示。前端**表单删月份选择器**即可 (字段后端自动)。
### 测试服真测 PASS
```
PUT /admin/activity/item/.../season/spring (body 不含 month) → GET 返 monthStart=3 monthEnd=5 ✓
PUT .../season/winter (跨年) → GET 返 monthStart=12 monthEnd=2 ✓
```
---
## B 部分 - 节点 description 季节动态覆盖
### 装配规则 (3 路径同款)
```
if (ResourceDetailDTO.seasonHighlights != null && !blank) {
NodeItem.description = seasonHighlights // 季节优先
} else {
NodeItem.description = 节点表 description // 本体兜底
}
```
### 3 个改动装配点
| 路径 | 文件 | PR |
|---|---|---|
| admin 详情 | `ProductDetailAggregator.fillItinerary` | #2301 |
| MP 实时详情 | `MpProductDetailAssembler.enrichNodeWithResource` | #2296 |
| MP 订单快照详情 | `MpProductSnapshotAssembler.mapNodes` | #2296 |
| internal 快照构建 | `ProductItineraryAggregateFacade.toNodeItem` | #2296 |
### admin 端特别说明 (PR #2301)
原本 admin `ProductDetailAggregator` 不调 ResourceBatchDetailService, 本次加注入 `ResourceDetailFeignClient + ProductHelperService`, 在 fillItinerary 加 3 个私有方法:
- `resolveSeasonForFallback(productId)`: 产品 → 产品线 → seasons[0]
- `fetchResourceDetailsBySeason(allNodes, season)`: Feign batch-details 带 season 参数
- `applySeasonHighlightsOverride`: 装配后覆盖
### 测试服真测 PASS (产品 2043604279016443905 productLine summer)
```
admin GET /admin/product/item/2043604279016443905 Day2 ACTIVITY 节点
→ description = "TEST_PR2301_SUMMER_HL" (summer highlights 覆盖) ✓
MP GET /mp/product/2043604279016443905 同节点
→ description = "TEST_PR2301_SUMMER_HL" ✓
DB 节点表 description 字段值不变 ✓
清理 summer season 后 → description 回 null (节点表本身 null) ✓
```
---
## 兼容性 (B 部分)
| 场景 | 行为 |
|------|------|
| 无 productLine.seasons | season=null → seasonHighlights 永远 null → 等同旧行为 (走节点表 description) |
| 资源详情 Feign 失败 | 静默降级 → 等同旧行为 |
| 节点不绑资源 (resourceId=null) | 不进 enrich → 等同旧行为 |
| HOTEL/CUSTOM 节点 | admin 路径跳过 Feign 调用 (节省, 当前需求不涉及) |
| 节点表 description 非空 + season 命中 | description = seasonHighlights (新行为, 季节优先) |
| 节点表 description 非空 + season 不命中 | description = 节点表 description (本体兜底, 与旧行为一致) |
---
## 前端 mmg 需要做的事
### A 部分 (季节编辑表单)
- 景区/游玩项目 SeasonDrawer.vue 表单**删月份选择器** (monthStart/monthEnd 控件)
- VO GET 仍返月份, 只读展示 (如 "适用月份: 3-5月" 文字)
### B 部分 (节点 description)
- **零前端改动**: admin/mp 详情接口字段名/位置不变, 后端透明切换值
- 之前如果有前端绕开 description 字段做季节展示的逻辑可以删 (后端已切)
---
## 不影响范围
- 节点表 product_itinerary_node.description 字段 (用户手填永久存 DB)
- ProductItineraryService.convertNode 保存逻辑 (PR #2250 行为完全保留)
- 季节子表 scenic_season/activity_season DDL (向前兼容)
- 老数据 (节点表 description / 季节表 monthStart/monthEnd 保留)
---
## 相关历史 PR
| PR | Issue | 说明 |
|----|-------|------|
| #2229 | #2227 | 景区列表 seasons 字段 + 封面切换 |
| #2234 | #2232 | 游玩项目同款 |
| #2250 | #2243 | 节点 description 季节回落 (独立快照, 保存时回填) |
| #2262 | #2257 | ActivitySeasonController admin CRUD |
| #2263 | #2256 | 节点媒体季节基础设施 (ResourceDetailDTO seasonXxx) |
| #2271 | #2268 | MP Assembler 接通 season |
| #2281 | #2278 | 列表加 productLineId 参数自动 resolve seasons |
| **#2295** | **#2291** | **季节月份自动赋值** |
| **#2296** | **#2293** | **节点 description 季节动态覆盖 (MP+internal)** |
| **#2301** | **#2298** | **admin 端同款 (补 #2296)** |
---
## 相关文档
- Issue #2291: [wx/HL#2291](https://git.1814.love:8443/wx/HL/issues/2291)
- Issue #2293: [wx/HL#2293](https://git.1814.love:8443/wx/HL/issues/2293)
- Issue #2298: [wx/HL#2298](https://git.1814.love:8443/wx/HL/issues/2298)
- PR #2295: [wx/HL#2295](https://git.1814.love:8443/wx/HL/pulls/2295)
- PR #2296: [wx/HL#2296](https://git.1814.love:8443/wx/HL/pulls/2296)
- PR #2301: [wx/HL#2301](https://git.1814.love:8443/wx/HL/pulls/2301)