feat: 游玩项目季节过滤 + 节点季节标签 changelog (PR #2234 / Issue #2232)

这个提交包含在:
API Changelog Bot 2026-05-14 11:34:12 +08:00
父节点 546d22454a
当前提交 698a3f03ed

查看文件

@ -0,0 +1,120 @@
---
date: 2026-05-14
type: frontend-feature
module: admin-product-v2-itinerary-activity-picker
priority: medium
backend_pr: "#2234"
backend_issue: "#2232"
related: "14_frontend_feature_admin_scenic-season-filter-and-tags.md"
notify: ["@mmg"]
status: pending
---
# 游玩项目按产品线季节过滤 + 节点展示季节标签
## 需求
紧接景区 (#2227 / PR #2229),「从资源库添加活动」弹窗第二个 tab「**游玩项目**」也要按产品线 `seasons` 过滤 + 缩略图按季节切换 + 节点季节标签。**行为与景区完全对齐**。
## 后端已就位 (PR #2234 已合并并部署测试服)
### 数据基础
**新建** `activity_season` 子表(Flyway V20260514_001,字段对照 `scenic_season` 复刻)。运营进后台逐个游玩项目补配季节数据后生效;未配的游玩项目 `vo.seasons``[]`
`activity.best_season` 自由文本字段保留不动(老 UI 兼容),**不**自动迁移到新结构化子表。
### 接口变更
#### `GET /admin/activity/items` — 游玩项目列表
**新增 query 参数**(与景区接口同款):
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `seasons` | `List<String>` | 否 | 多选 OR 过滤,枚举 `spring/summer/autumn/winter`。传任一交集即返回。空/不传 = 不过滤 |
请求格式(数组多参):
```http
GET /admin/activity/items?seasons=spring&seasons=autumn&pageSize=20
```
**`ActivityListVO` 新增字段:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `seasons` | `List<String>` | 该游玩项目在 `activity_season` 配置的全部季节(distinct,枚举 `spring/summer/autumn/winter`)。**节点标签直接读这个**。 |
> ⚠️ **已有的 `coverUrl` 字段含义变更**:当 query 含 `seasons` 时,`coverUrl` 返回 **query.seasons[0] 对应的 `activity_season.cover_material_id`** 物化 URL;该季节未配/物化失败回落到主表 `coverMaterialId`。无 query.seasons 时维持主表 cover(行为不变)。
#### 真实响应示例
**请求**: `GET /admin/activity/items?pageSize=2`
```json
{
"code": 200,
"message": "成功",
"data": {
"total": 56,
"records": [
{
"activityId": 3002000000000000000,
"name": "满洲里草原篝火晚会",
"subtitle": "...",
"categoryCode": "...",
"billingType": "...",
"city": "...",
"coverUrl": "https://hlgl-test.oss-cn-beijing.aliyuncs.com/test/...",
"seasons": [],
"tags": [...],
"longitude": ...,
"latitude": ...
}
]
},
"success": true
}
```
> 当前 `activity_season` 表是空的(刚建),所有游玩项目 `seasons` 都返 `[]`。**运营录数据后**,过滤会立即生效;封面切换也立即生效,行为与景区完全一致。
## 前端实现建议
### 弹窗「游玩项目」tab
参考景区 tab 的实现完全对照:
1. 弹窗打开时调列表接口透传产品线 `seasons`:
```js
const params = new URLSearchParams()
productLineSeasons.forEach(s => params.append('seasons', s))
await api.get(`/admin/activity/items?${params}&pageSize=20`)
```
2. 列表行的**缩略图**直接用 VO 返回的 `coverUrl`(后端已切换好)
3. 列表行的**季节标签**渲染 `vo.seasons` 数组,**4 色与景区一致**:
- `spring` → 春绿 `#67C23A`
- `summer` → 夏蓝 `#409EFF`
- `autumn` → 秋橙 `#E6A23C`
- `winter` → 冬蓝灰 `#909399`
### 已选节点(行程卡片上)
行程节点 ACTIVITY 类型的 `InternalProductDetailVO.NodeItem.seasons` 已透传 — 在游玩项目节点卡片上渲染同款季节标签(可多个)。**与景区节点同款组件即可复用**。
### 兜底
- 老游玩项目没配 `activity_season` 的,`vo.seasons``[]`,前端不渲染标签即可
- `coverUrl` 任何时候都不会因为新逻辑变 null(已做回落)
## 工单 & PR
- Backend Issue: #2232
- Backend PR: #2234 (squash merged dev,已部署测试服)
- 测试服真测通过: 基线 total=56 ✅ / VO seasons 字段 ✅ / seasons=autumn 过滤 total=0(数据空) / 反例 seasons=xxx 总=0 ✅ / coverUrl 物化 ✅
## 关联
景区同款实施: `14_frontend_feature_admin_scenic-season-filter-and-tags.md`(PR #2229 / Issue #2227)
弹窗的剩余两个 tab「餐厅」「服务」**暂未实施季节过滤** — 业务侧确认需要再开工单。