--- schema: "hl-changelog/v2" ticket: "8677" title: "团期预支可支取上限改为「整团已收(扣已退)− 在途」——GB-ADM-040 advanceAvailable 与 GB-ADM-042 发起上限同步改口径" consumer: "admin" author: "jw(GIT)" change_type: "修改接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "not_required" frontend_owner: "" frontend_ref: "" target_release: "" verified_at: "" status_note: "团期预支的可支取上限原为「团期尾款池(Σ 各户 max(0, 应收 − 已付))− 在途」,全额收款的团可支取恒为 0(TEST「11月1日额济纳胡杨林深秋4日游」应收 = 已收 26340.00 → 0.00),部分收款的团反倒能按还没收的钱预支。jw 2026-10-01 定案改为「团期已收池 − 在途」:已收池 = Σ 本团活跃子订单 max(0, 已付 − 已退),CANCELLED 户记 0;硬上限,超额 585004、无放宽通道;普通订单上限不改;部分收款团上限收紧属预期;存量不回溯(在途已超新上限时可支取显示 0.00、只拦新申请)。GB-ADM-040 的 advanceAvailable 与 GB-ADM-042 的发起校验共用同一方法,同时生效。路径、入参、出参字段名与类型零变化,错误码不变;变的是取值口径,属「语义取值」变化,按 jw 2026-09-18 口径推送。已合并 dev-v3(PR #8686,merge commit f4f545148)并部署测试服。hl-ui v2.1 弹窗「剩余可支取」与财务 Tab「可支取余额」都直接显示后端 advanceAvailable,前端零改动,frontend_status 记 not_required。" updated_at: "2026-10-01" base: "dev-v3" --- # 团期预支可支取上限改为整团已收(管理后台) > **服务**: hl-order-service-v3(端口 8086/8186) ## 一、接口背景 团期「发起团期预支」弹窗里的「剩余可支取」、财务 Tab 的「可支取余额」,以及发起时的超额校验,原先都按**整团还没收的尾款**算上限。 全额收款的团尾款为 0,于是一分钱都预支不了;只收了一部分的团,反倒能按还没收的钱预支。 本次改为只预支已经收到手的钱:上限 = 整团已收(扣已退)− 在途预支。 ## 二、变更接口清单 | # | 接口 | 方法 | 路径 | 变更类型 | 说明 | |---|---|---|---|---|---| | 1 | GB-ADM-040 团期财务总览 | GET | `/v3/admin/order/group-batch/{groupBatchId}/finance` | 修改接口 | 出参 `advanceAvailable` 取值口径改为「团期已收池 − 在途」;字段名、类型与其余字段零变化 | | 2 | GB-ADM-042 发起团期预支 | POST | `/v3/admin/order/group-batch/{groupBatchId}/advance` | 修改接口 | 金额上限改为同一口径;入参、出参、错误码零变化 | ## 三、接口详情 ### 1. GB-ADM-040 团期财务总览 `GET /v3/admin/order/group-batch/{groupBatchId}/finance` **VO**: `GroupBatchFinanceRespVO` #### 使用场景 团期详情「财务」Tab 切换即加载:四张金额卡、可支取余额、待审批预支、逐户付款。「发起团期预支」弹窗的「剩余可支取」也直接取本接口的 `advanceAvailable`。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | groupBatchId | path | Long | 是 | 正整数,团期聚合主键 | 团期 ID,不存在返 589501 | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | advanceAvailable | BigDecimal(字符串序列化) | **本次改口径**。可支取余额 = max(0, 团期已收池 − 在途)。团期已收池 = Σ 本团活跃子订单 max(0, 已付 − 已退),CANCELLED 户记 0;在途 = 本团期待审批 + 已通过 + 已付款的预支(团期级 ∪ 子订单级,口径不变)。即发起预支的硬上限 | | receivedAmount | BigDecimal | 整团已收(既有字段,本次未改)。毛已付,**不扣退款**;有退款时 `advanceAvailable` 的基数会小于它 | | receivableAmount | BigDecimal | 整团应收(既有字段,本次未改) | | unpaidAmount | BigDecimal | 整团待收(既有字段,本次未改;**不再**是预支上限的基数) | | advanceApproved | BigDecimal | 已预支(既有字段,本次未改) | | advancePending | BigDecimal | 待审批预支(既有字段,本次未改) | #### 请求示例 ```http GET /v3/admin/order/group-batch/2104839654727618562/finance HTTP/1.1 Host: api.test.1814.love Authorization: Bearer ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "receivableAmount": "26340.00", "receivedAmount": "26340.00", "unpaidAmount": "0.00", "advanceApproved": "0.00", "advancePending": "0.00", "advanceAvailable": "26340.00" } } ``` #### 空数据 / 降级响应 团里没有活跃子订单、或已收全部被在途占满时,`advanceAvailable` 返回 `"0.00"`,不返回 null、不返回负数。 ```json { "code": 200, "message": "成功", "data": { "receivableAmount": "0.00", "receivedAmount": "0.00", "unpaidAmount": "0.00", "advanceApproved": "0.00", "advancePending": "0.00", "advanceAvailable": "0.00" } } ``` #### 错误响应 ```json { "code": 589501, "message": "团期不存在", "data": null } ``` | 错误码 | 触发条件 | |---|---| | 589501 | groupBatchId 不存在或已软删 | #### 业务边界 - 展示值与 GB-ADM-042 的发起校验出自同一个方法,弹窗显示多少就能发起多少。 - 取户与在途同源:活跃子订单集按 `group_batch_id` 一跳圈定,已取消户不计入已收。 - 部分退款的户按「已付 − 已退」计入;单户已退不少于已付时按 0 计。 - 存量不回溯:在途已超过新上限的团(存量预支或事后退款),这里显示 `"0.00"`,已有预支记录不变。 ### 2. GB-ADM-042 发起团期预支 `POST /v3/admin/order/group-batch/{groupBatchId}/advance` **VO**: `OrderAdvanceRespVO` #### 使用场景 团期管理员为本团主报账人申请团期级预支,创建即待审批(SUBMITTED),财务在审批中心审批。本次只改金额上限的口径。 #### 入参 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |---|---|---|---|---|---| | groupBatchId | path | Long | 是 | 正整数 | 团期 ID | | payeeStaffId | body | Long | 是 | 须为本团主报账人(PRIMARY) | 借款对象,否则 589557 | | advanceType | body | String | 是 | 数据字典 `advance_type` 内的值 | 借款类型,否则 585006 | | amount | body | BigDecimal | 是 | ≥ 0.01,且 ≤ 当前 `advanceAvailable` | 预支金额;**上限口径本次改为团期已收池 − 在途**,超出 585004 | | purpose | body | String | 否 | ≤ 255 字 | 用途说明 | | voucherUrl | body | String | 否 | ≤ 512 字符 | 凭证文件 URL | #### 出参 | 字段 | 类型 | 说明 | |---|---|---| | id | Long | 预支 ID(既有,本次未改) | | status | String | 恒为 `SUBMITTED`(既有,本次未改) | | amount | BigDecimal | 预支金额(既有,本次未改) | | payeeStaffId / payeeName | Long / String | 领款人(既有,本次未改) | #### 请求示例 ```json { "payeeStaffId": 1009, "advanceType": "ACCOMMODATION_DEPOSIT", "amount": "24760.00", "purpose": "额济纳段酒店押金先行垫付" } ``` #### 响应示例 ```json { "code": 200, "message": "成功", "data": { "id": "2105531450843668481", "orderId": null, "payeeStaffId": "1007", "payeeName": "白云飞", "payeeRole": "LEADER", "advanceType": "ACCOMMODATION_DEPOSIT", "amount": 24760.0, "status": "SUBMITTED", "statusText": "待审批", "submittedAt": "2026-10-01 13:33:11" } } ``` #### 空数据 / 降级响应 本接口为写接口,无空数据形态;校验不通过时不写库、不占额度,返回下方错误。 ```json { "code": 585004, "message": "预支金额超过可用余额上限", "data": null } ``` #### 错误响应 ```json { "code": 585004, "message": "预支金额超过可用余额上限", "data": null } ``` | 错误码 | 触发条件 | |---|---| | 585004 | `amount` 大于当前可支取余额(团期已收池 − 在途);**本次口径变化**,错误码与文案不变 | | 585003 | `amount` ≤ 0 | | 589541 | 团期状态不允许发起预支(进入核单后关闭) | | 589557 | 借款对象不是本团主报账人 | #### 业务边界 - 上限在 `@Lock4j` 按 groupBatchId 串行的段内计算,读-算-插之间无并发窗口。 - 硬上限:超额一律 585004,没有放宽通道;审批时不再复核额度(维持现状)。 - 普通订单的预支上限不随本次改动,仍是「本单待收尾款 − 本单在途」。 - 部分收款的团上限随之收紧(例:应收 10720、已收 1600 的团,可支取由 9120.00 变为 1600.00)。 ## 四、契约约束与正确调用方式 - 弹窗「剩余可支取」与财务 Tab「可支取余额」继续直接读 `advanceAvailable`,**不要**用 `receivedAmount − advanceApproved − advancePending` 本地推算:`receivedAmount` 不扣退款,且在途还含已付款的预支。 - 发起前以最新一次 GB-ADM-040 的 `advanceAvailable` 为准;并发发起时以服务端 585004 为准。 ## 五、数据库行为 零数据库变更:不新增表、列或索引。GB-ADM-040 只读;GB-ADM-042 写入行为不变(校验通过后在 `order_advance` 插入一行 `scope=GROUP_BATCH`、`status=SUBMITTED`,并写团期时间线)。 上限的取数改为只读聚合 `order_main.paid_amount` / `refunded_amount`(按本团活跃子订单一次批量取),不再读应付尾款。 ## 六、边界行为 - 全额收款、无在途:可支取 = 整团已收净额。 - 已收全部被在途占满,或事后退款使已收低于在途:可支取 `"0.00"`,新申请 585004。 - 团里一户都不剩:已收为 0,可支取 `"0.00"`。 ## 六.6、修改前后对比 | 项 | 修改前 | 修改后 | |---|---|---| | 上限基数 | 团期尾款池 = Σ 各户 max(0, 应收 − 已付) | 团期已收池 = Σ 活跃户 max(0, 已付 − 已退),取消户记 0 | | 全额收款的团 | 可支取恒为 `0.00` | 可支取 = 整团已收净额 | | 部分收款的团 | 可支取 = 还没收的尾款 − 在途 | 可支取 = 已收净额 − 在途(收紧) | | 取户口径 | 按 `product_batch_id` 圈 | 按 `group_batch_id` 一跳(与在途同源) | | 路径 / 入参 / 出参 / 错误码 | — | 逐字未变 | ## 六.7、影响评估 - **兼容性**:字段名、类型、错误码不变;数值口径变化,前端零改动。 - **性能**:上限计算由一次按排期圈单改为一次按子订单 ID 批量取单,单团期无 N+1。 - **回滚**:撤销 PR #8686 的合并提交后重新部署 order-v3,无数据与配置残留。 ## 七、不影响范围 - 普通订单(非团期)的预支上限与订单级预支接口。 - 已预支 / 待审批两个数、预支记录列表(GB-ADM-043)、核单扣回口径。 - 网关:路径未变、无新增路由与权限码。 - 小程序端:本接口仅管理后台使用。 ## 八、测试环境已验证 2026-10-01 13:14–14:59 测试服(`api.test.1814.love`),部署 `dev-v3 @ f4f545148`(order-v3 双实例 8086/8186)。 每个状态节点都用 SQL 按同一公式独立手算并与 `advanceAvailable` 对照,**29 次对照全部一致**。 | 场景 | 读数 / 结果 | |---|---| | 部署身份:全额收款团「11月1日额济纳胡杨林深秋4日游」(应收 = 已收 26340.00) | 经网关 8 次 + 直连两实例全是 `"26340.00"`(旧口径为 `"0.00"`) | | 部分收款团「滇西北冬日三日团·一月廿二期」(应收 10720 / 已收 1600) | `"1600.00"`(旧口径为 9120.00) | | 自建全额收款团(已收 24760.00) | 可支取 `"24760.00"`;发起 24760.00 → 200 SUBMITTED,可支取变 `"0.00"`;再发起 0.01 → 585004 | | 自建部分收款团(应收 22080 / 已收 15720) | `"15720.00"`;待审 1200 后 `"14520.00"`;14520.01 → 585004,14520.00 → 200 | | 部分退款 1840(真实退款链路) | 21880 → `"20040.00"`;再取消一户(已付 7360)→ `"12680.00"` | | 在途 | 待审 / 已批 / 已付都扣;驳回、撤回不扣 | | 存量不回溯 | 在途占满后取消一户,可支取仍 `"0.00"`,新申请 585004,已有预支记录逐字段不变 | | 普通订单 | 上限仍是「本单待收 − 本单在途」:两档已付下超 0.01 都报 585004、等额都成功,与新口径可区分 | - 验收造数已全部回收,回读零残留。 - 单元测试:团期 / 预支整包 + 全部 ArchTest 258 类 4131 例 0 失败;合并提交上复跑定向 32 类 250 例 0 失败。 ## 十、相关文档 - 工单 #8677、PR #8686(merge commit `f4f545148`) - 团期统一池原设计:#7154(docs/group 接口文档 §0B.4,本次同步改为已收池) - 团单子订单禁走订单级入口、在途含 PAID:#8384 ## 关联 / 联系人 - 后端:jw - 前端:不涉及(`frontend_status: not_required`)