文件
hl-api-changelog/changelogs-v2/2026-09/24_8248_尾款收款模型重构-修改接口-管理后台.md
T
Mimingguang db68d11057
changelog-filename-gate / validate (push) Failing after 1s
docs(changelog): #8248 前端 verified(hl-admin b556da24)
2026-09-24 16:11:15 +08:00

20 KiB
原始文件 Blame 文件历史

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 8248 尾款收款模型重构:确认行程主报账人必填化 + 核单欠收硬闸按主报账人豁免 + 财务应收台账 admin yst 修改接口 deployed verified verified mmg b556da24791ca0ecf2393ef0e8505989c7996f8a v2.1 2026-09-24 尾款收款模型重构 Epic(方案甲·全实时)。PR-6a #8274(581062) + PR-5a #8263(584082) + PR-3 #8207 合并 + PR-5b #8325/②#8336/③#8328 部署。部署:hl-order-service-v3 dev-v3 @ baf643412,2026-09-24 滚动部署(8086/8186 均 UP)。部署后 fin↔order 对账 D1~D7 现网零漂移。 | 2026-09-24 mmg 交付:①LockModal 无主报账人选人必填化(星号+引导+置灰双保险);②两接口 JSDoc 补 581062/584082 新口径;③新增应收台账只读页(api/finance/receipt.js+finance/receipt/receivable,原型 recv-ledger 对齐,代收欠收互斥直显,订单号链接钻取);spec 5+3+2 例全绿;⚠菜单「收款管理/应收台账」依赖后端 sys_menu 补配(changelog 未提及,请后端补配) 2026-09-24 dev-v3

尾款收款模型重构:确认行程主报账人必填化 + 核单欠收硬闸豁免 + 财务应收台账

存放目录: 二期(v3) → changelogs-v2/2026-09/

服务: hl-order-service-v3(订单核心域 + 核单域)· hl-finance(收款域) Issue: wx/HL#8248 (主) · wx/HL#8265 · wx/HL#8191 PR: wx/HL#8274 · wx/HL#8263 · wx/HL#8207 日期: 2026-09-24 影响范围: 管理后台「订单确认行程弹框」「核单提交」「财务应收台账」三处


⚠️ 关键变化(前端必读)

  • 确认行程前必须先有主报账人:普通订单点「确认行程」时,如果本单还没有主报账人且弹框里也没选人,接口直接报 581062「请先指定本单主报账人(尾款代收人)」,确认动作不发生。确认弹框必须提供主报账人选择项并传 reporterAssignmentId。
  • 核单欠收硬闸放行面扩大:提交核单时「尾款未收齐」不再一律拦死——只要本单已指定主报账人,欠收部分记为主报账人代收口径,允许提交核单;只有「欠钱且无人兜底(无主报账人)」的单才会继续被 584082 拦截。
  • 新增财务应收台账只读分页接口:GET /admin/finance/receipt/receivable/page,订单维度看应收/已收/代收/欠收,代收列与欠收列互斥(指定主报账人后欠收挪入代收)。

一、接口背景

尾款收款模型重构 Epic(方案甲·全实时,不建债表)。旧模型里尾款由司导线下代收,经过「现场垫付 → 报销 → 核单 → 支付完成」长链路后才回写订单已付金额,导致:

  • 核单时「尾款未收齐」一律硬拦,司导已代收但还没走完报销回写的单被卡死;
  • 财务看不到「这笔钱到底在谁手里」——是客户还欠着,还是司导代收未回款。

新模型的三条规则:

  1. 金额实时算:应收/已收/欠收全部实时计算,不落地中间债表;
  2. 归属实时读主报账人:订单层有主报账人(reporterRank=PRIMARY)时,未收齐部分视为「主报账人代收中」;没有主报账人时才是「客户欠收」;
  3. 结清看核单终态:核单完成(FINALIZED)后由财务域支付完成事件回写已付金额,闭环。

本次三个接口分别对应:确认行程时把「主报账人」变成前置条件(接口1)、核单欠收硬闸按主报账人豁免(接口2)、财务侧新增应收台账把代收/欠收分列展示(接口3)。


二、变更清单

# 接口 方法 路径 变更类型 说明
1 确认行程 POST /v3/admin/order/{orderId}/confirm-itinerary 行为变更 + 新错误码 确认前必须已有主报账人,否则报 581062;入参出参结构不变
2 提交核单(完成核单) POST /v3/admin/order/{orderId}/settlement/finalize 行为变更 584082 只拦「无主报账人且欠收」的单;有主报账人放行,欠收走代收口径留痕
3 财务应收台账分页 GET /admin/finance/receipt/receivable/page 新增接口 订单维度应收/已收/代收/欠收分页,只读

