feat(changelog): 公司借款域全量上线——12 接口 + 出纳 COMPANY_LOAN 新付款线(#7845 / PR #7846 #7848 #7850 #7851)
changelog-filename-gate / validate (push) Failing after 2s

这个提交包含在:
yaosutu
2026-09-17 08:53:33 +08:00
父节点 09f59e6d68
当前提交 58c216669c
@@ -0,0 +1,705 @@
---
schema: "hl-changelog/v2"
ticket: "finance-company-loan-module-onboarding"
title: "公司借款域全量上线(12 接口:登记/详情/借入到账/收回收银台/归还收银台/还款流水/操作流水 + 出纳 COMPANY_LOAN 新付款线)"
consumer: "admin"
author: "yst(GIT)"
change_type: "新增接口"
backend_status: "deployed"
gateway_status: "verified"
frontend_status: "pending"
frontend_owner: ""
frontend_ref: ""
target_release: ""
verified_at: "2026-09-17"
status_note: "财务域公司借款模块首次整模块交付:双向 OUT 借出 / IN 借入,登记制四态(APPROVED 已登记 → PAID 已收付 → SETTLING 核销中 → SETTLED 已核销,无独立审批)。含登记、详情、借入到账确认、按单位聚合收回(OUT,支持部分收回)、整笔归还(IN)、还款流水分页、操作流水 12 个新端点;出纳侧新增 COMPANY_LOAN 付款类型(借出放款进待付款队列,金额锁死=借款金额)。后端 4 PR 已合并 dev-v3 并部署测试服,E2E 已真实跑通。前端此前无公司借款任何页面,按本文一次对接即可。"
updated_at: "2026-09-17"
base: "dev-v3"
---
# 公司借款域全量上线(管理后台)
> **服务**: hl-order-service-v3(hl-finance 模块)
> **类型**: 🆕 整模块新增(本期新交付,前端首次对接)
> **日期**: 2026-09-17
> **影响范围**: 管理后台财务域「公司借款」菜单 + 出纳支付管理「公司借款」页签
> **覆盖 Epic**: #7845(PR-1 #7846 地基+登记链 / PR-2 #7848 出纳放款+借入到账 / PR-3 #7850 收回·归还收银台 / PR-4 #7851 操作流水+文档)
---
## 一、接口背景
公司借款 = **公司与外部往来单位(本期恒为供应商)之间的资金拆借**。两个方向:
- **OUT 借出**:公司把钱借给单位 → 出纳放款 → 到期按单位聚合**收回**(可部分多次)
- **IN 借入**:公司向单位借钱 → 财务确认**借入到账** → 到期按单位**整笔归还**(勾选即还欠还全额)
业务闭环(登记制,**无独立审批**,登记即生效 APPROVED):
```
登记 POST /admin/finance/company-loans ──▶ APPROVED(已登记)
│
├─ OUT 借出:出纳支付管理队列放款 POST /admin/finance/cashier/pay (bizType=COMPANY_LOAN)
│ APPROVED → PAID(落 CASHIER_PAY 操作流水 + 资金流水 OUT)
└─ IN 借入:借款详情页确认到账 POST /{id}/confirm-inbound
APPROVED → PAID(落 CONFIRM_INBOUND 操作流水 + 资金流水 IN)
│
├─ OUT 收回:收回收银台 单位聚合 → 勾选填额 → POST /recover/settle(支持部分收回)
└─ IN 归还:归还收银台 单位聚合 → 勾选整笔 → POST /repay-cashier/settle(每笔还欠还全额)
│
按 SUM(repay) 现算状态:0 < 已收/还 < 借款金额 → SETTLING(核销中)
已收/还 ≥ 借款金额 → SETTLED(已核销,终态)
```
---
## 二、变更清单
| # | 变更 | 类型 | 说明 |
|---|------|------|------|
| 1 | `GET /admin/finance/company-loans/page` | ✨ 新增 | 公司借款分页(状态/方向/单位/关键字筛选) |
| 2 | `POST /admin/finance/company-loans` | ✨ 新增 | 登记借款(登记即 APPROVED) |
| 3 | `GET /admin/finance/company-loans/{id}` | ✨ 新增 | 借款单详情(全字段 + 还款流水 + 余额现算) |
| 4 | `POST /admin/finance/company-loans/{id}/confirm-inbound` | ✨ 新增 | 借入到账确认(仅 IN 单) |
| 5 | `GET /admin/finance/company-loans/{id}/review-logs` | ✨ 新增 | 操作流水(登记/到账/放款/收回/归还留痕) |
| 6 | `GET /admin/finance/company-loans/recover/units` | ✨ 新增 | 收回收银台-单位聚合(OUT) |
| 7 | `GET /admin/finance/company-loans/recover/units/{unitId}` | ✨ 新增 | 收回收银台-单位待收回明细 |
| 8 | `POST /admin/finance/company-loans/recover/settle` | ✨ 新增 | 合并收款(收回,仅 OUT,支持部分) |
| 9 | `GET /admin/finance/company-loans/repay-cashier/units` | ✨ 新增 | 归还收银台-单位聚合(IN) |
| 10 | `GET /admin/finance/company-loans/repay-cashier/units/{unitId}` | ✨ 新增 | 归还收银台-单位待归还明细 |
| 11 | `POST /admin/finance/company-loans/repay-cashier/settle` | ✨ 新增 | 整笔归还(仅 IN,每笔还欠还全额) |
| 12 | `GET /admin/finance/company-loans/repays/page` | ✨ 新增 | 还款流水分页(收回/归还共用) |
| 13 | `GET /admin/finance/cashier/queue?payType=COMPANY_LOAN` | 🔧 修改 | 出纳待付款队列新增 COMPANY_LOAN 付款类型(仅 OUT 借出且 APPROVED 单进队列) |
| 14 | `POST /admin/finance/cashier/pay`(bizType=COMPANY_LOAN) | 🔧 修改 | 出纳登记付款新增公司借款借出放款业务线,金额锁死=借款金额 |
| 15 | 资金流水 `bizType` 枚举 | ✨ 新增值 | 新增 `COMPANY_LOAN`(资金流水按 bizType=COMPANY_LOAN&bizId 可串联借款单) |
---
## 三、接口详情
| 项 | 说明 |
|---|---|
| 使用场景 | 管理后台财务域「公司借款」模块(列表/详情/登记/收银台)+ 出纳支付管理「公司借款」页签 |
| 认证 | 管理后台 JWT(网关统一鉴权),需财务域菜单权限 |
| 幂等性 | 登记/收付/收回/归还均**非幂等写**:重复提交由状态机守卫拦截——登记靠单号 uk 撞号重试;收付后状态已非 APPROVED,重试被 599302 拦;收回/归还对 SETTLED 单重试被 599308 拦、超额被 599309 拦(锁内现算余额,整体回滚不留半截) |
| 限流 | 走网关统一限流,无模块特殊限流 |
| 单号规则 | 借款单号 `GS-` + yyyyMMdd + 4 位序号(OUT/IN **共用前缀**,靠 `direction` 徽标区分);还款流水号 `GH-` + yyyyMMdd + 4 位序号 |
| ID 序列化 | 所有 Long 型 ID(id / unitId / handlerStaffId / fundAccountId / repayIds 等)序列化为 **String**,防 JS 精度丢失 |
---
## 四、接口入参
### 4.1 `GET /admin/finance/company-loans/page` 公司借款分页
Query 入参(继承 PageParam:`pageNo` / `pageSize` 必填):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `status` | String | 否 | 单据状态单值:APPROVED / PAID / SETTLING / SETTLED;空=全部 |
| `statuses` | String[] | 否 | 状态多值 IN 查询(如 `statuses=APPROVED&statuses=PAID`);非空时优先于 status |
| `direction` | String | 否 | 方向:OUT 借出 / IN 借入;空=不限 |
| `unitId` | Long | 否 | 往来单位ID 精确;空=不限 |
| `keyword` | String | 否 | 关键字(借款单号 / 往来单位名称模糊);空=不限 |
### 4.2 `POST /admin/finance/company-loans` 登记借款
请求体 `CompanyLoanCreateReqVO`:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `direction` | String | ✅ | 方向:OUT 借出 / IN 借入(其他值 → 599302) |
| `unitType` | String | 否 | 往来单位类型(字典 fin_unit_type;本期恒 SUPPLIER,空按 SUPPLIER 处理,其他值 → 599304) |
| `unitId` | Long | ✅ | 往来单位ID(供应商;后端反查可付款性并落全称快照,查不到/不可选 → 599304) |
| `unitName` | String | 否 | **契约兼容字段,服务端反查覆盖,前端传值不生效**(≤200 字) |
| `handlerStaffId` | Long | ✅ | 经办人 adminId(后端反查 wechatName 落姓名快照;查不到/无真名/用户域降级 → 599305) |
| `handlerStaffName` | String | 否 | **契约兼容字段,服务端反查覆盖,前端传值不生效**(≤64 字) |
| `amount` | BigDecimal | ✅ | 借款金额(>0,否则 599303;不计息) |
| `feeRate` | BigDecimal | 否 | 手续费率‰(登记参考值,空按 0;实际手续费在收付环节定) |
| `loanDate` | Date | 否 | 借款日期(yyyy-MM-dd) |
| `dueDate` | Date | ✅ | 约定归还日期(yyyy-MM-dd) |
| `purpose` | String | ✅ | 借款用途(≤200 字) |
### 4.3 `GET /admin/finance/company-loans/{id}` 借款单详情
路径参数:`id`(Long,借款单ID,必填)。不存在(含已软删)→ 599301。
### 4.4 `POST /admin/finance/company-loans/{id}/confirm-inbound` 借入到账确认
路径参数:`id`(必填)。**仅 direction=IN 单可调**(OUT 单 → 599310);状态须 APPROVED(否则 599302)。
请求体 `CompanyLoanInboundConfirmReqVO`:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `fundAccountId` | Long | ✅ | 入账资金账户ID(fin_fund_account;不存在 595001 / 已停用 595006) |
| `actualAmount` | BigDecimal | ✅ | 实际到账金额(>0 且 ≤ 借款金额,否则 599311;与借款金额差额 = 到账手续费) |
> 本端点**不收**到账凭证影像(契约无 voucherUrl)。
### 4.5 `GET /admin/finance/company-loans/{id}/review-logs` 操作流水
路径参数:`id`(必填,不存在 → 599301)。无 Query 入参。按操作发生时间**升序**返回,无记录返回空数组 `[]`。
### 4.6 `GET /admin/finance/company-loans/recover/units` 收回收银台-单位聚合
Query 入参:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `unitName` | String | 否 | 单位名模糊;空=全部 |
只聚合 direction=OUT 且状态 PAID/SETTLING(未核销)的借出单。
### 4.7 `GET /admin/finance/company-loans/recover/units/{unitId}` 单位待收回明细
路径参数:`unitId`(Long,必填)。返回该单位名下全部未核销借出单(余额现算 + 逾期标记),供勾选合并收款。
### 4.8 `POST /admin/finance/company-loans/recover/settle` 合并收款(收回)
请求体 `CompanyLoanRecoverSettleReqVO`:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `unitId` | Long | ✅ | 往来单位ID(勾选单须全部属该单位,否则 599312 整体回滚) |
| `items` | Item[] | ✅ | 勾选明细(≥1 条) |
| `items[].loanId` | Long | ✅ | 借款单ID |
| `items[].amount` | BigDecimal | ✅ | 本次收回金额(>0 且 ≤ 该单余额,超额 → 599309 整体回滚) |
| `fundAccountId` | Long | ✅ | 入账资金账户ID(595001/595006) |
| `feeRate` | BigDecimal | 否 | 手续费率‰(默认 0,负值 → 599303) |
| `voucherUrl` | String | 否 | 收款凭证影像URL |
金额口径:**fee = Σ勾选填额 × feeRate / 1000;实收 = Σ − fee**。资金流水合并**单笔 IN**(amount=实收,biz_id=首笔还款流水ID)。
### 4.9 `GET /admin/finance/company-loans/repay-cashier/units` 归还收银台-单位聚合
Query 入参同 4.6(`unitName` 模糊,可空)。只聚合 direction=IN 且 PAID/SETTLING 的借入单。
### 4.10 `GET /admin/finance/company-loans/repay-cashier/units/{unitId}` 单位待归还明细
路径参数:`unitId`(必填)。返回该单位名下未核销借入单,**勾选即整笔还 outstandingAmount 全额**(不支持部分归还)。
### 4.11 `POST /admin/finance/company-loans/repay-cashier/settle` 整笔归还
请求体 `CompanyLoanRepaySettleReqVO`:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `unitId` | Long | ✅ | 往来单位ID(勾选单须全部属该单位,否则 599312) |
| `loanIds` | Long[] | ✅ | 勾选整笔归还的借入单ID列表(≥1;**每笔还其欠还全额**,不填金额) |
| `fundAccountId` | Long | ✅ | 出账资金账户ID(595001/595006;余额不足透支闸 595103) |
| `feeRate` | BigDecimal | 否 | 手续费率‰(默认 0,负值 → 599303) |
| `voucherUrl` | String | 否 | 付款凭证影像URL |
金额口径:**fee = Σ欠还全额 × feeRate / 1000;实付 = Σ + fee**(注意:归还侧手续费是**外加**,与收回侧**内扣**相反)。资金流水合并**单笔 OUT**(结存 −Σ−fee)。
### 4.12 `GET /admin/finance/company-loans/repays/page` 还款流水分页
Query 入参(继承 PageParam:`pageNo` / `pageSize` 必填):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| `loanId` | Long | 否 | 借款单ID 精确;空=不限 |
| `unitId` | Long | 否 | 往来单位ID;空=不限 |
| `direction` | String | 否 | OUT 借出(行=收回)/ IN 借入(行=归还);空=不限 |
| `repaidAtStart` | Date | 否 | 收/还日期起(含,yyyy-MM-dd) |
| `repaidAtEnd` | Date | 否 | 收/还日期止(含,yyyy-MM-dd) |
### 4.13 出纳既有接口(COMPANY_LOAN 调用方式)
**队列**:`GET /admin/finance/cashier/queue?payType=COMPANY_LOAN&pageNo=1&pageSize=10`(出参结构不变,见 §五.13)。
**放款**:`POST /admin/finance/cashier/pay`,请求体 `CashierPayReqVO`(既有结构):
| 字段 | 必填 | 公司借款场景取值 |
|---|---|---|
| `bizType` | ✅ | 固定 `COMPANY_LOAN` |
| `bizId` | ✅ | 借款单ID(仅 direction=OUT 且 status=APPROVED;IN 单 → 599310) |
| `payAccountId` | ✅ | 出账公司账户ID |
| `payMethod` | 否 | 字典 fin_pay_way:CASH / BANK / THIRD_PARTY |
| `payChannel` | 否 | WXPAY / ALIPAY(仅 payMethod=THIRD_PARTY 时传) |
| `amount` | ✅ | **金额锁死 = 借款金额**,不等 → 598610 |
| `fee` | 否 | 手续费(≥0,挂出账流水) |
| `voucherNo` / `voucherUrl` | 否 | 付款凭证号 / 凭证影像 URL |
| `payDate` | ✅ | 付款日期(yyyy-MM-dd,可回溯补录) |
| `operatorName` | 否 | 后端忽略,统一取当前登录人快照 |
---
## 五、出参字段
### 5.1 分页行 `CompanyLoanRowRespVO`(接口 1,`Result<PageResult<行>>`)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String | 借款单ID |
| `loanNo` | String | 借款单号(GS- 前缀) |
| `direction` | String | OUT 借出 / IN 借入 |
| `unitType` | String | 往来单位类型(本期恒 SUPPLIER) |
| `unitId` | String | 往来单位ID |
| `unitName` | String | 往来单位名称快照 |
| `handlerStaffId` | String | 经办人 adminId |
| `handlerStaffName` | String | 经办人姓名快照 |
| `amount` | BigDecimal | 借款金额 |
| `repaidAmount` | BigDecimal | 已收/还金额(repay 汇总现算) |
| `outstandingAmount` | BigDecimal | 待收/还余额 = amount − repaidAmount |
| `purpose` | String | 借款用途 |
| `loanDate` | Date | 借款日期 |
| `dueDate` | Date | 约定归还日期 |
| `status` | String | APPROVED / PAID / SETTLING / SETTLED |
| `operatorId` | String | 登记人 adminId(created_by 反查) |
| `operatorName` | String | 登记人真名(反查降级可为 null,仅展示) |
| `createTime` | DateTime | 创建时间 |
### 5.2 登记响应 `CompanyLoanCreateRespVO`(接口 2)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String | 新借款单ID(状态 APPROVED) |
| `loanNo` | String | 借款单号(GS-) |
### 5.3 详情 `CompanyLoanDetailRespVO`(接口 3)
在 5.1 全部字段基础上增加:
| 字段 | 类型 | 说明 |
|---|---|---|
| `feeRate` | BigDecimal | 手续费率‰(登记参考值) |
| `fundAccountId` | String | 出/入账资金账户ID(**仅 PAID 及之后有值**) |
| `paidAt` | DateTime | 收付讫业务时间(**仅 PAID 及之后有值**) |
| `approvalInstanceId` | String | 审批实例ID(登记制无审批,恒 null) |
| `repays` | RepayItem[] | 还款流水(按收/还时间**倒序**;未发生收还前空数组) |
`repays[]` 子项 `CompanyLoanRepayItem`:
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String | 还款流水ID |
| `repayNo` | String | 还款单号(GH- 前缀) |
| `direction` | String | 随主单:OUT(本行=收回)/ IN(本行=归还) |
| `amount` | BigDecimal | 本次收/还金额 |
| `fundAccountId` | String | 收/付资金账户ID |
| `voucherUrl` | String | 收/还凭证影像URL |
| `repaidAt` | DateTime | 收/还时间 |
### 5.4 借入到账确认(接口 4)
`Result<Void>`,成功无返回体(`data=null`)。
### 5.5 操作流水行 `FinReviewLogRespVO`(接口 5,`Result<List<行>>`,升序)
| 字段 | 类型 | 说明 |
|---|---|---|
| `logId` | String | 流水ID |
| `action` | String | 操作动作:**REGISTER 登记 / CONFIRM_INBOUND 借入到账 / CASHIER_PAY 出纳放款 / RECOVER 收回 / REPAY 归还**(公司借款词表,与 #7801 六域审核词表 APPROVE/REJECT/RETURN/UN_APPROVE 不同) |
| `operatorId` | String | 操作人ID(无登录上下文快照可为空) |
| `operatorName` | String | 操作人姓名快照 |
| `opinion` | String | 操作意见/备注(可空) |
| `fromStatus` | String | 操作前状态(登记 REGISTER 时为 null) |
| `toStatus` | String | 操作后状态 |
| `createTime` | DateTime | 操作发生时间 |
### 5.6 收回单位聚合行 `CompanyLoanRecoverUnitRespVO`(接口 6,`Result<List<行>>`)
| 字段 | 类型 | 说明 |
|---|---|---|
| `unitId` | String | 往来单位ID |
| `unitName` | String | 往来单位名称快照 |
| `loanCount` | Integer | 未核销借出单笔数 |
| `totalOutstanding` | BigDecimal | 待收回余额合计 |
| `nearestDueDate` | Date | 最近到期日(MIN(due_date)) |
### 5.7 单位待收回明细行 `CompanyLoanRecoverItemRespVO`(接口 7)
| 字段 | 类型 | 说明 |
|---|---|---|
| `loanId` | String | 借款单ID |
| `loanNo` | String | 借款单号 |
| `amount` | BigDecimal | 借款金额 |
| `repaidAmount` | BigDecimal | 已收回合计(现算) |
| `outstandingAmount` | BigDecimal | 待收回余额(填额上限) |
| `dueDate` | Date | 约定归还日期 |
| `overdue` | Boolean | 是否已逾期(dueDate < 今日且未核销) |
### 5.8 合并收款响应 `CompanyLoanRecoverSettleRespVO`(接口 8)
| 字段 | 类型 | 说明 |
|---|---|---|
| `totalAmount` | BigDecimal | 本次收回合计(Σ 勾选填额) |
| `actualAmount` | BigDecimal | 实收 = totalAmount − fee |
| `fee` | BigDecimal | 手续费(= totalAmount × feeRate/1000) |
| `repayIds` | String[] | 生成的还款流水ID(逐笔) |
| `settledLoanIds` | String[] | 本次余额归零转 SETTLED 的借款单ID(前端可据此局部刷新行状态) |
### 5.9 归还单位聚合行 `CompanyLoanRepayUnitRespVO`(接口 9)
字段与 5.6 完全对称(unitId / unitName / loanCount / totalOutstanding / nearestDueDate),仅语义为「待归还」。
### 5.10 单位待归还明细行 `CompanyLoanRepayCashierItemRespVO`(接口 10)
字段与 5.7 完全对称(loanId / loanNo / amount / repaidAmount / outstandingAmount / dueDate / overdue);`outstandingAmount` = 勾选后整笔还的全额。
### 5.11 整笔归还响应 `CompanyLoanRepaySettleRespVO`(接口 11)
| 字段 | 类型 | 说明 |
|---|---|---|
| `totalAmount` | BigDecimal | 本次归还合计(Σ 勾选单欠还全额) |
| `actualPayAmount` | BigDecimal | 实付 = totalAmount + fee(手续费外加) |
| `fee` | BigDecimal | 手续费(= totalAmount × feeRate/1000) |
| `repayIds` | String[] | 生成的还款流水ID(逐笔) |
| `settledLoanIds` | String[] | 余额归零转 SETTLED 的借款单ID |
### 5.12 还款流水行 `CompanyLoanRepayRowRespVO`(接口 12,`Result<PageResult<行>>`)
| 字段 | 类型 | 说明 |
|---|---|---|
| `id` | String | 还款流水ID |
| `repayNo` | String | 归还/收回单号(GH- 前缀) |
| `loanId` | String | 关联借款单ID |
| `loanNo` | String | 关联借款单号 |
| `direction` | String | 随主单:OUT(本行=收回)/ IN(本行=归还) |
| `unitName` | String | 往来单位名称快照 |
| `amount` | BigDecimal | 本次归还/收回金额 |
| `fundAccountId` | String | 收/付资金账户ID |
| `repaidAt` | DateTime | 归还/收回时间 |
| `voucherUrl` | String | 凭证影像URL |
### 5.13 出纳队列行 `CashierQueueRowRespVO`(接口 13,既有结构)
COMPANY_LOAN 行的关键取值(其余字段沿用既有定义):
| 字段 | COMPANY_LOAN 取值 |
|---|---|
| `id` / `bizNo` | 借款单ID / 借款单号(GS-) |
| `payType` | `COMPANY_LOAN` |
| `payTypeName` | `公司借款` |
| `unitId` / `unitName` | 往来单位ID / 名称快照 |
| `category` | 固定值 `公司借款` |
| `categoryName` | 同 category(`公司借款`) |
| `amount` | 借款金额(放款金额锁死基准) |
| `fee` | 0(放款手续费在 pay 时登记) |
| `actualAmount` | = amount − fee |
| `operatorName` | 经办人姓名快照 |
| `status` | 队列内恒 `APPROVED` |
| `occurDate` / `remark` | 借款日期 / 借款用途 |
---
## 六、枚举 / 数据字典
### 6.1 方向 `direction`
| 值 | 中文 | 说明 |
|---|---|---|
| `OUT` | 借出 | 公司借钱给单位;出纳队列放款;收回走 recover 收银台 |
| `IN` | 借入 | 公司向单位借钱;confirm-inbound 确认到账;归还走 repay-cashier 收银台 |
### 6.2 单据状态 `status`(四态,登记制无审批)
| 值 | 中文 | 进入条件 |
|---|---|---|
| `APPROVED` | 已登记 | 登记成功即此态(无独立审批) |
| `PAID` | 已收付 | OUT=出纳放款完成 / IN=借入到账确认完成 |
| `SETTLING` | 核销中 | 0 < SUM(repay) < amount(部分收/还) |
| `SETTLED` | 已核销 | SUM(repay) ≥ amount(终态,一次还满则 PAID→SETTLED 直达) |
### 6.3 操作流水动作 `action`(公司借款词表)
| 值 | 中文 | fromStatus → toStatus |
|---|---|---|
| `REGISTER` | 登记 | null → APPROVED |
| `CONFIRM_INBOUND` | 借入到账 | APPROVED → PAID(仅 IN) |
| `CASHIER_PAY` | 出纳放款 | APPROVED → PAID(仅 OUT) |
| `RECOVER` | 收回 | PAID/SETTLING → SETTLING/SETTLED(仅 OUT) |
| `REPAY` | 归还 | PAID/SETTLING → SETTLING/SETTLED(仅 IN) |
### 6.4 出纳付款类型 `FinCashierPayTypeEnum`(本次新增值)
| 值 | 中文 | 单号前缀 | 进付款队列 |
|---|---|---|---|
| `COMPANY_LOAN` 🆕 | 公司借款 | GS- | ✅(仅 direction=OUT 且 APPROVED 单) |
(既有值 NONBIZ / EXPENSE / PAYMENT / PREPAY / STAFF_LOAN / REIMBURSE / ADVANCE 不变;ADVANCE 仍 queueVisible=false 不进队列。)
### 6.5 资金流水 `bizType` 新增值
`COMPANY_LOAN` —— 公司借款相关的全部真金白银落点(放款 OUT / 到账 IN / 收回 IN / 归还 OUT)均以此 bizType + bizId 串联,资金流水列表可按 bizType=COMPANY_LOAN 过滤。
### 6.6 数据字典
| 字典 | 用途 |
|---|---|
| `fin_unit_type` | 往来单位类型(本期恒 SUPPLIER 供应商) |
| `fin_pay_way` | 付款方式(出纳 pay:CASH / BANK / THIRD_PARTY) |
---
## 七、错误码
### 7.1 公司借款新段位 599301-599312(owner=hl-finance)
| 码 | 常量 | 消息 | 触发场景 |
|---|---|---|---|
| 599301 | COMPANY_LOAN_NOT_FOUND | 借款单不存在 | 详情/流水/收付/收回/归还操作的借款单不存在(含已软删) |
| 599302 | COMPANY_LOAN_STATUS_ILLEGAL | 借款单状态或方向不允许此操作 | 状态机守卫:如对 PAID 单再次 confirm-inbound、对 APPROVED 单收回 |
| 599303 | COMPANY_LOAN_AMOUNT_INVALID | 借款金额无效(须大于0) | 登记 amount≤0;settle feeRate 为负 |
| 599304 | COMPANY_LOAN_UNIT_INVALID | 往来单位不存在或不可选 | unitId 查不到/不可付款/资源域降级/unitType 非 SUPPLIER |
| 599305 | COMPANY_LOAN_HANDLER_INVALID | 经办人非法(不存在或未登记真名) | handlerStaffId 查不到/wechatName 空/用户域降级 |
| 599306 | COMPANY_LOAN_NO_CONFLICT | 借款单号生成冲突,请重试 | GS- 取号撞号重试 3 次耗尽 |
| 599307 | COMPANY_LOAN_REPAY_NO_CONFLICT | 还款单号生成冲突,请重试 | GH- 取号撞号重试 3 次耗尽 |
| 599308 | COMPANY_LOAN_ALREADY_SETTLED | 借款单已核销,不允许再收/还 | 对 SETTLED 终态单收回/归还 |
| 599309 | COMPANY_LOAN_REPAY_EXCEED | 收/还金额超过借款剩余余额 | 收回填额 > 该单锁内现算余额(整体回滚,不静默截断) |
| 599310 | COMPANY_LOAN_DIRECTION_ILLEGAL | 借款单方向与该操作不匹配 | IN 单走出纳放款 / OUT 单 confirm-inbound / IN 单收回 / OUT 单归还 |
| 599311 | COMPANY_LOAN_INBOUND_AMOUNT_INVALID | 到账金额须大于0且不超过借款金额 | confirm-inbound actualAmount 越界 |
| 599312 | COMPANY_LOAN_UNIT_MISMATCH | 勾选借款单与所选单位不一致 | settle 勾选单含其他单位的单(整体回滚) |
### 7.2 复用既有码
| 码 | 消息 | 触发场景 |
|---|---|---|
| 598601 | 业务单不存在 | 出纳 pay 的 bizId 查不到借款单 |
| 598602 | 业务单状态非已批准,不可付款 | 出纳 pay 时单据已非 APPROVED(含并发推进) |
| 598604 | 账户余额不足且不允许透支 | 出纳放款出账账户余额不足(595103 在出纳域边界的翻译) |
| 598610 | 付款金额与单据应付金额不一致(出纳付款须严格按审批应付金额,不得修改) | 出纳 pay amount ≠ 借款金额(金额锁死) |
| 595001 | 账户不存在 | fundAccountId/payAccountId 查不到 |
| 595006 | 账户已停用 | 资金账户状态非 ACTIVE |
| 595103 | 账户余额不足且不允许透支 | 归还 settle 出账账户余额不足(本域直接透出,不翻译) |
---
## 八、示例
### 8.1 典型成功:IN 借入全链路(登记 → 到账 → 整笔归还)
**① 登记** `POST /admin/finance/company-loans`
```json
{
"direction": "IN",
"unitId": 8801,
"handlerStaffId": 12,
"amount": 50000.00,
"dueDate": "2026-12-31",
"purpose": "旺季房费周转",
"loanDate": "2026-09-17",
"feeRate": 0
}
```
响应:
```json
{ "code": 0, "data": { "id": "1932700112233445567", "loanNo": "GS-202609170001" }, "msg": "" }
```
**② 借入到账确认** `POST /admin/finance/company-loans/1932700112233445567/confirm-inbound`
```json
{ "fundAccountId": 101, "actualAmount": 50000.00 }
```
响应 `{ "code": 0, "data": null }`;状态 APPROVED→PAID,落 CONFIRM_INBOUND 操作流水。
**③ 归还收银台单位明细** `GET /admin/finance/company-loans/repay-cashier/units/8801`
```json
{
"code": 0,
"data": [
{
"loanId": "1932700112233445567",
"loanNo": "GS-202609170001",
"amount": 50000.00,
"repaidAmount": 0.00,
"outstandingAmount": 50000.00,
"dueDate": "2026-12-31",
"overdue": false
}
]
}
```
**④ 整笔归还** `POST /admin/finance/company-loans/repay-cashier/settle`
```json
{
"unitId": 8801,
"loanIds": [1932700112233445567],
"fundAccountId": 101,
"feeRate": 1,
"voucherUrl": "https://oss.example.com/voucher/gh001.png"
}
```
响应(fee = 50000 × 1/1000 = 50,实付 50050):
```json
{
"code": 0,
"data": {
"totalAmount": 50000.00,
"actualPayAmount": 50050.00,
"fee": 50.00,
"repayIds": ["1932700998877665544"],
"settledLoanIds": ["1932700112233445567"]
}
}
```
状态 PAID→SETTLED(一次还满直达终态)。
### 8.2 边界情况:OUT 借出部分收回 + 到账金额小于借款额 + 空流水
**① 借入到账差额(到账手续费)**:借款 50000,实际到账 49800(差额 200 视为到账手续费,合法):
```json
{ "fundAccountId": 101, "actualAmount": 49800.00 }
```
→ 200 OK,资金流水 IN 记 49800。
**② OUT 单部分收回**(借款 50000,先收 20000):`POST /admin/finance/company-loans/recover/settle`
```json
{
"unitId": 8801,
"items": [{ "loanId": 1932700112233445566, "amount": 20000.00 }],
"fundAccountId": 101,
"feeRate": 0
}
```
响应:`totalAmount=20000 / actualAmount=20000 / fee=0 / settledLoanIds=[]`,状态 PAID→**SETTLING**(余额 30000 未清)。
**③ 无操作流水**(异常空态):`GET /admin/finance/company-loans/{id}/review-logs` 无记录时返回空数组:
```json
{ "code": 0, "data": [] }
```
(正常单据至少有 REGISTER 一条。)
**④ 详情未收付前**:`fundAccountId=null / paidAt=null / repays=[] / approvalInstanceId=null`。
### 8.3 业务失败:最小复现
**① 方向不匹配(599310)**:对 OUT 借出单调借入到账:
```http
POST /admin/finance/company-loans/{OUT单id}/confirm-inbound
Content-Type: application/json
{ "fundAccountId": 101, "actualAmount": 100.00 }
```
```json
{ "code": 599310, "data": null, "msg": "借款单方向与该操作不匹配" }
```
**② 收回超额(599309)**:余额 30000 的单填 30001:
```json
{ "unitId": 8801, "items": [{ "loanId": 1932700112233445566, "amount": 30001.00 }], "fundAccountId": 101 }
```
```json
{ "code": 599309, "data": null, "msg": "收/还金额超过借款剩余余额" }
```
**③ 出纳放款金额未锁死(598610)**:借款 50000 的单按 49999 付款:
```json
{ "bizType": "COMPANY_LOAN", "bizId": 1932700112233445566, "payAccountId": 101, "amount": 49999.00, "payDate": "2026-09-17" }
```
```json
{ "code": 598610, "data": null, "msg": "付款金额与单据应付金额不一致(出纳付款须严格按审批应付金额,不得修改)" }
```
---
## 九、业务边界
### 适用场景
- 公司与**供应商**之间的资金拆借登记与核销(借出收回 / 借入归还)
- 收回支持**部分多次**(同一单位多笔单可合并一次收款,逐笔填额)
- 归还为**整笔**(勾选即还该单欠还全额,不支持部分归还——这是产品设计,非缺陷)
### 不适用场景
- 员工个人借款 → 走「员工借款」模块(fin_staff_loan,接口前缀 `/admin/finance/staff-loans`),不要混用
- 需要审批流的借款 → 本域登记制**无审批**,登记即生效
- 跨单位合并结算 → 一次 settle 只能勾同一 unitId 的单(599312)
### 特殊边界
- **SETTLED 为终态**:已核销单不再出现在收回/归还收银台(聚合与明细接口都只出 PAID/SETTLING 单)
- **一次还满直达 SETTLED**:PAID 单一次收/还满额时状态 PAID→SETTLED,不经 SETTLING
- **方向锁死动作**:OUT 单只进出纳付款队列 + recover 收银台;IN 单只走 confirm-inbound + repay-cashier 收银台,交叉一律 599310
- **逾期仅展示**:overdue=true 不阻断收回/归还,仅列表标记
- **余额现算不存列**:repaidAmount/outstandingAmount 由 fin_company_loan_repay 实时 SUM,页面刷新即最新
---
## 十、修改前后对比(出纳既有接口)
### 10.1 `GET /admin/finance/cashier/queue` 出参变化
| 项 | 原来 | 现在 |
|---|---|---|
| `payType` 枚举值 | NONBIZ / EXPENSE / PAYMENT / PREPAY / STAFF_LOAN / REIMBURSE / ADVANCE(预留) | 新增 **COMPANY_LOAN**(公司借款,queueVisible=true) |
| `category` 固定值映射 | PREPAY=「预付款」/ STAFF_LOAN=「员工借款」/ REIMBURSE=「报账款」 | 新增 COMPANY_LOAN=固定值「**公司借款**」 |
| 队列数据源 | 6 条已接通业务线 | +1 条:fin_company_loan(direction=OUT 且 status=APPROVED) |
字段**零增删**,仅枚举值扩充——前端按 payType 建字典/页签的需补 COMPANY_LOAN 一项。
### 10.2 `POST /admin/finance/cashier/pay` 行为变化
| 项 | 原来 | 现在 |
|---|---|---|
| `bizType` 可选值 | NONBIZ / EXPENSE / PAYMENT / PREPAY / STAFF_LOAN / REIMBURSE | +**COMPANY_LOAN** |
| COMPANY_LOAN 金额基准 | ——(不存在) | **锁死 = 借款金额 amount**(不等 → 598610) |
| COMPANY_LOAN 方向红线 | —— | 仅 direction=OUT;IN 单 → 599310(借入到账走本域 confirm-inbound,不进出纳) |
### 10.3 资金流水 `bizType` 枚举
| 原来 | 现在 |
|---|---|
| NONBIZ / EXPENSE / PAYMENT / PREPAY / STAFF_LOAN / REIMBURSE 等 | +**COMPANY_LOAN**(放款/到账/收回/归还四种资金动作共用) |
---
## 十一、影响评估 / 回滚
| 项 | 评估 |
|---|---|
| 破坏兼容 | **否**。12 个端点全部新增;出纳侧仅枚举值扩充 + 新路由分支,既有 payType 行为零变化 |
| 前端同步上线 | 无需同步——前端无公司借款页面时不影响任何既有功能;新页面按本文对接后上线即可 |
| 数据库 | 新建 3 表(fin_company_loan / fin_company_loan_repay / fin_company_loan_review_log),无存量表/存量数据变更,Flyway `V20260917_108` 部署自动执行 |
| 菜单配置 | 后端需配 sys_menu:财务域「公司借款」页 + 出纳支付管理「公司借款」页签(以后端菜单 SQL 落地为准) |
| 回滚方案 | 代码回滚到 PR-1 前即可(新端点 404、队列不再出 COMPANY_LOAN 行);3 张新表为增量,无存量依赖时可 `DROP TABLE fin_company_loan_review_log, fin_company_loan_repay, fin_company_loan;` 并清理 flyway_schema_history 对应行(需 DBA 操作,正常不需要) |
---
## 十二、注意事项
1. **unitName / handlerStaffName 前端传值不生效**:登记时服务端按 unitId / handlerStaffId 反查覆盖快照(防乱传),前端只需传 ID;展示用快照名,单位/人员改名不追溯。
2. **收回与归还手续费方向相反**:收回 fee 内扣(实收=合计−fee);归还 fee 外加(实付=合计+fee)。前端金额预览区不要套错公式。
3. **action 词表勿复用六域审核字典**:公司借款操作流水是 REGISTER/CONFIRM_INBOUND/CASHIER_PAY/RECOVER/REPAY,与 #7801 六域(APPROVE/REJECT/RETURN/UN_APPROVE)是两套词表,VO 虽同为 FinReviewLogRespVO,action 展示字典要分开。
4. **GS- 单号 OUT/IN 共用**:列表/详情必须用 direction 徽标区分方向,不能只凭单号前缀判断。
5. **Long ID 全部 String 序列化**:前端按字符串处理,勿转 Number(精度丢失)。
6. **金额锁死**:出纳放款 amount 必须等于借款金额,前端付款弹窗金额框应只读回填队列行 amount。
7. **settledLoanIds 局部刷新**:settle 响应已给出本次转 SETTLED 的单ID,收银台明细列表可据此直接移除对应行,不必整表重查。
8. **confirm-inbound 不收凭证**:借入到账确认无 voucherUrl 字段,凭证在后续归还环节才登记。
---
## 十三、关联 / 联系人
- **父 Issue**: https://git.1814.love:8443/wx/HL/issues/7845
- **PR-1 地基+登记链**: https://git.1814.love:8443/wx/HL/pulls/7846 | commit https://git.1814.love:8443/wx/HL/commit/f2ed775962
- **PR-2 出纳放款+借入到账**: https://git.1814.love:8443/wx/HL/pulls/7848 | commit https://git.1814.love:8443/wx/HL/commit/c5c0c0672e
- **PR-3 收回/归还收银台**: https://git.1814.love:8443/wx/HL/pulls/7850 | commit https://git.1814.love:8443/wx/HL/commit/7e238c211f
- **PR-4 操作流水+文档**: https://git.1814.love:8443/wx/HL/pulls/7851 | commit https://git.1814.love:8443/wx/HL/commit/d2fbfd0846
- **后端负责人**: 腰苏图(yst)