feat: 订单时间线 11 事件类型扩展 + 转单入参正式生效(管理后台 changelog)
覆盖 6 PR:#4422 #4426 #4434 #4449 #4454 #4464 关联 Issue:#4419 #4423 #4428 #4446 #4451 #4459
这个提交包含在:
父节点
98e35c6096
当前提交
c88cc66ffd
@ -0,0 +1,405 @@
|
||||
# 订单时间线 11 事件类型扩展 + 转单入参正式生效
|
||||
|
||||
- **变更类型**:修改接口
|
||||
- **端类型**:管理后台
|
||||
- **日期**:2026-06-26
|
||||
- **服务**:hl-order-service-v3
|
||||
- **关联 PR**:#4422 #4426 #4434 #4449 #4454 #4464
|
||||
- **关联 Issue**:#4419 #4423 #4428 #4446 #4451 #4459
|
||||
- **后端负责人**:yst
|
||||
|
||||
---
|
||||
|
||||
## ① 接口背景
|
||||
|
||||
订单时间线 Epic(6 PR 合并周期)带来两处接口变更:
|
||||
|
||||
1. **GET /v3/admin/order/{id}/status-log**:时间线出参 `eventType` 新增 11 个枚举值,覆盖增减项、合同、保险、退款、发票等业务动作,前端需按 eventType 渲染对应 label 与 content 文本。
|
||||
2. **PUT /v3/admin/order/{id}**(修改订单字段):原先 `targetConsultantId` + `transferReason` 两个字段静默忽略(后端收到但不执行),现在**正式生效**——传这两个字段即触发真实转单逻辑。⚠️ 若前端已在传这两个字段,行为将从「无效」变为「真改归属」。
|
||||
|
||||
---
|
||||
|
||||
## ② 变更清单
|
||||
|
||||
| 接口 | 变更类型 | 变更描述 |
|
||||
|---|---|---|
|
||||
| GET /v3/admin/order/{id}/status-log | ✨ 出参枚举扩展 | eventType 新增 11 个取值(详见 §⑥) |
|
||||
| GET /v3/admin/order/{id}/status-log | ✨ 出参新增触发场景 | INFO_ADJUST、REFUND_RECEIVED 新增触发场景(枚举值本身不变) |
|
||||
| PUT /v3/admin/order/{id} | ⚠️ 入参行为变更 | targetConsultantId + transferReason 从「静默忽略」变为「真转单」 |
|
||||
| PUT /v3/admin/order/{id} | ✨ 新增错误码 | 581042「目标定制师不存在或已停用」 |
|
||||
|
||||
---
|
||||
|
||||
## ③ 接口详情
|
||||
|
||||
### 接口 A:GET /v3/admin/order/{id}/status-log
|
||||
|
||||
- **描述**:获取订单详情记录 Tab 的时间线列表
|
||||
- **认证**:需要管理员 JWT(Bearer Token)
|
||||
- **幂等性**:只读,天然幂等
|
||||
- **限流**:无特殊限流
|
||||
|
||||
### 接口 B:PUT /v3/admin/order/{id}
|
||||
|
||||
- **描述**:修改订单字段(含转单字段)
|
||||
- **认证**:需要管理员 JWT(Bearer Token)
|
||||
- **幂等性**:非幂等;同一 targetConsultantId 重复调用不报错但无实际变化(归属已是目标定制师则不重复写时间线)
|
||||
- **限流**:无特殊限流
|
||||
|
||||
---
|
||||
|
||||
## ④ 接口入参
|
||||
|
||||
### 接口 A(GET /v3/admin/order/{id}/status-log)
|
||||
|
||||
#### 4.1 路径参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| id | Long | 是 | 订单 ID(雪花 ID,字符串形式传入) |
|
||||
|
||||
无请求体。
|
||||
|
||||
### 接口 B(PUT /v3/admin/order/{id})
|
||||
|
||||
#### 4.1 路径参数
|
||||
|
||||
| 参数 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| id | Long | 是 | 订单 ID |
|
||||
|
||||
#### 4.2 请求体字段(仅列出本次变更相关字段,其余字段不变)
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|---|---|---|---|
|
||||
| targetConsultantId | Long | 否 | 转单目标定制师的 adminId。不传或与当前一致则不转单;传入且与当前不同则触发真实转单。 |
|
||||
| transferReason | String | 否 | 转单原因备注,配合 targetConsultantId 使用;targetConsultantId 未传时忽略 |
|
||||
|
||||
---
|
||||
|
||||
## ⑤ 出参字段
|
||||
|
||||
### 接口 A(GET /v3/admin/order/{id}/status-log)
|
||||
|
||||
出参结构未变,records 数组每项字段如下:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|---|---|---|
|
||||
| records | Array | 时间线记录列表,按 changedAt 降序 |
|
||||
| records[].id | String | 记录 ID |
|
||||
| records[].eventType | String | 事件类型枚举(见 §⑥ 完整枚举表) |
|
||||
| records[].content | String | 事件描述文本(见 §⑥ content 样例,后端生成,直接渲染) |
|
||||
| records[].changedAt | String | 事件发生时间,ISO8601 格式 |
|
||||
| records[].operatorName | String | 操作人名称(系统触发时为「系统」) |
|
||||
| records[].fromStatus | String | 事件发生时订单粗状态快照 |
|
||||
| records[].toStatus | String | 事件发生后订单粗状态快照 |
|
||||
| records[].fromStatusName | String | fromStatus 中文名 |
|
||||
| records[].toStatusName | String | toStatus 中文名 |
|
||||
| records[].extra | Object | 附加结构化数据,按 eventType 不同而不同(可为 null) |
|
||||
|
||||
### 接口 B(PUT /v3/admin/order/{id})
|
||||
|
||||
出参结构不变,仍返回 `Result<Void>`(成功时 data 为 null,code=200)。
|
||||
|
||||
---
|
||||
|
||||
## ⑥ 枚举 / 数据字典
|
||||
|
||||
### eventType 完整枚举表(含本次新增)
|
||||
|
||||
| eventType 值 | label | content 文本样例 | 是否本次新增 |
|
||||
|---|---|---|---|
|
||||
| ORDER_CREATED | 创建订单 | 创建订单 | 否 |
|
||||
| ORDER_CONFIRMED | 确认订单 | 确认订单 | 否 |
|
||||
| ORDER_CANCELLED | 订单取消 | 订单取消 | 否 |
|
||||
| ORDER_TRIP_STARTED | 出行开始 | 出行开始 | 否 |
|
||||
| ORDER_TRIP_TERMINATED | 终止行程 | 终止行程 | 否 |
|
||||
| ORDER_COMPLETED | 订单完成 | 订单完成 | 否 |
|
||||
| PAYMENT_RECEIVED | 收款记录 | 收款 ¥6000(微信支付) | 否 |
|
||||
| PAYMENT_DEPOSIT | 定金收款 | 定金收款 ¥2000 | 否 |
|
||||
| TRAVELER_CHANGED | 出行人变更 | 新增出行人:张三 / 移除出行人:李四 | 否 |
|
||||
| INFO_ADJUST | 订单信息调整 | 修改客户信息 | 否(新增触发场景:改客户姓名/电话/紧急联系人/备注) |
|
||||
| REFUND_RECEIVED | 退款到账 | ¥6000 已到账(手动完成) | 否(新增触发场景:退款手动完成) |
|
||||
| ADJUSTMENT_ADD | 新增增减项 | 新增优惠 -¥500:VIP优惠 / 新增附加费 +¥500:船票升舱 | **是** |
|
||||
| ADJUSTMENT_REVERSE | 撤销增减项 | 撤销优惠 ¥500:录错 / 撤销附加费 ¥300:录错 | **是** |
|
||||
| CONSULTANT_TRANSFER | 转单 | 转单:张三→李四 | **是** |
|
||||
| CONTRACT_CREATED | 创建合同 | 已创建合同:HT202601001 / 已报备合同:HT202601001 | **是** |
|
||||
| CONTRACT_INVALIDATED | 作废合同 | 已作废合同:HT202601001 | **是** |
|
||||
| INSURANCE_PURCHASED | 投保 | 已投保 保游金标准方案(3人) | **是** |
|
||||
| INSURANCE_CANCELLED | 退保 | 已退保:BY2026001234 | **是** |
|
||||
| REFUND_REVIEWED | 退款审核 | 退款审核通过 ¥6000 / 退款审核驳回:金额超出实付 | **是** |
|
||||
| REFUND_EXECUTING | 退款发起 | 退款发起 ¥6000 | **是** |
|
||||
| INVOICE_APPLIED | 申请开票 | 申请开票:呼籁旅行科技有限公司 | **是** |
|
||||
|
||||
> **eventType 分类说明**:
|
||||
> - ADJUSTMENT_ADD / ADJUSTMENT_REVERSE:DATA 类(订单内数据变更),fromStatus/toStatus 为当时订单粗状态快照,不代表状态发生了迁移。
|
||||
> - CONSULTANT_TRANSFER:DATA 类,归属变更不触发订单状态迁移。
|
||||
> - CONTRACT_CREATED / CONTRACT_INVALIDATED / INSURANCE_PURCHASED / INSURANCE_CANCELLED / REFUND_REVIEWED / REFUND_EXECUTING / INVOICE_APPLIED:DATA 类,均为业务动作记录。
|
||||
|
||||
---
|
||||
|
||||
## ⑦ 错误码
|
||||
|
||||
### 接口 B(PUT /v3/admin/order/{id})新增错误码
|
||||
|
||||
| 错误码 | message | 触发场景 |
|
||||
|---|---|---|
|
||||
| 581042 | 目标定制师不存在或已停用 | targetConsultantId 对应的 admin 用户不存在,或该用户账号已被停用 |
|
||||
|
||||
### 已有错误码(保持不变,供参考)
|
||||
|
||||
| 错误码 | message | 说明 |
|
||||
|---|---|---|
|
||||
| 581001 | 订单不存在 | id 对应订单不存在 |
|
||||
| 400 | 参数校验失败 | 请求体字段类型/格式错误 |
|
||||
|
||||
---
|
||||
|
||||
## ⑧ 示例
|
||||
|
||||
### 8.1 典型成功示例
|
||||
|
||||
#### 接口 A:获取时间线(含新枚举值)
|
||||
|
||||
请求:
|
||||
```
|
||||
GET /v3/admin/order/1234567890123456789/status-log
|
||||
Authorization: Bearer <admin-token>
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": {
|
||||
"records": [
|
||||
{
|
||||
"id": "9876543210",
|
||||
"eventType": "ADJUSTMENT_ADD",
|
||||
"content": "新增优惠 -¥500:VIP优惠",
|
||||
"changedAt": "2026-06-25T14:30:00+08:00",
|
||||
"operatorName": "李定制",
|
||||
"fromStatus": "CONFIRMED",
|
||||
"toStatus": "CONFIRMED",
|
||||
"fromStatusName": "已确认",
|
||||
"toStatusName": "已确认",
|
||||
"extra": null
|
||||
},
|
||||
{
|
||||
"id": "9876543211",
|
||||
"eventType": "CONSULTANT_TRANSFER",
|
||||
"content": "转单:张三→李四",
|
||||
"changedAt": "2026-06-25T10:00:00+08:00",
|
||||
"operatorName": "系统",
|
||||
"fromStatus": "CONFIRMED",
|
||||
"toStatus": "CONFIRMED",
|
||||
"fromStatusName": "已确认",
|
||||
"toStatusName": "已确认",
|
||||
"extra": null
|
||||
},
|
||||
{
|
||||
"id": "9876543212",
|
||||
"eventType": "INSURANCE_PURCHASED",
|
||||
"content": "已投保 保游金标准方案(3人)",
|
||||
"changedAt": "2026-06-24T16:20:00+08:00",
|
||||
"operatorName": "系统",
|
||||
"fromStatus": "CONFIRMED",
|
||||
"toStatus": "CONFIRMED",
|
||||
"fromStatusName": "已确认",
|
||||
"toStatusName": "已确认",
|
||||
"extra": null
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 接口 B:转单成功
|
||||
|
||||
请求:
|
||||
```
|
||||
PUT /v3/admin/order/1234567890123456789
|
||||
Authorization: Bearer <admin-token>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"targetConsultantId": 987654321,
|
||||
"transferReason": "原定制师休假,转交李四跟进"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8.2 边界情况示例
|
||||
|
||||
#### 转单字段与当前归属相同(不触发转单,静默成功)
|
||||
|
||||
请求(当前归属已是 adminId=123456789):
|
||||
```json
|
||||
{
|
||||
"targetConsultantId": 123456789,
|
||||
"transferReason": "测试"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
不会写入 CONSULTANT_TRANSFER 时间线记录。
|
||||
|
||||
#### 不传转单字段(其他字段正常修改,归属不变)
|
||||
|
||||
请求:
|
||||
```json
|
||||
{
|
||||
"customerRemark": "客户备注更新"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"code": 200,
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 8.3 业务失败示例
|
||||
|
||||
#### targetConsultantId 目标定制师不存在或已停用
|
||||
|
||||
请求:
|
||||
```json
|
||||
{
|
||||
"targetConsultantId": 999999999,
|
||||
"transferReason": "转单测试"
|
||||
}
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"code": 581042,
|
||||
"msg": "目标定制师不存在或已停用",
|
||||
"data": null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⑨ 业务边界
|
||||
|
||||
### 适用场景
|
||||
|
||||
- 接口 A:任何状态的订单均可查询时间线(只读)。
|
||||
- 接口 B 转单:订单状态为非终态(未取消、未完成)时可转单;已取消或已完成订单会被业务守卫拦截。
|
||||
|
||||
### 不适用场景
|
||||
|
||||
- 不可通过时间线接口触发业务动作,时间线为只读查询。
|
||||
- 转单不可将订单归属给已停用的管理员账号(报 581042)。
|
||||
- transferReason 在 targetConsultantId 未传时无意义,后端忽略。
|
||||
|
||||
### 特殊边界
|
||||
|
||||
- ADJUSTMENT_ADD / ADJUSTMENT_REVERSE 的 fromStatus/toStatus 是当时订单粗状态的快照,这两个事件本身**不改变订单状态**,前端时间线渲染不需要显示状态迁移箭头。
|
||||
- CONSULTANT_TRANSFER 同上,仅归属变更,不改变订单主状态。
|
||||
- 时间线记录不可删除,历史订单在本次上线前发生的对应操作无时间线记录属正常现象(该类事件在此次上线前未记录)。
|
||||
- targetConsultantId 传 null 或不传,后端视为「不转单」,与历史行为完全一致。
|
||||
|
||||
---
|
||||
|
||||
## ⑩ 修改前后对比
|
||||
|
||||
### 接口 A:eventType 枚举值扩展
|
||||
|
||||
| 维度 | 修改前 | 修改后 |
|
||||
|---|---|---|
|
||||
| eventType 取值域 | 11 个基础值(ORDER_CREATED 等) | 在原有基础上新增 11 个值:ADJUSTMENT_ADD / ADJUSTMENT_REVERSE / CONSULTANT_TRANSFER / CONTRACT_CREATED / CONTRACT_INVALIDATED / INSURANCE_PURCHASED / INSURANCE_CANCELLED / REFUND_REVIEWED / REFUND_EXECUTING / INVOICE_APPLIED |
|
||||
| INFO_ADJUST 触发场景 | 仅原有场景 | 新增:改客户姓名/电话/紧急联系人/备注时写入 |
|
||||
| REFUND_RECEIVED 触发场景 | 仅原有场景 | 新增:退款手动标记完成时写入 |
|
||||
| 出参字段结构 | 不变 | 不变 |
|
||||
|
||||
### 接口 B:转单字段行为变更
|
||||
|
||||
| 维度 | 修改前 | 修改后 |
|
||||
|---|---|---|
|
||||
| targetConsultantId 传非空值行为 | **静默忽略**(后端接收但不执行任何操作) | **正式生效**:触发转单逻辑,改变订单归属定制师 |
|
||||
| transferReason 传值行为 | **静默忽略** | 正式生效:记录转单备注,写入 CONSULTANT_TRANSFER 时间线 |
|
||||
| 转单时间线记录 | 无 | 自动写入 CONSULTANT_TRANSFER 事件 |
|
||||
| 错误码 581042 | 不存在 | **新增**,targetConsultantId 校验失败时返回 |
|
||||
|
||||
---
|
||||
|
||||
## ⑪ 影响评估 / 回滚
|
||||
|
||||
### ⚠️ 破坏兼容风险(高优先级确认)
|
||||
|
||||
**接口 B 转单字段行为变更是破坏性变更。**
|
||||
|
||||
请前端确认:
|
||||
1. 现有「修改订单」表单是否已在传 `targetConsultantId` 字段?
|
||||
- 若**已在传**:上线后该字段会真实改变订单归属,需评估是否符合预期,或在前端侧暂时移除该字段传入,等转单 UI 正式上线再接入。
|
||||
- 若**未传**:无影响。
|
||||
2. `transferReason` 同上评估逻辑。
|
||||
|
||||
### 前端同步上线要求
|
||||
|
||||
- **接口 A 时间线**:新枚举值前端若不处理,时间线条目的 label/icon 会命中 fallback。建议按 §⑥ 枚举表逐一补充渲染逻辑(content 文本由后端返回,直接展示即可)。
|
||||
- **接口 B 转单**:确认现有表单是否已传转单字段,按需调整后再上线。
|
||||
|
||||
### 回滚方案
|
||||
|
||||
- 后端可通过 Nacos 开关关闭时间线新事件写入(不影响已有记录)。
|
||||
- 接口 B 转单生效逻辑如需紧急回滚,联系后端 yst 单独发布回滚版本。
|
||||
|
||||
---
|
||||
|
||||
## ⑫ 注意事项
|
||||
|
||||
1. **时间线 content 文本由后端生成**,前端直接渲染 records[].content 字符串,无需前端拼接或翻译枚举值。
|
||||
2. **eventType 枚举将随业务持续扩展**,前端建议用 map/switch 处理已知值,对未知 eventType 做 fallback 渲染(展示 content 文本而不崩溃)。
|
||||
3. **INFO_ADJUST 和 REFUND_RECEIVED 并非新枚举值**,只是新增了触发场景。历史订单在本次上线前对应操作无时间线记录,属预期行为。
|
||||
4. **接口 B 转单字段传 null 或不传**等同于「不转单」,与历史行为一致。
|
||||
5. **转单不改变订单主状态**,前端进度条/状态显示无需因转单刷新。
|
||||
|
||||
---
|
||||
|
||||
## ⑬ 关联 / 联系人
|
||||
|
||||
### Issue
|
||||
|
||||
- [#4419](https://git.1814.love:8443/wx/HL/issues/4419)
|
||||
- [#4423](https://git.1814.love:8443/wx/HL/issues/4423)
|
||||
- [#4428](https://git.1814.love:8443/wx/HL/issues/4428)
|
||||
- [#4446](https://git.1814.love:8443/wx/HL/issues/4446)
|
||||
- [#4451](https://git.1814.love:8443/wx/HL/issues/4451)
|
||||
- [#4459](https://git.1814.love:8443/wx/HL/issues/4459)
|
||||
|
||||
### PR
|
||||
|
||||
- [#4422](https://git.1814.love:8443/wx/HL/pulls/4422)
|
||||
- [#4426](https://git.1814.love:8443/wx/HL/pulls/4426)
|
||||
- [#4434](https://git.1814.love:8443/wx/HL/pulls/4434)
|
||||
- [#4449](https://git.1814.love:8443/wx/HL/pulls/4449)
|
||||
- [#4454](https://git.1814.love:8443/wx/HL/pulls/4454)
|
||||
- [#4464](https://git.1814.love:8443/wx/HL/pulls/4464)
|
||||
|
||||
### 后端负责人
|
||||
|
||||
yst(腰苏图)
|
||||
正在加载...
x
在新工单中引用
屏蔽一个用户