新增 公司借款单笔收回端点 changelog(管理后台)(#8664)
changelog-filename-gate / validate (push) Failing after 2s

这个提交包含在:
yaosutu
2026-10-01 07:20:18 +08:00
父节点 b5c37d3724
当前提交 86c8752549
@@ -0,0 +1,209 @@
---
schema: "hl-changelog/v2"
ticket: "8664"
title: "公司借款收回新增单笔形态:POST /admin/finance/company-loans/{id}/recover"
consumer: "admin"
author: "yst(GIT)"
change_type: "新增接口"
backend_status: "merged"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude-opus-4-8)"
frontend_ref: ""
target_release: "v2.1"
verified_at: "2026-09-30"
base: "dev-v3"
updated_at: "2026-09-30"
status_note: "公司借款「借出方向」的收回(借出的钱到期收回)新增单笔形态——已付款借出单按单笔收款,与既有按单位合并收款(§1.4.3 recover/settle)共用 settle 核心。原型收回入口并入「支付管理/公司借款支付」已付款台账行「收款登记」;原「收款管理/公司借款收回」按单位汇总收银台原型入口下线(合并收款三端点后端保留可用)。"
---
# 【新增接口·管理后台】公司借款单笔收回 POST /admin/finance/company-loans/{id}/recover (#8664)
> **PR**: #8670 | **服务**: hl-order-service-v3(hl-finance 模块,8086) | **更新时间**: 2026-09-30
## 1. 接口背景
公司借款「借出方向」(direction=OUT,公司借钱给供应商/员工)的到期收回,原只有「按单位汇总收银台」一种形态(勾选同一单位多笔借款单合并收款,§1.4.1-1.4.3)。本次按业务拍板新增**单笔形态**:在「支付管理 / 公司借款支付」的**已付款台账行**上对**单笔借出单**直接「收款登记」——已付款的借出单逐笔收回,无需按单位聚合勾选。
**只写"为什么",不涉及实现**:收回动作的入口从「按单位汇总收银台」下沉到「已付款借出单行」,更符合"只有已付款的台账才能收回"的业务直觉,也避免跨单位合并的复杂度(用户明确不需要跨单位合并收款,单笔即可)。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 公司借款单笔收回 | POST | `/admin/finance/company-loans/{id}/recover` | 新增接口 | 按单笔借款单收回,与合并收款共用 settle 核心 |
> 合并收款三端点(§1.4.1 可选借款单 / §1.4.2 手续费试算 / §1.4.3 合并收款提交)**保留可用**,本次仅新增单笔便捷形态。
## 3. 接口详情
### 3.1 公司借款单笔收回
- **使用场景**:出纳在「支付管理 / 公司借款支付」的已付款台账,对某笔 direction=OUT(借出)且未收齐的借款单做收回登记(收欠收全额或部分金额)。
- **认证**:需管理后台 JWT。
- **幂等性**:否(每次调用产生一笔还款流水 GH- + 一笔资金流水 IN + 一条 RECOVER 操作流水;重复提交会重复入账,前端提交后应禁用按钮防连点)。
- **限流**:无。
**行为**:按本单收回指定金额 → 写还款流水(GH- 取号)+ 资金流水单笔 IN(bizType=COMPANY_LOAN)+ RECOVER 操作流水 + CAS 状态重算(收齐转已核销)+ 联动入账账户结存。与合并收款 §1.4.3 共用 `recoverSettle` settle 核心,守卫完全一致。
## 4. 接口入参
### 4.1 路径参数
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `id` | Long | ✅ | 借款单ID(path) |
### 4.2 请求体字段
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|----------|
| `amount` | BigDecimal | ✅ | 本次收回金额 | >0 且 ≤ 该单剩余余额,否则 599309 |
| `fundAccountId` | Long | ✅ | 入账资金账户ID | 须存在且 ACTIVE(fin_fund_account) |
| `feeRate` | BigDecimal | ❌ | 手续费率(‰) | 默认 0;fee = amount × feeRate / 1000,实收 = amount − fee |
| `voucherUrl` | String | ❌ | 收款凭证影像URL | — |
> ⚠️ **`unitId` / `unitType` 不入参**——后端从借款单反查归属单位,前端无需也不应传(防传错串单)。
## 5. 出参(响应)
复用合并收款 `CompanyLoanRecoverSettleRespVO`:
| 字段 | 类型 | 说明 |
|------|------|------|
| `totalAmount` | BigDecimal | 本次收回金额(=入参 amount) |
| `actualAmount` | BigDecimal | 实收(= totalAmount − fee) |
| `fee` | BigDecimal | 手续费 |
| `repayIds` | Long[] | 还款流水ID列表(**单笔恒单元素**) |
| `settledLoanIds` | Long[] | 本次收齐转已核销的借款单ID(收齐时含本单 id,未收齐为空) |
## 6. 枚举 / 数据字典
本接口入参/出参无枚举字段。相关业务方向(由后端从借款单反查,前端不传):
### 6.1 direction(借款方向,借款单自身字段)
| 值 | 中文 | 说明 |
|----|------|------|
| `OUT` | 借出 | 公司借钱给单位/员工,到期**收回**(本接口只收 OUT 单) |
| `IN` | 借入 | 公司向单位/员工借钱,到期**归还**(走归还链路,非本接口) |
## 7. 错误码
| code | 含义 | 触发场景 |
|------|------|----------|
| 599301 | 借款单不存在 | id 查无此单 |
| 599310 | 借款方向不匹配 | 该单非 OUT(借出),借入单不能走收回 |
| 599308 | 借款单已核销 | 已收齐核销的单不可再收 |
| 599302 | 借款单状态不可收 | 非可收回状态(如待付款未放款) |
| 599309 | 收回金额超剩余余额 | amount > 该单剩余余额 |
| 599303 | 手续费率非法 | feeRate < 0 或 ≥ 1000 |
| 599307 | 还款单号取号耗尽 | GH- 单号序列耗尽(极少) |
| 595001 | 资金账户不存在 | fundAccountId 查无此账户 |
| 595006 | 资金账户不可用 | 账户非 ACTIVE |
## 8. 示例
### 8.1 典型成功(足额收齐,无手续费)
**请求**:
```http
POST /admin/finance/company-loans/101234/recover
Authorization: Bearer <token>
Content-Type: application/json
{
"amount": 5000.00,
"fundAccountId": 88
}
```
**响应**(收齐转已核销):
```json
{
"code": 0,
"data": {
"totalAmount": 5000.00,
"actualAmount": 5000.00,
"fee": 0.00,
"repayIds": [900001],
"settledLoanIds": [101234]
},
"msg": ""
}
```
### 8.2 边界(部分收回 + 含手续费)
**请求**:
```json
{
"amount": 2000.00,
"fundAccountId": 88,
"feeRate": 5,
"voucherUrl": "https://oss.example.com/voucher/x.jpg"
}
```
**响应**(部分收回,settledLoanIds 为空;fee=2000×5/1000=10,实收 1990):
```json
{
"code": 0,
"data": {
"totalAmount": 2000.00,
"actualAmount": 1990.00,
"fee": 10.00,
"repayIds": [900002],
"settledLoanIds": []
},
"msg": ""
}
```
### 8.3 业务失败(超额收回)
**请求**:
```json
{ "amount": 99999.00, "fundAccountId": 88 }
```
**响应**:
```json
{ "code": 599309, "msg": "收回金额超剩余余额", "data": null }
```
## 9. 业务边界
- ✅ **适用**:direction=OUT(借出)、未收齐(核销中 / 部分核销)的借款单。
- ❌ **不适用**:借入单(IN,走归还链路)→ 599310;已收齐核销单 → 599308;待付款未放款单 → 599302。
- ⚠️ **特殊**:amount 收齐该单余额时本单转已核销(`settledLoanIds` 含本单 id);未收齐则保持核销中(`settledLoanIds` 为空),可再次调用收回剩余。
## 10. 修改前后对比
新增接口,无修改前版本。与原「按单位合并收款」的关系:
| 维度 | 按单位合并收款(§1.4.3) | 单笔收回(本接口) |
|------|--------------------------|--------------------|
| 入口 | 勾选同单位多笔合并 | 单笔直接收 |
| 入参 | unitId + items[](多单填额) | path id + 单 amount |
| 还款流水 | 多单逐笔 | 单笔一条 |
| 共用 | — | 与本接口共用 settle 核心,守卫一致 |
## 11. 影响评估 / 回滚
- **是否破坏向后兼容**:否(纯新增端点)。
- **前端是否必须同步上线**:否(前端可在「公司借款支付」已付款行接入本接口实现单笔收款登记;不接入不影响既有合并收款)。
- **回滚方式**:revert PR #8670 即可下线本端点,无数据迁移、无缓存清理。
## 12. 注意事项
- **原型/菜单侧**:收回原型入口已并入「支付管理 / 公司借款支付」已付款台账行「收款登记」;原「收款管理 / 公司借款收回」按单位汇总收银台原型入口已下线。前端实现管理后台时,单笔收款登记建议挂在已付款借出单行(只有已付款台账能收回)。
- 提交后请禁用按钮防连点(非幂等,重复提交会重复入账)。
- 合并收款三端点后端保留可用,如未来仍提供按单位合并入口可继续用 §1.4.1-1.4.3。
## 13. 关联 / 联系人
- **Issue**: [#8664](https://git.1814.love/wx/HL/issues/8664)
- **PR**: [#8670](https://git.1814.love/wx/HL/pulls/8670)
- **Merge commit**: [11a61d3739](https://git.1814.love/wx/HL/commit/11a61d3739)
- **后端负责人**: @yst