说明:接口2 任务背景里常被称为「核单提交/生成报账」链路,硬闸实际落在「完成核单」写接口上;GET /v3/admin/order/{orderId}/settlement/reports/reimbursement(查询主报账人报账表)出参结构无任何变化。


三、接口详情

接口1:确认行程 POST /v3/admin/order/{orderId}/confirm-itinerary

使用场景

订单详情页点「确认行程」,把订单从「定制中」推进到「待出行」。普通订单在确认弹框中选择本单主报账人(尾款代收人)后提交;团期子订单由团期扇出自动带主报账人,一般无需选择。

  • 认证:管理后台 JWT
  • 幂等性:非幂等写操作,重复确认会被状态机拦截(订单已不在「定制中」)
  • 限流:无

入参

字段 位置 类型 必填 说明
orderId Path Long 是 订单 ID
reporterAssignmentId Body Long 否 新的主报账人人员安排 ID;不传则沿用当前主报账人。注意:本单当前没有主报账人时,不传会被 581062 拦截

请求体整体可空({} 或不传 body),但仅当订单已有主报账人时才能通过。

出参 Result<OrderTransitionRespVO>

字段 类型 说明
success Boolean 是否成功
oldStatus String 变更前订单状态(确认成功时恒为 CUSTOMIZING)
newStatus String 变更后订单状态(确认成功时恒为 PENDING_DEPARTURE)
oldFlowStatus String 变更前流程状态
newFlowStatus String 变更后流程状态
triggeredEvents Array 本次转换派生的事件列表

业务边界

  • 仅「定制中」订单可确认;5 项前置 checklist 未全过时仍报 581036(既有行为不变)。
  • 传了 reporterAssignmentId 会在确认前先把该人员置为主报账人,再校验主报账人存在性——即「弹框选人」与「确认」是一步完成的。
  • 团期子订单:团期人员扇出后订单层已有主报账人副本,不传 reporterAssignmentId 也放行;扇出延迟窗口期(极短)可能暂无主报账人被 581062 拦截,稍候重试即可。
  • reporterAssignmentId 传非数字/非法格式报 581046(既有行为不变)。

示例

典型成功(确认弹框选了主报账人)见「八、示例」8.1;无主报账人被拦见 8.3。


接口2:提交核单(完成核单)POST /v3/admin/order/{orderId}/settlement/finalize

使用场景

核单页核对完主报账、单团核算后点「提交核单/完成核单」,冻结核单快照并把订单推进到已核单。

  • 认证:管理后台 JWT
  • 幂等性:非幂等写操作,重复提交会被核单状态拦截
  • 限流:无

入参

字段 位置 类型 必填 说明
orderId Path Long 是 订单 ID(无请求体)

出参

Result<SettlementSubmitRespVO>(核单提交结果,结构无变化)。

业务边界(本次核心变化)

场景 变更前 变更后
尾款未收齐 + 无主报账人 报 584082 拦截 报 584082 拦截(不变)
尾款未收齐 + 已有主报账人 报 584082 拦截 放行,欠收记为主报账人代收口径
尾款已收齐 放行 放行(不变)
  • 「放行」不等于「欠收已清」:欠收金额会留在报账快照的欠收勾稽字段里,由财务域下游追款兜底,前端不要把「提交成功」理解为「钱已收齐」。
  • 代收口径:应代收 = 核单总额 − 客户线上已付(paidAmount)。旧的「司机现金代收登记」项已废止,代收不再计入已付金额。
  • 配套读接口 GET /v3/admin/order/{orderId}/settlement/reports/reimbursement(查询主报账人报账表)出参结构无变化。

示例

欠收但有主报账人提交成功、无主报账人被 584082 拦截,见「八、示例」8.2 / 8.3。


接口3:财务应收台账分页 GET /admin/finance/receipt/receivable/page(新增)

使用场景

财务「应收台账」页,按订单维度查看应收/已收/代收/欠收,用于内部财务对账与追款。

  • 认证:管理后台 JWT(网关注入)
  • 幂等性:只读
  • 限流:无

入参(Query)

字段 类型 必填 默认 说明
page Integer 否 1 页码
pageSize Integer 否 20 每页条数
keyword String 否 空 搜索关键字(团号 / 客户姓名 / 产品名 / 订单号 模糊);空=不限
receivableStatus String 否 空 收款状态筛选:UNPAID / PARTIAL / DONE;空或非法值=不按状态过滤

出参 Result<PageResult<ReceiptReceivableRowRespVO>>

分页外层固定为 records / total / page / pageSize。records[] 行字段:

