docs(changelog): #8181 线下收款废除报账人代收DRIVER_CASH渠道——options出参channels移除DRIVER_CASH+register新增520417(尾款收款模型重构PR-1,修改接口-管理后台)
changelog-filename-gate / validate (push) Failing after 2s

这个提交包含在:
yaosutu
2026-09-22 17:07:15 +08:00
父节点 165cbcb69a
当前提交 a0413a53be
@@ -0,0 +1,369 @@
---
schema: "hl-changelog/v2"
ticket: "8181"
title: "线下收款废除报账人代收 DRIVER_CASH 渠道(尾款收款模型重构 PR-1)"
consumer: "admin"
author: "yst(GIT)"
change_type: "修改接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: "v2.1"
verified_at: ""
status_note: "backend_status: merged - 已合 dev-v3(PR #8183,merge commit a1fb7bc5ef),未部署测试服; gateway_status: not_required - 零网关改动,/v3/admin/order/** 走 hl-gateway 既有通配路由; frontend_status: pending - 前端适配情况未知,需排查 options 消费方是否硬编码 DRIVER_CASH"
updated_at: "2026-09-22"
base: "dev-v3"
---
# 线下收款废除「报账人代收 DRIVER_CASH」渠道(尾款收款模型重构 PR-1,#8181)
> 订单线下收款登记的「报账人收款(DRIVER_CASH)」渠道下线:登记选项接口不再返回该渠道,登记接口传 DRIVER_CASH 直接报新错误码 520417。历史 DRIVER_CASH 收款记录的查询、撤销不受影响。
## ① 接口背景
此前管理后台给订单补录线下收款时有三个渠道:定制师代收、对公转账、报账人收款(司机/导游等报账人手持现金收尾款)。「报账人代收尾款」模式资金不入公账、对账困难,尾款收款模型重构(Epic #8181)将其废除,改为「确认行程时指定主报账人,尾款挂其代收债务」的新链路(后续 PR 上线)。
本 PR 是该重构的第 1 步:只下线 DRIVER_CASH 的**登记入口**,存量数据完全兼容。
## ② 变更清单
| 类型 | 接口 | 变更 |
|---|---|---|
| 修改(出参结构收缩) | `GET /v3/admin/order/{orderId}/payment/manual-receipt/options` | 出参 `channels[]` 从 3 项变 2 项,**移除 DRIVER_CASH 渠道项** |
| 修改(行为变更 + 新错误码) | `POST /v3/admin/order/{orderId}/payment/manual-receipt` | `channel=DRIVER_CASH` 不再可登记,返回 **520417**(此前可正常登记尾款) |
| 不变 | `GET /v3/admin/order/{orderId}/payment/manual-receipt` 列表 | 历史 DRIVER_CASH 记录照常返回 |
| 不变 | `DELETE /v3/admin/order/{orderId}/payment/manual-receipt/{receiptId}` 撤销 | 历史 DRIVER_CASH 记录照常可撤销 |
## ③ 接口详情
### 3.1 查询线下收款登记选项
```
GET /v3/admin/order/{orderId}/payment/manual-receipt/options
```
- 使用场景:订单详情「登记线下收款」弹窗打开时拉取,渲染渠道 tabs + 各渠道可选款项/代收人/收款方式
- 认证:管理后台 JWT(hl-gateway 统一鉴权)
- 权限:订单归属校验(订单顾问/财务相关角色可读),车务管理员(VEHICLE_MANAGER)可读;房源角色(HOUSE)禁止
- 幂等性:只读接口,天然幂等
- 限流:走网关默认限流,无单独配额
### 3.2 登记线下收款
```
POST /v3/admin/order/{orderId}/payment/manual-receipt
```
- 使用场景:线下收到款项后补录(对公转账到账、定制师代收款),同事务推进订单 `paid_amount` / `pay_status`
- 认证:管理后台 JWT
- 权限:需线下收款写权限(结算写守卫)
- 幂等性:非幂等,每次成功调用新增一条收款凭据并累加已付金额;**重复提交会重复收款**,前端须防连击
- 事务:凭据落库与订单已付金额累加在同一事务,不会出现半成功状态
## ④ 入参
### 4.1 选项接口入参
| 参数 | 位置 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| `orderId` | path | Long | 是 | 订单 ID |
无 query / body 参数。
### 4.2 登记接口请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `channel` | string | 是 | 收款渠道:`CONSULTANT_COLLECTION`(定制师代收)/ `BANK_TRANSFER`(对公转账)。⚠️ `DRIVER_CASH` 已下线,传了返回 520417 |
| `payType` | string | 是 | 款项类型:`DEPOSIT`(订金)/ `BALANCE`(尾款)/ `FULL`(全款) |
| `amount` | number | 是 | 收款金额,必须 > 0,不能超过当前可收余额 |
| `receivedAt` | string(datetime) | 否 | 收款时间,可补录过去时间;不传默认当前时间 |
| `transferRef` | string | 条件必填 | 对公转账流水号,`BANK_TRANSFER` 渠道必填 |
| `receiptMethod` | string | 否 | 收款方式(字典 `manual_receipt_method`),`BANK_TRANSFER` 为空 |
| `collectorStaffId` | Long | 否 | 代收人 assignmentId。原为 DRIVER_CASH 必填,该渠道下线后登记链路不再使用 |
| `collectorType` | string | 否 | 实际代收人类型 `ORDER_STAFF`/`CONSULTANT`/`COMPANY_ACCOUNT`;不传按 channel 兼容推导 |
| `voucherUrls` | string[] | 否 | 凭证图片 URL 列表 |
| `remark` | string | 否 | 备注,最长 500 字 |
## ⑤ 出参
### 5.1 选项接口出参 `ManualReceiptOptionsVO`
| 字段 | 类型 | 说明 |
|---|---|---|
| `channels` | array | 收款渠道选项。**本次变更后固定 2 项:`CONSULTANT_COLLECTION`、`BANK_TRANSFER`**(顺序即此顺序) |
`channels[]` 元素 `ChannelOption`:
| 字段 | 类型 | 说明 |
|---|---|---|
| `channel` | string | 渠道值 |
| `channelText` | string | 渠道显示文案(「定制师代收」/「对公转账」) |
| `allowedPayTypes` | string[] | 当前订单状态下允许的款项类型,空数组 = 该渠道不可用 |
| `disabled` | boolean | 是否禁用,true 时前端置灰 |
| `disabledReason` | string | 禁用原因,`disabled=true` 时有值 |
| `collectors` | array | 该渠道可选代收人(见下表);`BANK_TRANSFER` 恒为空 |
| `receiptMethods` | array | 该渠道可选收款方式(见下表);`BANK_TRANSFER` 恒为空 |
`collectors[]` 元素 `CollectorOption`:
| 字段 | 类型 | 说明 |
|---|---|---|
| `collectorType` | string | `ORDER_STAFF` / `CONSULTANT` / `COMPANY_ACCOUNT` |
| `collectorId` | Long(string) | 代收人 ID:`CONSULTANT` 为 adminId |
| `collectorName` | string | 代收人姓名 |
| `collectorRole` | string | 代收人角色 |
| `collectorRoleText` | string | 角色显示文案 |
| `defaultSelected` | boolean | 是否默认选中 |
`receiptMethods[]` 元素 `OptionItem`:
| 字段 | 类型 | 说明 |
|---|---|---|
| `value` | string | 选项值(字典 `manual_receipt_method`) |
| `label` | string | 显示文案 |
| `defaultSelected` | boolean | 是否默认选中(首项为 true) |
### 5.2 登记接口出参 `ManualReceiptVO`
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | Long(string) | 收款凭据 ID(雪花,String 防 JS 精度丢失) |
| `orderId` | Long(string) | 订单 ID |
| `channel` | string | 收款渠道值 |
| `channelLabel` | string | 渠道显示标签 |
| `payType` | string | 款项类型值 |
| `payTypeLabel` | string | 款项类型显示标签 |
| `amount` | number | 收款金额 |
| `receivedAt` | string(datetime) | 收款时间 |
| `collectorStaffId` | Long(string) | 代收人 assignmentId(历史 DRIVER_CASH 行才有值) |
| `collectorStaffName` | string | 代收人姓名(历史 DRIVER_CASH 行才有值) |
| `collectorType` | string | 实际代收人类型 |
| `collectorAdminId` | Long(string) | 定制师/管理员代收人 ID |
| `collectorName` | string | 通用代收人姓名快照 |
| `collectorRole` | string | 通用代收人角色快照 |
| `transferRef` | string | 对公转账流水号(BANK_TRANSFER) |
| `receiptMethod` | string | 收款方式值 |
| `receiptMethodLabel` | string | 收款方式显示标签 |
| `voucherUrls` | string[] | 凭证图片 URL 列表 |
| `remark` | string | 备注 |
| `operatorName` | string | 登记人姓名 |
| `createTime` | string(datetime) | 登记时间 |
| `voided` | boolean | 是否已撤销 |
| `voidedByName` | string | 撤销人姓名 |
| `voidedAt` | string(datetime) | 撤销时间 |
| `voidReason` | string | 撤销原因 |
| `paidAmountAfter` | number | 登记/撤销后订单累计已付金额(仅登记/撤销响应有值,列表查询为 null) |
| `payStatusAfter` | string | 登记/撤销后订单支付状态(同上) |
## ⑥ 枚举 / 数据字典
**收款渠道 `channel`**:
| 值 | 含义 | 本次变化 |
|---|---|---|
| `CONSULTANT_COLLECTION` | 定制师代收 | 不变 |
| `BANK_TRANSFER` | 对公转账 | 不变 |
| `DRIVER_CASH` | 报账人收款 | **已下线**:options 不再返回、register 拒绝(520417)。⚠️ 但列表接口的历史数据仍可能出现该值(channelLabel=「报账人收款」),渲染映射不能删 |
**款项类型 `payType`**:`DEPOSIT` 订金 / `BALANCE` 尾款 / `FULL` 全款(不变)。
**代收人类型 `collectorType`**:`ORDER_STAFF` / `CONSULTANT` / `COMPANY_ACCOUNT`(不变)。
**收款方式 `receiptMethod`**:走数据字典 `manual_receipt_method`(如 `WECHAT_TRANSFER` 微信转账、`CASH` 现金收款),以 options 接口实际返回为准。
## ⑦ 错误码
| 码 | 语义 | 触发场景 |
|---|---|---|
| **520417** | 报账人代收尾款已下线,请通过确认行程挂账主报账人代收 | **本次新增**:register 传 `channel=DRIVER_CASH` |
| 520401 | 收款渠道非法: {0} | register 传了枚举外的渠道值 |
| 520402 | 对公转账渠道必须填写转账流水号 | BANK_TRANSFER 缺 `transferRef` |
| 520407 | 订单已取消,不允许登记线下收款 | 订单状态 CANCELLED |
| 520412 | 订单没有可用定制师,不能登记定制师代收 | CONSULTANT_COLLECTION 但订单无定制师 |
| 520413 | 本次收款金额超过当前可收余额 | 金额超可收余额 |
| 520415 | 收款方式不能为空 | 渠道要求 receiptMethod 但未传 |
| 520416 | 收款方式非法: {0} | receiptMethod 不在字典内 |
| 520405 | 线下收款凭据不存在 | 撤销时 receiptId 无效或不属本单 |
| 520406 | 该收款凭据已撤销,无法重复操作 | 重复撤销 |
| 520414 | 当前订单状态禁止撤销收款 | 当前订单/流程状态不允许撤销 |
> 注:原 DRIVER_CASH 专属错误码 520403(缺代收人)/ 520404(代收人不属本单)/ 520410(只能登尾款)/ 520411(必须选报账人)因入口下线在 register 链路不再可达,前端无需再处理这几个码(保留兼容无害)。
## ⑧ 示例
### 8.1 典型:options 返回 2 个渠道 + BANK_TRANSFER 登记成功
请求 `GET /v3/admin/order/2100123456789012345/payment/manual-receipt/options`(订单状态非待支付、尾款未收齐):
```json
{
"code": 200,
"success": true,
"data": {
"channels": [
{
"channel": "CONSULTANT_COLLECTION",
"channelText": "定制师代收",
"allowedPayTypes": ["BALANCE"],
"disabled": false,
"disabledReason": null,
"collectors": [
{
"collectorType": "CONSULTANT",
"collectorId": "2090001111222233334",
"collectorName": "王定制",
"collectorRole": "CONSULTANT",
"collectorRoleText": "定制师",
"defaultSelected": true
}
],
"receiptMethods": [
{ "value": "WECHAT_TRANSFER", "label": "微信转账", "defaultSelected": true },
{ "value": "CASH", "label": "现金收款", "defaultSelected": false }
]
},
{
"channel": "BANK_TRANSFER",
"channelText": "对公转账",
"allowedPayTypes": ["BALANCE"],
"disabled": false,
"disabledReason": null,
"collectors": [],
"receiptMethods": []
}
]
}
}
```
请求 `POST /v3/admin/order/2100123456789012345/payment/manual-receipt`:
```json
{
"channel": "BANK_TRANSFER",
"payType": "BALANCE",
"amount": 3000.00,
"transferRef": "GZL20260922001",
"remark": "客户对公转账尾款"
}
```
响应:
```json
{
"code": 200,
"success": true,
"data": {
"id": "2102999888777666555",
"orderId": "2100123456789012345",
"channel": "BANK_TRANSFER",
"channelLabel": "对公转账",
"payType": "BALANCE",
"payTypeLabel": "尾款",
"amount": 3000.00,
"receivedAt": "2026-09-22T17:00:00",
"transferRef": "GZL20260922001",
"collectorType": "COMPANY_ACCOUNT",
"voided": false,
"paidAmountAfter": 8800.00,
"payStatusAfter": "FULLY_PAID"
}
}
```
### 8.2 边界:订单无定制师 → 定制师代收渠道置灰
订单无定制师快照时,`CONSULTANT_COLLECTION` 渠道 `collectors` 为空且禁用:
```json
{
"channel": "CONSULTANT_COLLECTION",
"channelText": "定制师代收",
"allowedPayTypes": [],
"disabled": true,
"disabledReason": "订单没有可用定制师",
"collectors": [],
"receiptMethods": [
{ "value": "WECHAT_TRANSFER", "label": "微信转账", "defaultSelected": true },
{ "value": "CASH", "label": "现金收款", "defaultSelected": false }
]
}
```
尾款已收齐时各渠道 `allowedPayTypes=[]`、`disabled=true`、`disabledReason="尾款已收齐,无可收余额"`;订单已取消时 `disabledReason="订单已取消,不能登记收款"`。
### 8.3 业务失败:DRIVER_CASH 登记被拒(520417)
请求:
```json
{
"channel": "DRIVER_CASH",
"payType": "BALANCE",
"amount": 2000.00,
"collectorStaffId": "2099888777666555444",
"receiptMethod": "CASH"
}
```
响应:
```json
{
"code": 520417,
"success": false,
"message": "报账人代收尾款已下线,请通过确认行程挂账主报账人代收"
}
```
不会落库任何收款记录,订单金额无变化。
## ⑨ 业务边界
- **适用**:订单收到线下款项后补录(订金/全款仅限待支付状态,尾款在非待支付、非已取消状态);定制师代收、对公转账两种渠道照常可用。
- **不适用**:报账人(司机/导游)手持现金收尾款的场景——此入口已废除,改走「确认行程挂账主报账人代收」链路(后续 PR 上线,前端关注后续 changelog)。
- **存量兼容**:历史已登记的 DRIVER_CASH 收款记录正常返回在列表接口、正常可撤销,撤销后 `paid_amount` 精确回退,行为与旧版一致。
- **可收余额**:`amount` 不能超过订单当前可收余额(520413);可收余额 ≤ 0 时各渠道 `allowedPayTypes` 为空且置灰。
- **登记接口非幂等**:重复点击会产生重复收款记录,前端须做按钮防重。
## ⑩ 修改前后对比
### 字段/结构级
| 项 | 修改前 | 修改后 |
|---|---|---|
| options 出参 `channels[]` | `[CONSULTANT_COLLECTION, BANK_TRANSFER, DRIVER_CASH]`(3 项,DRIVER_CASH 项内含本单报账人候选 collectors) | `[CONSULTANT_COLLECTION, BANK_TRANSFER]`(2 项,**DRIVER_CASH 项整体移除**) |
| register 入参 `channel=DRIVER_CASH` | 合法值,可登记尾款(仅 BALANCE,需 collectorStaffId 属本单报账人) | 非法值,一律返回 520417,不落库 |
### 行为级
| 项 | 修改前 | 修改后 |
|---|---|---|
| 报账人代收尾款登记 | 可在订单详情登记 | 入口下线,需走确认行程挂账主报账人代收(后续 PR) |
| 历史 DRIVER_CASH 记录查询/撤销 | 可查可撤 | 不变,仍可查可撤 |
| 订金/全款/尾款的定制师代收、对公转账补录 | 可用 | 不变,照常可用 |
## ⑪ 影响评估 / 回滚
- **破坏兼容**:是。options 出参结构收缩(少一个渠道项)+ register 对 DRIVER_CASH 的行为从成功变为报错。
- **前端同步上线要求**:前端必须先排查并适配(见 ⑫),否则若按旧 channels 结构硬编码索引/枚举映射,渠道 tabs 渲染或默认值逻辑可能异常;若仍向用户展示 DRIVER_CASH 入口,登记会收到 520417。
- **后端兼容**:已合 dev-v3,未部署测试服;列表/撤销接口对历史数据完全兼容。
- **回滚方案**:代码回滚至本 PR 前版本即恢复 DRIVER_CASH 渠道;下线期间不会产生新 DRIVER_CASH 数据,回滚无数据迁移负担。
## ⑫ 注意事项
1. **前端排查 options 消费方是否硬编码 DRIVER_CASH**:检查渠道 tabs 渲染、渠道枚举映射、默认渠道选中逻辑、channel 文案映射表(`DRIVER_CASH → 报账人收款`)等位置。options 不再返回该渠道,任何按「必有 3 项 / 必有 DRIVER_CASH」假设的代码都要清理。
2. **列表接口渲染映射不能删 DRIVER_CASH**:历史收款记录仍可能返回 `channel=DRIVER_CASH`、`channelLabel=报账人收款`,列表/详情的展示映射需保留该值,否则历史行显示异常。
3. 新错误码 520417 的 message 可直接透出给用户(文案即操作指引)。
4. 原 DRIVER_CASH 专属错误码 520403/520404/520410/520411 在 register 链路不再可达,前端可不再处理。
## ⑬ 关联 / 联系人
- Issue:https://git.1814.love:8443/wx/HL/issues/8181
- PR:https://git.1814.love:8443/wx/HL/pulls/8183
- Commit:https://git.1814.love:8443/wx/HL/commit/a1fb7bc5ef6befa409696fdba7c1d98b840a7c4a
- 后端负责人:yst