# 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` 即可）
