文件
hl-api-changelog/changelogs-v2/2026-09/28_8384_团期预支防双花-修改接口-管理后台.md

13 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 8384 团期预支防双花:团单子订单禁走订单级预支入口(新错误码 589558)+ 团期财务在途预支口径补 PAID admin yst 修改接口 deployed verified implemented mmg 577683cf59a5ce3bf74bc48e6682be745de638c1 v2.1 2026-09-29 前端已交付(团期子订单隐藏订单级预支按钮,589558 拦截器透 message 兜底),详见 hl-admin v2.1 提交 577683cf。 2026-09-28 dev-v3

【修改接口·管理后台】团期预支防双花 (#8384)

PR: #8479 | 服务: hl-order-service-v3 | 更新时间: 2026-09-28 16:30

1. 接口背景

团期预支走「统一池」口径:同一团期下所有预支(团期级 + 各子订单级)共用一个尾款池、共享一条额度上限。本次修复该模型下的两个「双花」缺口:

  1. 入口双花:团期产品的子订单(即订单挂在某个团期下的单)此前仍可以从「订单级预支」入口申请预支,同一笔尾款可能在订单级、团期级两个入口被重复支取。业务拍板:团期子订单只允许走团期级预支入口,订单级入口一律拒绝。
  2. 口径双花:团期财务 Tab 的「可支取余额」在计算扣减项时,漏算了「已付款但核单尚未扣回」的预支(状态 PAID),导致这笔钱在核单完成前被重复放出额度。本次把 PAID 补入扣减口径,与订单级预支上限的既有口径对齐。

2. 变更清单

# 接口 方法 路径 变更类型 说明
1 创建预支(订单级) POST /v3/admin/order/{orderId}/advance 行为变更 团期子订单调用直接返回新错误码 589558,不再创建预支;普通散客订单行为不变
2 团期财务总览(财务 Tab) GET /v3/admin/order/group-batch/{groupBatchId}/finance 行为变更 「可支取余额」的统计口径补入 PAID 状态预支;字段结构零变化,返回值数值可能变小

3. 接口详情

3.1 创建预支(订单级)

  • 使用场景:订单详情页为单个订单的报账人申请预支借款(创建即进待审批)
  • 认证:需管理后台 JWT
  • 幂等性:否(同单并发创建由后端串行化)
  • 限流:无

3.2 团期财务总览(财务 Tab)

  • 使用场景:团期详情页「财务」Tab,展示整团应收/已收/待收/预支与逐户明细
  • 认证:需管理后台 JWT + group-batch:finance:view 权限
  • 幂等性:是(只读查询)
  • 限流:无

4. 接口入参

4.1 创建预支(POST /v3/admin/order/{orderId}/advance)

路径参数

字段 类型 必填 说明
orderId Long 必填 订单 ID

请求体字段

字段 类型 必填 说明 校验规则
payeeStaffId Long 必填 借款对象(取借款对象候选下拉接口 GET /v3/admin/order/{orderId}/advance/payee-candidates 返回的 id) 必须为本订单的在职人员
advanceType String 必填 预支借款类型(取数据字典 advance_type 的 dictValue) 非空
amount Number 必填 预支金额 必须 > 0,且不超过可支取余额上限
purpose String 可选 用途说明 —
voucherUrl String 可选 凭证文件 URL —

4.2 团期财务总览(GET /v3/admin/order/group-batch/{groupBatchId}/finance)

路径参数

字段 类型 必填 说明
groupBatchId Long 必填 团期 ID

无请求体、无 Query 参数。

5. 出参(响应)

5.1 创建预支 → OrderAdvanceRespVO

字段 类型 说明
id String(Long) 预支 ID
orderId String(Long) 订单 ID
teamNo String 团号
payeeStaffId String(Long) 报账人 staff 分配 ID(快照)
payeeName String 报账人姓名(快照)
payeeRole String 报账人角色代码
payeeRoleText String 报账人角色中文文案
advanceType String 预支类型
amount Number 预支金额
purpose String 用途说明
voucherUrl String 凭证文件 URL
status String 状态代码(枚举见 §6)
statusText String 状态中文文案
rejectReason String 驳回原因(仅 REJECTED 有值)
createdByName String 创建人姓名
createTime String(日期时间) 创建时间
submittedAt String(日期时间) 提交审批时间
approvedAt String(日期时间) 审批时间
approvedBy String 审批人姓名

5.2 团期财务总览 → GroupBatchFinanceRespVO

字段 类型 说明
receivableAmount String(金额) 整团应收
receivedAmount String(金额) 整团已收
unpaidAmount String(金额) 整团待收(本期总尾款,预支上限计算基数)
advanceApproved String(金额) 已预支 = 本团期 APPROVED 状态预支合计(团期级 + 各子订单级)
advancePending String(金额) 待审批预支 = 本团期 SUBMITTED 状态预支合计
advanceAvailable String(金额) 可支取余额 = 团期尾款池 −(待审批 + 已通过 + 已付款未核单),下限 0。本次口径变化字段
items Array 逐户付款明细(一户一行,全量返回不分页)
totals Object 整团合计行(totalPrice / paidAmount / unpaidAmount)
withdrawnCount Integer 已退团户数
primaryPayeeName String 主报账人姓名(未设置为 null)
secondaryPayeeName String 次报账人姓名(未设置为 null)

字段结构零变化;变化的是 advanceAvailable 的计算口径(见 §10)。

6. 枚举 / 数据字典

6.1 预支状态 status(AdvanceStatus)

所属字段:OrderAdvanceRespVO.status | 类型:String

值 中文 说明
SUBMITTED 待审批 创建即进此态,占额度
APPROVED 已通过 财务审批通过,占额度
PAID 已支付 出纳已付款、核单未扣回,仍占额度(核单完成才释放)
REJECTED 已驳回 终态,不占额度

7. 错误码

code 含义 触发场景
589558(新增) 团期订单请通过团期预支入口申请 对团期子订单(挂在团期下的订单)调用订单级创建预支接口 POST /v3/admin/order/{orderId}/advance 时直接返回,不创建预支
585003 当前订单状态不允许预支 订单状态不在 {待出发, 行程中} 或已结算完成(既有行为不变;团单判断优先于此码)
585004 预支金额超过可用余额上限 申请金额 > 可支取余额上限(既有行为不变)

团单判断先于状态判断:团期子订单即使状态满足,也固定返回 589558,不会返回 585003。

8. 示例(3 组:典型 / 边界 / 异常)

8.1 业务失败(新增场景):团期子订单调订单级预支入口

场景说明:订单是团期产品的子订单,前端仍调了订单级入口 → 返回 589558。

请求:

POST /v3/admin/order/1234567890123456789/advance
Authorization: Bearer <token>
Content-Type: application/json
{
  "payeeStaffId": 9876543210987654321,
  "advanceType": "TRAVEL_EXPENSE",
  "amount": 500.00,
  "purpose": "门票垫付"
}

响应:

{
  "code": 589558,
  "message": "团期订单请通过团期预支入口申请",
  "data": null
}

8.2 典型成功:普通散客订单正常创建预支

场景说明:订单不挂在任何团期下,状态为待出发/行程中、未结算完成 → 行为与改前完全一致。

请求:

POST /v3/admin/order/1111222233334444555/advance
Authorization: Bearer <token>
Content-Type: application/json
{
  "payeeStaffId": 9876543210987654321,
  "advanceType": "TRAVEL_EXPENSE",
  "amount": 500.00,
  "purpose": "门票垫付"
}

响应:

{
  "code": 200,
  "message": "success",
  "data": {
    "id": "7777888899990000111",
    "orderId": "1111222233334444555",
    "teamNo": "26-0480",
    "payeeStaffId": "9876543210987654321",
    "payeeName": "张领队",
    "payeeRole": "GUIDE",
    "payeeRoleText": "导游",
    "advanceType": "TRAVEL_EXPENSE",
    "amount": 500.00,
    "purpose": "门票垫付",
    "voucherUrl": null,
    "status": "SUBMITTED",
    "statusText": "待审批",
    "rejectReason": null,
    "createdByName": "李财务",
    "createTime": "2026-09-28T16:00:00",
    "submittedAt": "2026-09-28T16:00:00",
    "approvedAt": null,
    "approvedBy": null
  }
}

8.3 边界情况:团期财务 Tab 可支取余额口径

场景说明:某团期尾款池(unpaidAmount)= 7300.00;本团期有 1 笔预支已审批通过并已付款(PAID,金额 2000.00),核单尚未扣回。

请求:

GET /v3/admin/order/group-batch/5555666677778888999/finance
Authorization: Bearer <token>
(无请求体)

响应(改后):

{
  "code": 200,
  "message": "success",
  "data": {
    "receivableAmount": "40200.00",
    "receivedAmount": "32900.00",
    "unpaidAmount": "7300.00",
    "advanceApproved": "0.00",
    "advancePending": "0.00",
    "advanceAvailable": "5300.00",
    "items": [],
    "totals": { "totalPrice": "40200.00", "paidAmount": "32900.00", "unpaidAmount": "7300.00" },
    "withdrawnCount": 0,
    "primaryPayeeName": "张领队",
    "secondaryPayeeName": null
  }
}

对比:改前同一数据下 advanceAvailable 返回 "7300.00"(PAID 的 2000.00 未计入扣减);改后返回 "5300.00"。该笔 PAID 预支在核单完成扣回后才会释放额度。

9. 业务边界

  • 适用场景(订单级预支):不挂在任何团期下的普通散客订单,状态为待出发/行程中、未结算完成 → 行为与改前完全一致
  • 不适用场景(订单级预支):团期产品的子订单 → 固定返回 589558,须改用团期级预支入口(POST /v3/admin/order/group-batch/{groupBatchId}/advance,该接口逻辑本次不变)
  • 特殊边界(财务 Tab):团期下存在 PAID(已付款未核单)预支时,advanceAvailable 比改前变小;核单完成后该笔释放,advanceAvailable 回升。这是口径修正,不是数据异常

10. 修改前后对比

10.1 字段级对比

无字段增删 / 改名 / 类型变化。

10.2 行为级对比

行为 改前 改后
团期子订单调订单级预支入口 放行(只看本单状态/上限),可能与团期级重复支取同一笔尾款 直接返回 589558,不创建预支
团期财务 Tab「可支取余额」扣减口径 只扣 SUBMITTED + APPROVED 扣 SUBMITTED + APPROVED + PAID(已付款未核单也占额度)
普通散客订单订单级预支 原有校验链 不变
团期级预支入口 原有逻辑 不变

11. 影响评估 / 回滚

11.1 影响评估

  • 是否破坏向后兼容:行为收紧型变更。① 团期子订单此前能调通的订单级预支入口现在固定失败(589558)——若前端在团期子订单详情页暴露了订单级预支入口,需要处理该错误码;② 财务 Tab advanceAvailable 数值在存在 PAID 预支时变小,属口径修正
  • 前端是否必须同步上线:否。接口字段结构零变化;但建议尽快处理 589558 的提示与入口引导(把团期子订单的预支操作引导到团期级入口)
  • 影响已有数据:无数据迁移;既有 PAID 预支自动按新口径计入扣减

11.2 回滚方案

  • 回滚方式:revert merge commit 7bbc98c335,重新部署 hl-order-service-v3 即恢复原行为
  • 回滚后清理:无需清理数据 / 缓存
  • 回滚耗时:约等于一次常规服务部署

12. 注意事项

  • 若前端此前对「团期子订单调订单级预支失败」有任何本地兜底 / workaround(如自行判断团单后隐藏入口但保留了调用),本次后端已硬拦,相关兜底不会再触发到创建成功分支
  • 财务 Tab 的 advanceAvailable 请继续直接读接口返回值,不要本地自算(本次口径调整只发生在服务端,本地自算会与卡片对不上)
  • 需要重启的服务:hl-order-service-v3(测试服部署走 Deploy Panel 操作)

13. 关联 / 联系人

13.1 链接

13.2 联系人

  • 后端负责人: @yst