diff --git a/changelogs-v2/2026-09/17_7845_公司借款域全量上线-新增接口-管理后台.md b/changelogs-v2/2026-09/17_7845_公司借款域全量上线-新增接口-管理后台.md new file mode 100644 index 00000000..30526478 --- /dev/null +++ b/changelogs-v2/2026-09/17_7845_公司借款域全量上线-新增接口-管理后台.md @@ -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>`) + +| 字段 | 类型 | 说明 | +|---|---|---| +| `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`,成功无返回体(`data=null`)。 + +### 5.5 操作流水行 `FinReviewLogRespVO`(接口 5,`Result>`,升序) + +| 字段 | 类型 | 说明 | +|---|---|---| +| `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>`) + +| 字段 | 类型 | 说明 | +|---|---|---| +| `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>`) + +| 字段 | 类型 | 说明 | +|---|---|---| +| `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)