字段 类型 说明
id String 订单 ID(字符串防精度丢失)
orderNo String 订单号
teamNo String 团号
productName String 产品名
customerName String 客户姓名
customerPhone String 客户手机号(明文,内部财务对账用)
orderStatus String 订单粗状态枚举值(见「六、枚举」)
orderStatusName String 订单粗状态中文名
receivableStatus String 收款状态:UNPAID 待收款 / PARTIAL 部分收款 / DONE 已收讫
receivableStatusName String 收款状态中文名
receivableAmount Number 应收总额(取消单归 0)
paidAmount Number 已收金额(毛额,退款不回减)
collectedAmount Number 代收金额(主报账人代收中的实时金额;取消单归 0)
balanceAmount Number 欠收金额(未指定主报账人的实时欠收;取消单归 0)

业务边界

  • 代收列与欠收列互斥:同一行 collectedAmount 与 balanceAmount 必有一列为 0——订单指定了主报账人,未收齐部分进代收列;未指定主报账人,进欠收列。两列合计恒等于该单实时待收尾款。
  • 收款状态由金额派生:已收=0 → UNPAID;已收>0 且仍有欠收/代收 → PARTIAL;欠收代收均=0 且已收>0 → DONE。
  • 只读接口,无登记/核销按钮;追款动作不在本接口。
  • 本接口在 hl-finance 服务(/admin/finance/*),与订单域接口不同服务,但同一网关入口。

示例

见「八、示例」8.1 / 8.2。


四、接口入参

各接口入参已分别内联在「三、接口详情」各小节,不再汇总大表。

五、出参字段

各接口出参已分别内联在「三、接口详情」各小节,不再汇总大表。

六、枚举 / 数据字典

6.1 reporterRank(报账人等级,接口1 相关概念)

值 中文名 说明
PRIMARY 主报账人 单内唯一;本次起确认行程前必须存在(尾款代收人)
SECONDARY 次报账人 单内唯一,不满足接口1 的前置条件
NONE 非报账人 默认值

6.2 receivableStatus(收款状态,接口3 行字段 + 筛选项)

值 中文名 派生条件
UNPAID 待收款 已收金额 = 0
PARTIAL 部分收款 已收 > 0 且(欠收 + 代收)> 0
DONE 已收讫 欠收 + 代收 = 0 且已收 > 0

6.3 orderStatus(订单粗状态,接口3 行字段)

值 中文名
PENDING_PAY 待支付
CUSTOMIZING 定制中
PENDING_DEPARTURE 待出行
TRAVELLING 出行中
COMPLETED 已完成
CANCELLED 已取消

七、错误码

错误码 报文 触发接口 说明
581062 请先指定本单主报账人(尾款代收人) 接口1 确认行程 本次新增。确认前置换后仍无主报账人时抛出;团期单扇出延迟窗口期被拦属预期,稍候重试
581036 确认订单前置校验未通过,请先补全所有必填项 接口1 确认行程 既有。5 项 checklist 未全过
581046 报账人ID格式非法,须为有效的数字ID 接口1 确认行程 既有。reporterAssignmentId 格式非法
584082 存在待收尾款,请收齐后再提交核单 接口2 提交核单 既有但触发条件收紧:现在仅「无主报账人且欠收不为 0」才抛出;有主报账人的欠收单不再触发

八、示例

8.1 典型成功

接口1:确认行程(确认弹框选了主报账人)

POST /v3/admin/order/2086272700140957697/confirm-itinerary
Authorization: Bearer <admin token>
Content-Type: application/json

{
  "reporterAssignmentId": 2072930844657283074
}
{
  "code": 200,
  "message": "成功",
  "data": {
    "success": true,
    "oldStatus": "CUSTOMIZING",
    "newStatus": "PENDING_DEPARTURE",
    "oldFlowStatus": "CUSTOMIZING",
    "newFlowStatus": "CONFIRMED",
    "triggeredEvents": ["CHECKLIST_CONFIRMED"]
  },
  "success": true
}

接口3:应收台账分页(指定了主报账人的在途单,欠收进代收列)

GET /admin/finance/receipt/receivable/page?page=1&pageSize=20&keyword=26-0001
Authorization: Bearer <admin token>
{
  "code": 200,
  "message": "成功",
  "data": {
    "records": [
      {
        "id": "2086272700140957697",
        "orderNo": "HL20260920001",
        "teamNo": "26-0001",
        "productName": "呼伦贝尔草原 5 日游",
        "customerName": "张三",
        "customerPhone": "13800001111",
        "orderStatus": "TRAVELLING",
        "orderStatusName": "出行中",
        "receivableStatus": "PARTIAL",
        "receivableStatusName": "部分收款",
        "receivableAmount": 12800.00,
        "paidAmount": 6400.00,
        "collectedAmount": 6400.00,
        "balanceAmount": 0
      }
    ],
    "total": 1,
    "page": 1,
    "pageSize": 20
  },
  "success": true
}

8.2 边界情况

接口2:尾款未收齐但已有主报账人——变更前被拦、变更后放行

POST /v3/admin/order/2086272700140957697/settlement/finalize
Authorization: Bearer <admin token>
{
  "code": 200,
  "message": "成功",
  "data": { "submitted": true },
  "success": true
}

注意:提交成功不代表欠收已清,欠收金额留在报账快照勾稽字段中,由财务域下游追款。

接口3:未指定主报账人的订单——同一笔未收齐金额进欠收列

{
  "receivableStatus": "PARTIAL",
  "receivableStatusName": "部分收款",
  "receivableAmount": 12800.00,
  "paidAmount": 6400.00,
  "collectedAmount": 0,
  "balanceAmount": 6400.00
}

接口3:空结果(关键词无匹配)

{
  "code": 200,
  "message": "成功",
  "data": { "records": [], "total": 0, "page": 1, "pageSize": 20 },
  "success": true
}

8.3 业务失败

接口1:无主报账人且弹框未选人 → 581062

POST /v3/admin/order/2086272700140957697/confirm-itinerary
Authorization: Bearer <admin token>
Content-Type: application/json

{}
{
  "code": 581062,
  "message": "请先指定本单主报账人(尾款代收人)",
  "data": null,
  "success": false
}

接口2:尾款未收齐且无主报账人 → 584082(唯一仍会被拦的情形)

POST /v3/admin/order/2086272700140957697/settlement/finalize
Authorization: Bearer <admin token>
{
  "code": 584082,
  "message": "存在待收尾款,请收齐后再提交核单",
  "data": null,
  "success": false
}

九、业务边界

  • 接口1 仅适用「定制中 → 待出行」确认动作;其他状态变更不受影响。
  • 接口1 团期子订单正常路径无需前端传参(扇出已带主报账人);扇出延迟窗口被 581062 拦时引导「稍候重试」即可,不要当成数据异常。
  • 接口2 放行后核单可正常完成,但财务仍会在应收台账/报账勾稽里看到代收欠收金额;「已核单」不等于「已收讫」。
  • 接口3 为只读台账,不提供任何写操作;customerPhone 为明文,仅限内部财务对账场景使用,前端不要在非财务页面引用该接口。
  • 接口3 已取消订单的金额列全部归 0。

十、修改前后对比

10.1 字段级对比

接口 字段 变更
接口1 确认行程 入参/出参全部字段 无变化(reporterAssignmentId 仍为非必填,但语义从「纯可选置换」变为「无主报账人时事实必填」)
接口2 提交核单 入参/出参全部字段 无变化
接口3 应收台账 全部字段 新增接口,无对比

10.2 行为级对比

场景 变更前 变更后
确认行程时本单无主报账人 直接确认成功 报 581062,确认不发生;传 reporterAssignmentId 选人后放行
核单时尾款未收齐、有主报账人 报 584082 拦死 放行,欠收记主报账人代收口径
核单时尾款未收齐、无主报账人 报 584082 拦死 报 584082 拦死(不变)
应代收口径 核单总额 −(已付 − 司机现金代收登记合计) 核单总额 − 已付(司机现金代收登记项已废止)
财务看尾款归属 无接口可看 应收台账代收/欠收互斥分列

十一、影响评估 / 回滚

  • 破坏性:接口1 对「此前无主报账人也能确认」的流程是行为收紧,普通订单确认弹框必须支持选择主报账人并传 reporterAssignmentId,否则确认会被 581062 拦截——前端需要同步上线。
  • 接口2 是放行面扩大,前端对 584082 的既有提示逻辑继续有效(触发面变窄),无强制改动;但原来「被拦 → 引导收尾款」的引导文案对「有主报账人」场景不再出现,如有相关 workaround 可清理。
  • 接口3 纯新增,不影响存量页面。
  • 回滚:后端回滚后,接口1 恢复「无主报账人也可确认」、接口2 恢复「欠收一律拦」、接口3 下线(请求返回 404)。回滚期间前端确认弹框保留选人逻辑无副作用(多传字段旧版兼容)。

十二、注意事项

  • 确认弹框的主报账人候选来自本单人员安排,reporterAssignmentId 传人员安排 ID(不是用户 ID、不是员工编号)。
  • 581062 与 581036 可能先后出现:先补 checklist(581036),再补主报账人(581062),前端引导顺序建议先 checklist 后选人。
  • 「确认成功」「核单提交成功」都不代表尾款已收齐;尾款是否收讫以应收台账 receivableStatus=DONE 为准。
  • 应收台账的代收/欠收两列互斥,前端渲染时不要对两列同时展示非 0 值做兜底合并——合计即实时待收尾款。
  • 本 Epic 还包含财务域支付完成后回写订单已付金额的配套链路(PR-5b 起),对管理后台 REST 契约无新增字段,不单独列接口。

十三、关联 / 联系人