20 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8809 | 出纳付款联动司导收款账户(选已建档账户 / 手填自动建档,报账款线) | admin | yst | 修改接口 | merged | not_required | implemented | hl-admin(claude-opus-4-8) | 1f96d87b5df0c4e56e9db65f43856b96fb849269 | v2.1 | 2026-10-09 | 后端 PR #8810 已合并 dev-v3(squash e50a43b72a),含 Flyway 迁移 V20261009_136(fin_reimburse 加收款账户两列 + fin_payee_account 唯一索引),需部署 order-v3 后生效;存量报账单新字段为 null。前端已交付:付款弹窗 REIMBURSE 门控加收款账户「选已建档(list-by-payee 下拉+默认预选)/手填建档」二选一,CASH 隐藏清空,账户类型按付款方式过滤;台账加收款账户留痕列、报账详情加快照展示;checkpoint 全量通过 | 2026-10-09 | dev-v3 |
出纳付款联动司导收款账户(报账款线)
面向:管理后台前端(hl-admin) 日期:2026-10-09 | 后端:hl-finance(编译进 order-v3 同进程,端口 8086) 范围:只动 REIMBURSE 报账款付款线;其余 7 条出纳付款线(advance / company-loan / expense / payment / nonbiz 等)入参壳共用但对新字段静默忽略,行为零变化。
1. 接口背景
报账款是出纳付款给司导/报账人的钱。此前出纳付款表单只有「公司出账账户」(payAccountId),没有「司导收款账户」——出纳把钱打到哪张卡/哪个收款码全靠线下沟通,系统无留痕。
本次让出纳在付款弹窗里:
- 选账户:下拉列出该报账人已建档的收款账户(新接口 list-by-payee 提供数据源,队列行已自动带出默认账户);
- 手填建档:现场登记新收款账户,付款成功同时自动落入收款账户主数据(fin_payee_account),账户名强制取服务人员档案真名,不允许手填名称。
付款成功后台账、报账详情均可回查「这单钱打给了哪个账户」。
2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 报账款登记付款 | POST | /admin/finance/cashier/reimburse/pay | 修改接口 | 入参新增 payeeAccountId 与嵌套 newPayeeAccount(二选一);出参加 payeeAccountId / payeeAccountLabel |
| 2 | 按收款方查账户全集 | GET | /admin/finance/payee-accounts/list-by-payee | 新增接口 | 付款弹窗「选账户」下拉数据源,轻量不分页 |
| 3 | 报账款待付款队列 | GET | /admin/finance/cashier/reimburse/queue | 修改接口 | 出参加 bizType / reporterAssignmentId / reporterRefId / reporterPayeeType / defaultPayeeAccountId / defaultPayeeAccountLabel |
| 4 | 出纳台账 | GET | /admin/finance/cashier/payments/page | 修改接口 | 出参行(仅 REIMBURSE 行)回填 payeeAccountId / payeeAccountLabel 留痕 |
| 5 | 报账详情 | GET | /admin/finance/reimburses/{id} | 修改接口 | 出参加 payeeAccountId / payeeAccountSnapshot 留痕 |
其余出纳付款线(advance / company-loan / expense / payment / nonbiz 等 7 条)的 /pay 接口:入参壳上能看到 payeeAccountId / newPayeeAccount 两个字段,但传了会被静默忽略,不报错,行为与之前完全一致——前端不要在这些页签渲染收款账户控件。
3. 接口详情
3.1 报账款登记付款 POST /admin/finance/cashier/reimburse/pay
- 使用场景:出纳在「财务出纳-报账款」页签对待付款报账单(status=APPROVED 且 direction=PAYABLE)执行付款,记资金流水 OUT + 回写 PAID。
- 认证:需管理后台 JWT。
- 幂等性:有。重复付款由 CAS 条件更新兜底(重复提交报 598602 状态冲突),重复手填建档由收款账户唯一索引兜底(不重复建档)。
- 限流:无。
3.2 按收款方查账户全集 GET /admin/finance/payee-accounts/list-by-payee
- 使用场景:付款弹窗打开时,用队列行的 reporterPayeeType + reporterRefId 拉该报账人全部收款账户,渲染「选账户」下拉。
- 认证:需管理后台 JWT。
- 幂等性:只读。
- 限流:无。
3.3 报账款待付款队列 GET /admin/finance/cashier/reimburse/queue
- 使用场景:报账款页签列表;行数据现在直接带主报账人档案信息与默认收款账户,前端打开付款弹窗时无需二次查询即可预选默认账户。
- 认证 / 幂等 / 限流:同上,只读。
4. 接口入参
4.1 付款入参 CashierPayFormReqVO(变化后全量,仅报账款线语义)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| bizId | String(Long) | 是 | 业务单据ID(报账单ID) |
| payAccountId | String(Long) | 是 | 出账公司账户ID(fin_fund_account) |
| payMethod | String | 否 | 付款方式(字典 fin_pay_way:CASH 现金 / BANK 银行转账 / THIRD_PARTY 三方支付) |
| payChannel | String | 条件 | 付款渠道(WXPAY / ALIPAY;仅 payMethod=THIRD_PARTY 时传,不超过 20 字符) |
| amount | Number | 是 | 付款金额(大于 0,须等于单据结算金额,不一致 598610 硬拦) |
| fee | Number | 否 | 手续费(不小于 0) |
| voucherNo | String | 否 | 付款凭证号 |
| voucherUrl | String | 否 | 付款凭证影像 URL |
| payDate | Date | 是 | 付款日期(yyyy-MM-dd,可回溯补录) |
| payeeAccountId | String(Long) | 条件必填 | 新增:已建档收款账户ID。payMethod=BANK/THIRD_PARTY 时与 newPayeeAccount 二选一必填;CASH 时禁传(598612) |
| newPayeeAccount | Object | 条件必填 | 新增:手填建档对象,与 payeeAccountId 互斥(同传 598612),子字段见 4.2 |
4.2 newPayeeAccount 子字段(手填建档)
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| payeeRefId | String(Long) | 是 | 服务人员档案ID(司机=driverId,其余角色=staffId)。强制挂档案,不允许纯手填名称 |
| accountType | String | 是 | 账户类型:WECHAT_QR / ALIPAY_QR / BANK_CARD(对公 CORP_ACCOUNT 在报账款线被拦,见 §6) |
| qrUrl | String | 条件 | 收款码图 URL(QR 类账户必填) |
| bankAccount | String | 条件 | 银行卡号(BANK_CARD 必填) |
| bankName | String | 否 | 开户行 |
不传 payeeName:账户名一律以服务人员档案真名覆盖,出纳手填的名称不生效(防止同名串账)。
4.3 list-by-payee 查询参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| payeeType | String | 是 | 收款方类型:DRIVER / GUIDE / PHOTOGRAPHER / LEADER(直接取队列行 reporterPayeeType) |
| payeeRefId | Long | 是 | 关联ID(司机=driverId,其余=staffId;直接取队列行 reporterRefId) |
4.4 队列入参
无变化:仅分页参数 page / pageSize。
5. 出参字段
统一 Result 包装;分页为 PageResult(records / total / page / pageSize)。Long 主键一律以 JSON 字符串返回。
5.1 付款响应 CashierPayRespVO(变化后)
| 字段 | 类型 | 说明 |
|---|---|---|
| flowId | String(Long) | 资金流水ID |
| flowNo | String | 资金流水单号 |
| balanceAfter | Number | 出账账户付后余额 |
| bizId | String(Long) | 业务单据ID(已回写 PAID) |
| payeeAccountId | String(Long),可空 | 新增:实际落账的收款账户ID(手填建档场景返回新建ID);现金付款为 null |
| payeeAccountLabel | String,可空 | 新增:收款账户快照(类型+脱敏尾号+收款人名,如「银行卡 ****0123 巴特尔」);现金付款为 null |
5.2 list-by-payee 响应(PayeeAccountRowRespVO 数组)
| 字段 | 类型 | 说明 |
|---|---|---|
| id | String(Long) | 收款账户ID |
| payeeType | String | 收款方类型 |
| payeeRefId | String(Long) | 关联ID |
| payeeName | String | 收款方名称(档案真名快照) |
| accountType | String | 账户类型:WECHAT_QR / ALIPAY_QR / BANK_CARD / CORP_ACCOUNT |
| qrUrl | String | 收款码图 URL |
| bankAccount | String | 银行卡号/对公账号(展示层脱敏) |
| bankName | String | 开户行 |
| isDefault | Integer | 是否默认账户:0 否 / 1 是 |
5.3 队列行 ReimburseQueueRowRespVO(新增字段)
| 字段 | 类型 | 说明 |
|---|---|---|
| bizType | String | 新增:业务对象类型(ORDER 订单 / GROUP_BATCH 团期批次) |
| reporterAssignmentId | String(Long),可空 | 新增:主报账人人员分配ID(多态口径,无主报账人为空)。注意:这是分配行ID,不能直接当 payeeRefId 用 |
| reporterRefId | String(Long),可空 | 新增:主报账人档案ID(司机=driverId,其余=staffId),后端已按 bizType 反查好,前端直接用 |
| reporterPayeeType | String,可空 | 新增:主报账人收款方类型(DRIVER/GUIDE/PHOTOGRAPHER/LEADER;不可映射角色为空) |
| defaultPayeeAccountId | String(Long),可空 | 新增:该报账人默认收款账户ID(无默认账户为空) |
| defaultPayeeAccountLabel | String,可空 | 新增:默认账户标签(类型+脱敏尾号,如「银行卡 ****0123」;无默认账户为空) |
其余既有字段(reimburseId / bizNo / reporterName / settleAmount / status 等)无变化。
5.4 台账行 / 报账详情(留痕字段)
- 台账 GET /admin/finance/cashier/payments/page 行(CashierPaymentRowRespVO)新增 payeeAccountId(String 可空)+ payeeAccountLabel(String 可空),仅 REIMBURSE 报账款行有值,其余付款线恒 null。
- 报账详情 GET /admin/finance/reimburses/{id} 新增 payeeAccountId(String 可空)+ payeeAccountSnapshot(String 可空,类型+脱敏尾号+收款人名)。现金付款两字段为 null。
6. 枚举 / 数据字典
6.1 账户类型 accountType(PayeeAccountTypeEnum)
| 码值 | 中文 | 报账款线可用 |
|---|---|---|
| WECHAT_QR | 微信收款码 | 可用 |
| ALIPAY_QR | 支付宝收款码 | 可用 |
| BANK_CARD | 银行卡 | 可用 |
| CORP_ACCOUNT | 对公账户 | 不可用(报账款对私,手填传对公被拦 595202) |
6.2 收款方类型 payeeType / reporterPayeeType
DRIVER 司机 / GUIDE 导游 / PHOTOGRAPHER 摄影师 / LEADER 领队。角色映射:GUIDE_ASSISTANT(副导游)归并为 GUIDE;其余不可映射角色 reporterPayeeType 为空,付款时选账户会被归属校验拦(598614)。
6.3 付款方式 payMethod(字典 fin_pay_way,无变化)
CASH 现金 / BANK 银行转账 / THIRD_PARTY 三方支付。
6.4 账户类型与付款方式强校验(已拍板硬拦)
| payMethod | 允许的账户类型 | 错位报错 |
|---|---|---|
| BANK | BANK_CARD | 598615 |
| THIRD_PARTY | WECHAT_QR / ALIPAY_QR | 598615 |
| CASH | 不允许传任何收款账户 | 598612 |
7. 错误码
7.1 新增(出纳域 5986 段顺延)
| 码 | 常量 | 文案 | 触发场景 |
|---|---|---|---|
| 598612 | CASHIER_PAYEE_ACCOUNT_CONFLICT | 收款账户参数非法(现金付款无需收款账户;选账户与手填建档二选一) | CASH 传了 payeeAccountId/newPayeeAccount;或两者同传 |
| 598613 | CASHIER_PAYEE_ACCOUNT_REQUIRED | 该付款方式须选择或登记报账人收款账户 | BANK/THIRD_PARTY 但两者皆空 |
| 598614 | CASHIER_PAYEE_ACCOUNT_NOT_MATCH | 收款账户不属于本报账人 | 所选账户的 payeeRefId 不等于报账单主报账人档案ID |
| 598615 | CASHIER_PAYEE_ACCOUNT_TYPE_MISMATCH | 收款账户类型与付款方式不匹配(银行转账须选银行卡账户,三方支付须选收款码账户) | 如 payMethod=BANK 却选了微信收款码账户 |
7.2 复用(收款账户域 5952xx,手填建档链抛出)
| 码 | 说明 |
|---|---|
| 595201 | 收款账户不存在或已停用(选了已被删除/停用的账户) |
| 595202 | 账户要素组合校验失败(QR 类缺 qrUrl / BANK_CARD 缺 bankAccount / 报账款线传 CORP_ACCOUNT 等) |
| 595204 | 服务人员档案不存在或不在册(手填建档 payeeRefId 校验 fail-fast) |
7.3 既有(本次不变)
598602 状态冲突(重复付款)/ 598610 金额与单据应付不一致 / 598604 账户余额不足 等沿用。
8. 示例
8.1 典型成功(银行转账 + 手填建档银行卡)
请求:
POST /admin/finance/cashier/reimburse/pay
{
"bizId": "88010001",
"payAccountId": "3101",
"payMethod": "BANK",
"amount": 1500.00,
"payDate": "2026-10-09",
"newPayeeAccount": {
"payeeRefId": "6601",
"accountType": "BANK_CARD",
"bankAccount": "6222021234567890123",
"bankName": "工商银行呼和浩特支行"
}
}
响应:
{
"code": 0,
"data": {
"flowId": "990001",
"flowNo": "FL2026100912300001",
"balanceAfter": 48500.00,
"bizId": "88010001",
"payeeAccountId": "7701",
"payeeAccountLabel": "银行卡 ****0123 巴特尔"
},
"msg": ""
}
队列行带默认账户(付款弹窗直接预选):
{
"reimburseId": "88010001",
"bizNo": "BX202610090001",
"reporterName": "巴特尔",
"bizType": "ORDER",
"reporterAssignmentId": "5501",
"reporterRefId": "6601",
"reporterPayeeType": "DRIVER",
"defaultPayeeAccountId": "7701",
"defaultPayeeAccountLabel": "银行卡 ****0123",
"settleAmount": 1500.00
}
list-by-payee 下拉数据源:
GET /admin/finance/payee-accounts/list-by-payee?payeeType=DRIVER&payeeRefId=6601
{
"code": 0,
"data": [
{
"id": "7701",
"payeeType": "DRIVER",
"payeeRefId": "6601",
"payeeName": "巴特尔",
"accountType": "BANK_CARD",
"qrUrl": null,
"bankAccount": "****0123",
"bankName": "工商银行呼和浩特支行",
"isDefault": 1
}
]
}
8.2 边界情况
8.2a 现金付款(不带收款账户,两新字段回 null):
{
"bizId": "88010002",
"payAccountId": "3101",
"payMethod": "CASH",
"amount": 300.00,
"payDate": "2026-10-09"
}
{
"code": 0,
"data": {
"flowId": "990002",
"flowNo": "FL2026100912300002",
"balanceAfter": 48200.00,
"bizId": "88010002",
"payeeAccountId": null,
"payeeAccountLabel": null
}
}
8.2b 报账人无默认账户 / 角色不可映射:队列行 defaultPayeeAccountId / defaultPayeeAccountLabel / reporterPayeeType 为 null,下拉为空数组,出纳须手填建档。
8.3 业务失败
8.3a 银行转账不传收款账户(598613):
{
"bizId": "88010001",
"payAccountId": "3101",
"payMethod": "BANK",
"amount": 1500.00,
"payDate": "2026-10-09"
}
{ "code": 598613, "msg": "该付款方式须选择或登记报账人收款账户", "data": null }
8.3b 银行转账选了微信收款码账户(598615):
请求体同上但改为传 "payeeAccountId": "7702"(7702 是该报账人的 WECHAT_QR 账户)。
{ "code": 598615, "msg": "收款账户类型与付款方式不匹配(银行转账须选银行卡账户,三方支付须选收款码账户)", "data": null }
8.3c 选了别的报账人的账户(598614):
{ "code": 598614, "msg": "收款账户不属于本报账人", "data": null }
8.3d 现金付款传了收款账户 / 选账户与手填同传(598612):
{ "code": 598612, "msg": "收款账户参数非法(现金付款无需收款账户;选账户与手填建档二选一)", "data": null }
9. 业务边界
- 适用:报账款(REIMBURSE)页签、direction=PAYABLE(付给司导)的付款单。
- 不适用:
- 反向收款(confirm-in,司导向公司上交余款)零改动,不涉及收款账户。
- 其余 7 条出纳付款线不接联动,壳入参上的两个新字段传了静默忽略。
- 强制挂档案:手填建档必须选服务人员档案(payeeRefId 必填),账户名=档案真名,出纳不能手填收款人姓名。
- 例外通道:收款人不是司导本人(如家属代收)时,不走付款弹窗手填,先去「收款账户管理」用组织类/手填入口建档,再回到付款弹窗选择(归属校验口径:账户须挂到该报账人档案下,否则 598614)。
- 首个账户自动置默认:手填建档的账户若是该收款方名下首个账户,自动成为默认;已有账户时不抢既有默认。
- 重复提交安全:双击/重试不会产生重复账户(唯一索引兜底),也不会重复扣款(598602)。
10. 修改前后对比
10.1 字段级
| 位置 | 原来 | 现在 |
|---|---|---|
| 付款入参 | 只有出账账户 payAccountId | 新增 payeeAccountId / newPayeeAccount(BANK/THIRD_PARTY 二选一必填,CASH 禁传) |
| 付款响应 | flowId/flowNo/balanceAfter/bizId | 新增 payeeAccountId / payeeAccountLabel(CASH 为 null) |
| 队列行 | 无报账人档案/账户信息 | 新增 bizType / reporterAssignmentId / reporterRefId / reporterPayeeType / defaultPayeeAccountId / defaultPayeeAccountLabel |
| 台账行 | 无收款账户留痕 | REIMBURSE 行回填 payeeAccountId / payeeAccountLabel,其余线 null |
| 报账详情 | 无收款账户留痕 | 新增 payeeAccountId / payeeAccountSnapshot |
| 选账户下拉数据源 | 不存在 | 新增 GET /admin/finance/payee-accounts/list-by-payee |
10.2 行为级
| 项 | 原来 | 现在 |
|---|---|---|
| BANK/THIRD_PARTY 付款 | 只填出账账户即可 | 必须选收款账户或手填建档,否则 598613 |
| CASH 付款 | 不变 | 传收款账户参数会被 598612 拦(前端应禁用控件) |
| 账户与付款方式 | 无校验 | 错位强拦 598615(银行转账须银行卡,三方须收款码) |
| 账户归属 | 无校验 | 只能选/建本报账人名下账户,否则 598614 |
11. 影响评估 / 回滚
- 破坏性:有(行为级)。报账款 BANK/THIRD_PARTY 付款从「不填收款账户也能付」变为「必填」,前端付款弹窗必须同步改造(加选账户下拉 + 手填建档表单 + CASH 时禁用),否则出纳付款会被 598613 全量拦截。
- 前端同步上线:要求。后端部署后旧版前端的报账款银行/三方付款会全部报 598613,前端改版须与后端部署同步或紧随。
- 回滚方案:后端回退到 #8810 之前版本即可恢复旧行为;前端新版代码对旧后端兼容(新字段不传即走旧链路)。DB 新增两列可空,保留无害;fin_payee_account 唯一索引不影响既有数据。
12. 注意事项
- 金额/ID 一律字符串:所有 Long ID(bizId / payeeAccountId / payeeRefId / defaultPayeeAccountId 等)JSON 序列化为字符串,前端按 string 处理。
- CASH 禁用收款账户控件:payMethod 切到 CASH 时隐藏/禁用选账户与手填入口,并清空已填值,避免 598612。
- 下拉数据源用队列行字段:打开付款弹窗时直接拿 reporterPayeeType + reporterRefId 调 list-by-payee,不要用 reporterAssignmentId(那是分配行ID,口径不同)。
- 默认账户预选:队列行 defaultPayeeAccountId 非空时直接预选,出纳可改选或改手填。
- 新字段全部可空:存量报账单、现金付款、其余付款线场景下 payeeAccountId/Label 均为 null,渲染一律判空。
- 部署时序:依赖 Flyway 迁移 V20261009_136__fin_reimburse_payee_account_link.sql(fin_reimburse 加两列 + fin_payee_account 加唯一索引),order-v3 部署时自动执行;未部署前新字段不生效(598613 不会触发,行为同旧版)。
- 手填建档幂等:同一报账人同一账户要素重复提交不会重复建档,返回已有账户ID,前端无需防重逻辑(但仍建议提交后禁用按钮)。
13. 关联 / 联系人
- Issue:wx/HL#8809
- PR:wx/HL#8810
- Commit:
e50a43b72a - 关联:#8679(收款账户关联服务人员档案基建)
- 后端负责人:腰苏图(yst)