# product-v2: 操作日志两个 UX 改进 — 价格档位过滤 + 长字段友好展示

> **服务**: hl-product-service-v2 (端口 8083)
> **PR**: #2505 (价格日历档位过滤) + #2508 (changes 友好展示)
> **Issue**: #2504 + #2507
> **日期**: 2026-05-18
> **影响范围**: 产品操作日志 `changes` 字段(GET /admin/product/item/{id}/operation-logs 出参 `data[].changes`)

---

## mmg 必看 — 接口契约不变,只是 changes 内容更友好

接口 URL / Method / 入参 / 出参结构**完全不变**。仅 `changes[]` 数组里 `oldValue` / `newValue` 字段的内容形态优化:

### 1. 价格日历操作日志只列本次档位字段(PR #2505)

**修复前**:用户只改"轻奢档"价格,oplog 同时记录"高端档"全字段为 DELETE
```
成人售价 [2026-05-09~05-22 (14 天)] [高端]:  4865 → —    ← 用户没改, 误报
儿童售价 [2026-05-09~05-22 (14 天)] [高端]:  1865 → —    ← 用户没改, 误报
```

**修复后**:同样操作,oplog 只列"轻奢档"字段
```
成人售价 [2026-05-29] [轻奢]:  5690 → 888
儿童售价 [2026-05-29] [轻奢]:  2620 → 888
单房差 [2026-05-29~05-30 (2 天)] [轻奢]:  0 → 500
```

测试服 e2e 实测产品 2046069754753335297 设置 tierSeq=1, oplog 7 条全部 [轻奢], 无 [高端]。

### 2. 长 List / Map 字段不再裸 JSON 展示(PR #2508)

**修复前**:装备建议 / 快速理解 / 儿童体验 / 成长收获 等 List<Map> 字段
```
装备建议:  已修改 ({"text":"111111","icon":"<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\"...h14a4 4 0 1 1 0 8h-1\"/><path d=\"M6 2v2\"/></svg>","color":"rgb(197, 20, 20)"})
            → 已修改 ([ProductSupplementSaveReqVO.EquipmentItem(text=111111, icon=<svg ...)
孩子经历:  [object Object] → —
```

**修复后**:Collection 给"已修改 (共 N 项)"摘要;Map 给"已修改";null/空给"未填写"
```
装备建议:  已修改 (共 1 项) → 已修改 (共 1 项)
孩子经历:  未填写 → 已修改 (共 2 项)
快速理解:  已修改 (共 3 项) → 已修改 (共 3 项)
```

### 3. 展示车型同 ID 不再误判"已修改"(PR #2508)

**修复前**:VO `List<Long>` 与 DO `Object`(JSON 反序列化为 List<Integer/String>)同 ID 但元素类型不同
```
展示车型:  2023765365569355778 → 2023765365569355778   ← 误判 MODIFY
```

**修复后**:`valuesEqual` Collection 分支不等时再走元素 stringify 顺序比较,同 ID 视为相等
- 不再产出 FieldChange 条目

---

## 服务端改动

### PR #2505 (Closes #2504)
- `ProductPricingService.batchSetPriceCalendar`: 查 oldList 加 `tierSeq == req.getTierSeq()` 过滤(空回退 DEFAULT_TIER_SEQ)
- 80 单测全绿(ProductPricingServiceTest 69 + 关联 11)

### PR #2508 (Closes #2507)
- `EntityFieldDiffUtil.friendlyLongDisplay(Object)`: 长字段友好展示统一入口
  - Collection → "已修改 (共 N 项)"
  - Map → "已修改"
  - String 短 → 原文,长 → 前80+后80摘要
  - null/空 → "未填写"
- `EntityFieldDiffUtil.hashIfNotNull(Object)`: hash 计算(Collection/Map 用 toString)
- `EntityFieldDiffUtil.valuesEqual`: Collection 分支不等时走 safeStringify(顺序保留) 兜底
- 编译通过(本地机器 commit charge 99% 满, surefire fork 不出, 用测试服 e2e 验证)

---

## 影响

- 前端不需要改代码,changes 字段类型不变(`Object`),只是值更友好
- 长 List 字段不再泄漏内部 SVG/JSON,审计上"内容真改了"靠 `oldHash`/`newHash` SHA-256 前 16 位鉴定
- 价格日历"我没改的档位被记 DELETE"的虚假日志彻底消除

---

## 部署

- 测试服: 已部署 (deploy task `e116c0b6` + `32469c73`)
- 正式服: 等 Deploy Panel /prod 触发
- nacos: 无配置变化
