docs(product-v2): 操作日志 P1 逐日聚合 diff + 跨产品检索 (PR #2423)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
这个提交包含在:
父节点
d827a14dbe
当前提交
cf06dc6afc
@ -0,0 +1,172 @@
|
||||
# product-v2: 操作日志 P1 — 价格日历逐日聚合 diff + STEP2-5 全字段 + 跨产品检索
|
||||
|
||||
> **服务**: hl-product-service-v2 (端口 8083)
|
||||
> **PR**: #2423(承接 P0 #2355)
|
||||
> **Issue**: #2409
|
||||
> **日期**: 2026-05-16
|
||||
> **影响范围**: 管理端「产品 → 操作日志」侧边栏 + 新增「跨产品操作日志检索」入口
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 关键变化
|
||||
|
||||
**P0 已让 `changes` 字段可用,P1 让 `changes` 真正"细到每天每档"+ 全字段覆盖**:
|
||||
|
||||
- 旧逻辑(P0 上线后):PRICE_SET 只看到 1 行「成人售价 5800→9400」,不知道改了哪几天
|
||||
- 新逻辑(P1):**按 (字段, 档位, 旧值, 新值) 智能聚合日期** → 8 天改同价 = **1 行带日期范围**「成人售价 2026-07-12~07-19 (8 天) 经典档: 5800→9400」
|
||||
- **前端 0 改动**:复用 P0 的 `changes` 数组渲染表格,新 `fieldLabel` 自带聚合描述
|
||||
|
||||
**新接口**:`GET /admin/product/item/operation-logs/search` 跨产品按字段名检索(运营查"今天谁改过价")。
|
||||
|
||||
---
|
||||
|
||||
## 一、背景
|
||||
|
||||
承接 P0 [#2355](https://git.1814.love:8443/wx/HL/pulls/2355) — 张老板查产品改价时,P0 只能看到「成人售价 5800→9400」一行(取第一天作代表),看不出来"是哪几天改了"。如果运营改 8 天里 3 天,剩 5 天没改,P0 完全无法区分。
|
||||
|
||||
P1 解决:逐日 diff + 智能聚合 + STEP2-5 全字段全覆盖。
|
||||
|
||||
---
|
||||
|
||||
## 二、变更接口清单
|
||||
|
||||
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|
||||
|---|------|------|------|----------|------|
|
||||
| 1 | 查产品操作日志 | GET | `/admin/product/item/{id}/operation-logs` | **出参增强** | `changes` 数组含聚合 fieldLabel + STEP2-5 全字段 |
|
||||
| 2 | 跨产品检索操作日志 | GET | `/admin/product/item/operation-logs/search` | **新接口** | 按字段名(如 adultSellPrice)查谁改过价 |
|
||||
|
||||
---
|
||||
|
||||
## 三、接口详情
|
||||
|
||||
### 1. 查产品操作日志(出参增强)
|
||||
|
||||
`changes[].fieldLabel` 现在自带日期范围描述:
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "PRICE_SET",
|
||||
"actionLabel": "设置价格",
|
||||
"step": "STEP4",
|
||||
"stepLabel": "价格与班期",
|
||||
"changes": [
|
||||
{
|
||||
"field": "adultSellPrice",
|
||||
"fieldLabel": "成人售价 2026-07-12~07-19 (8 天) 经典档",
|
||||
"oldValue": "5800",
|
||||
"newValue": "9400",
|
||||
"changeType": "MODIFY"
|
||||
},
|
||||
{
|
||||
"field": "childSellPrice",
|
||||
"fieldLabel": "儿童售价 2026-07-12~07-19 (8 天) 经典档",
|
||||
"oldValue": "3000",
|
||||
"newValue": "3980",
|
||||
"changeType": "MODIFY"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 聚合描述格式
|
||||
|
||||
| 场景 | fieldLabel 示例 |
|
||||
|------|----------------|
|
||||
| 连续日期 | `成人售价 2026-07-12~07-19 (8 天) 经典档` |
|
||||
| 离散日期 | `成人售价 2026-07-12, 07-15, 07-18 (3 天) 经典档` |
|
||||
| 单天 | `成人售价 2026-07-12 经典档` |
|
||||
| 跨月连续 | `成人售价 2026-07-30~08-01 (3 天) 经典档` |
|
||||
| 不同新值(如周末加价) | 自动按 oldValue→newValue 分多组,多行 |
|
||||
|
||||
#### 长文本字段(>200 字)摘要
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "introduction",
|
||||
"fieldLabel": "产品介绍",
|
||||
"oldValue": "已修改 (前80字…后80字)",
|
||||
"newValue": "已修改 (前80字…后80字)",
|
||||
"oldHash": "a3f4...",
|
||||
"newHash": "b8e2...",
|
||||
"changeType": "MODIFY"
|
||||
}
|
||||
```
|
||||
- 字段白名单:`description / introduction / highlights / quickUnderstand / childExperience / growthGains / equipmentList / routeMapUrl / detailContent / coverContent`
|
||||
- `oldHash/newHash`:SHA-256 前 16 字节,**前端可选展示**(用于跨日志判断"内容是否真变了")
|
||||
|
||||
#### 集合字段聚合
|
||||
|
||||
```json
|
||||
{
|
||||
"field": "itineraryDays",
|
||||
"fieldLabel": "行程列表",
|
||||
"oldValue": "共 7 天",
|
||||
"newValue": "共 8 天 (新增 1 / 删除 0)",
|
||||
"changeType": "MODIFY"
|
||||
}
|
||||
```
|
||||
- 集合白名单:`seasons / tags / tiers / itineraryDays / dayNodes / dayHotels / feeItems / carouselImages / excludedFees / includedFees / customFees`
|
||||
- ⚠️ **注意**:集合字段不做深 diff(避免日志 JSON 爆 50KB+),只聚合「新增 N / 删除 M」。具体哪一项的字段变了,目前需要查日志详情(P2 后续可优化)
|
||||
|
||||
---
|
||||
|
||||
### 2. 跨产品检索操作日志(新接口)
|
||||
|
||||
`GET /admin/product/item/operation-logs/search`
|
||||
|
||||
#### 入参(VO)
|
||||
|
||||
| 字段 | 位置 | 类型 | 必填 | 默认 | 说明 |
|
||||
|------|------|------|------|------|------|
|
||||
| `field` | Query | String | ✅ | — | 字段英文名,如 `adultSellPrice` |
|
||||
| `fromDate` | Query | LocalDate | ❌ | today-30d | 开始日期(双侧空时套 30 天兜底) |
|
||||
| `toDate` | Query | LocalDate | ❌ | today | 结束日期 |
|
||||
| `page` | Query | Integer | ❌ | 1 | 页码 |
|
||||
| `size` | Query | Integer | ❌ | 20 (max 100) | 页大小 |
|
||||
|
||||
#### 权限
|
||||
|
||||
Service 入口校验 role ∈ {`SUPER_ADMIN`, `ADMIN`},普通销售员调用返 `PERMISSION_DENIED`(403 业务码)。
|
||||
|
||||
#### 全词匹配
|
||||
|
||||
避免 `field=price` 误命中 `adultSellPrice` / `childSellPrice` / `infantPrice`:
|
||||
- 写入端:`changed_fields` 存 `,adultSellPrice,childSellPrice,`(两端逗号包裹)
|
||||
- 查询端:`LIKE '%,adultSellPrice,%'` 全词匹配
|
||||
- 兼容老日志:P0 写入的没逗号包裹,OR 兜底 3 种边界(exact / 头部 / 尾部)
|
||||
|
||||
#### 出参
|
||||
|
||||
复用 `ProductOperationLogRespVO`(带分页)。
|
||||
|
||||
---
|
||||
|
||||
## 四、前端改动建议(mmg)
|
||||
|
||||
**方案 A+ 聚合版**:**前端 0 改动**。
|
||||
|
||||
- P0 已实现的 `changes` 表格渲染逻辑自动适配(新 fieldLabel 自带"日期范围 (N 天) 档位名")
|
||||
- 长文本字段:oldValue/newValue 已是「已修改 (前80字…后80字)」文本,直接渲染
|
||||
- 集合字段:oldValue/newValue 已是「共 N 条 (新增 A / 删除 C)」文本,直接渲染
|
||||
|
||||
**新接口入口建议**(可选):管理端加个「操作日志检索」菜单,调 `/operation-logs/search?field=&fromDate=&toDate=`,用于审计场景。
|
||||
|
||||
---
|
||||
|
||||
## 五、向后兼容
|
||||
|
||||
- ✅ 老日志(changes=null)继续不渲染 diff 区块
|
||||
- ✅ P0 写入的日志(changed_fields 没两端逗号)依然能被新检索接口匹配
|
||||
- ✅ 新接口 `/operation-logs/search` 不影响现有 `/operation-logs`
|
||||
- ✅ Flyway V20260516_003 只加索引,无 DDL 风险
|
||||
|
||||
**部署顺序**:Flyway 先行 → 后端发布。前端 0 改动可选发。
|
||||
|
||||
---
|
||||
|
||||
## 六、不在本期范围(P2 后续)
|
||||
|
||||
- **集合字段深 diff**:当前只聚合「新增/删除数」,不展示哪一项的哪个字段变了
|
||||
- 「还原到此版本」按钮
|
||||
- 调价超阈值(>20%)钉钉/企微告警
|
||||
- 跨产品聚合可视化页(直接读 `/operation-logs/search` 即可)
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户