# 订单时间线 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`(成功时 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 ``` 响应: ```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 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(腰苏图)