diff --git a/changelogs/2026-05/15_feat_product_v2_operation_log_chinese_field_diff.md b/changelogs/2026-05/15_feat_product_v2_operation_log_chinese_field_diff.md new file mode 100644 index 0000000..8d7695c --- /dev/null +++ b/changelogs/2026-05/15_feat_product_v2_operation_log_chinese_field_diff.md @@ -0,0 +1,210 @@ +# product-v2: 产品操作日志全中文化 + 字段级 diff(旧值→新值) + +> **服务**: hl-product-service-v2 (端口 8083) +> **PR**: #2355 +> **Issue**: #2354 +> **日期**: 2026-05-15 +> **影响范围**: 管理端「产品 → 操作日志」侧边栏(admin.1814.love `/product/edit?id=X` 右侧抽屉) + +--- + +## ⚠️ 关键变化 + +`GET /admin/product/item/{id}/operation-logs` **出参新增 3 字段**(`actionLabel` / `stepLabel` / `changes`),接口路径、入参、HTTP 方法**完全没变**。 + +- 旧逻辑(mmg 现在):把 `action="EDIT"` / `step="STEP1"` 当字符串裸渲染,用户看不懂 +- 新逻辑:直接读 `actionLabel="编辑"` / `stepLabel="基础信息"` 即可;新增 `changes` 数组渲染字段差异表格 +- **老日志兼容**:2026-05-15 之前的历史日志 `changes` 字段为 `null`,前端**不渲染 diff 区块**,只显示 `actionLabel + detail`(向后兼容) + +--- + +## 一、背景 + +张老板 2026-05-15 反馈:产品 `2055138817265897473` 改价后无法追溯具体金额变化。 +- 旧版日志只记 `detail="价格区间设置 2026-07-12~2026-07-19"`,金额变化要靠 RDS binlog 还原(1 小时排查) +- 20 种 action 落库为英文 `EDIT/PRICE_SET/COMPLETE`,前端裸出导致每次都要询问含义 +- `operatorName` 列存在但 Service 没赋值,所有日志显示 null + +P0 解决:action 中文化 + 字段级 diff(前端表格可直接展示「成人售价 5800 → 9400」)。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 查产品操作日志 | GET | `/admin/product/item/{id}/operation-logs` | **出参 EXTEND** | 新增 actionLabel / stepLabel / changes 三字段 | + +--- + +## 三、接口详情 + +### 1. 查产品操作日志 `GET /admin/product/item/{id}/operation-logs` + +**VO**: `ProductOperationLogRespVO` + +#### 入参(无变化) + +| 字段 | 位置 | 类型 | 必填 | 说明 | +|------|------|------|------|------| +| id | Path | Long | ✅ | productId | + +#### 出参(新增 3 字段,加粗为新) + +```json +{ + "code": 200, + "data": [ + { + "id": "2055231522046218242", + "action": "EDIT", + "actionLabel": "编辑", + "step": "STEP1", + "stepLabel": "基础信息", + "detail": "基础信息", + "operatorId": "2033350100000000006", + "operatorName": "wangyu", + "createTime": "2026-05-15 18:19:14", + "changes": [ + { + "field": "name", + "fieldLabel": "产品名称", + "oldValue": "Tina空想2大1小 私人定制线路", + "newValue": "Tina空想2大1小 私人定制线路 8天7晚", + "changeType": "MODIFY" + }, + { + "field": "depositAmount", + "fieldLabel": "订金金额", + "oldValue": "1000.00", + "newValue": "1500.00", + "changeType": "MODIFY" + } + ] + }, + { + "id": "2055222984284573697", + "action": "PRICE_SET", + "actionLabel": "设置价格", + "step": "STEP4", + "stepLabel": "价格与班期", + "detail": "价格区间设置 2026-07-12~2026-07-19", + "operatorId": "2033350100000000006", + "operatorName": "wangyu", + "createTime": "2026-05-15 12:01:59", + "changes": [ + { + "field": "adultSellPrice", + "fieldLabel": "成人售价", + "oldValue": "5800.00", + "newValue": "9400.00", + "changeType": "MODIFY" + }, + { + "field": "childSellPrice", + "fieldLabel": "儿童售价", + "oldValue": "3000.00", + "newValue": "3980.00", + "changeType": "MODIFY" + } + ] + }, + { + "id": "2055216483381071873", + "action": "COMPLETE", + "actionLabel": "完成设计", + "step": null, + "stepLabel": null, + "detail": "", + "operatorId": "2033350100000000006", + "operatorName": "wangyu", + "createTime": "2026-05-15 17:43:16", + "changes": null + } + ] +} +``` + +#### `changes` 字段定义 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `changes` | `Array \| null` | 字段差异列表;老日志为 `null` 时不渲染 | +| `changes[].field` | `string` | 字段英文名(用于排序/筛选/调试) | +| `changes[].fieldLabel` | `string` | **字段中文名(前端直接展示这个)** | +| `changes[].oldValue` | `string \| number \| null` | 旧值(已转中文:枚举值如 `FULL` 已翻译为 `"全款"`) | +| `changes[].newValue` | `string \| number \| null` | 新值 | +| `changes[].changeType` | `"ADD" \| "MODIFY" \| "DELETE"` | ADD=新增(oldValue=null)/MODIFY=修改/DELETE=删除(newValue=null) | + +#### 本期接入字段级 diff 的 3 类操作 + +| action / step | 接入字段(5 个/类) | +|--------------|------| +| `PRICE_SET` / `BATCH_SAVE`(STEP4) | `adultSellPrice` / `childSellPrice` / `toddlerDiscount` / `dailyStock` / `priceType` | +| `EDIT` STEP1 | `name` / `subtitle` / `paymentType` / `depositAmount` / `defaultDailyStock` | +| 状态变更(`SUBMIT_PUBLISH`/`APPROVE`/`REJECT` 等) | `status`(旧→新状态值已翻译为中文) | + +**未接入的 action(changes=null,按老日志渲染)**:CREATE / EDIT STEP2-5 / COPY / DELETE / EDIT_MAP_AUTO / EDIT_MAP_MODE / SAVE_DETAIL_BLOCKS / TEAM_SAVE 等。 +**P1 后续**会全字段 diff 覆盖 STEP2-5 + 长文本摘要 + 集合 diff,单独发 changelog 通知。 + +--- + +## 四、前端改动建议(mmg) + +### 1. 日志侧边栏渲染逻辑(伪代码) + +```jsx +{logs.map(log => ( + +
+ {log.actionLabel} + {log.stepLabel && {log.stepLabel}} + {log.createTime} + {log.operatorName ?? '系统'} +
+ {log.detail &&
{log.detail}
} + {log.changes && log.changes.length > 0 && ( + + + + {log.changes.map(c => ( + + + + + + ))} + +
字段旧值新值
{c.fieldLabel}{c.oldValue ?? '—'}{c.newValue ?? '—'}
+ )} +
+))} +``` + +### 2. action 颜色建议(前端可自行决定) + +| 类别 | 取值 | 建议色 | +|------|------|--------| +| 价格类 | PRICE_SET / PRICE_DELETE / BATCH_SAVE / ADJUST_PRICE | 橙 | +| 状态类 | PUBLISH / SUBMIT_PUBLISH / APPROVE / DIRECT_PUBLISH | 绿 | +| 状态类(负向) | REJECT / WITHDRAW / UNPUBLISH / FORCE_UNPUBLISH / DELETE | 红 | +| 编辑类 | EDIT / EDIT_MAP_AUTO / EDIT_MAP_MODE | 蓝 | +| 其它 | CREATE / COPY / COMPLETE / TRANSFER / TEAM_SAVE | 灰 | + +--- + +## 五、向后兼容性 + +- ✅ 接口路径、入参、HTTP 方法、原有出参字段全部不变 +- ✅ 老日志 `changes=null`,前端 `if(changes)` 守卫即可 +- ✅ 后端可独立先行发布(前端不改也不会出错,只是不显示新功能) +- ✅ 新日志的 actionLabel/stepLabel 即使前端不读也不影响 + +**部署顺序**:Flyway 先行 → 后端 → 前端(任意顺序都安全)。 + +--- + +## 六、不在本期范围(P1/P2 后续单独工单) + +- **P1**:全字段 diff(>100 字段全覆盖)、长文本摘要策略、集合字段 diff(seasons/tags/tiers/days/feeItems) +- **P2**:「还原到此版本」按钮、跨产品聚合查询页、调价超阈值钉钉/企微告警