比较提交

...
27 次代码提交
作者 SHA1 备注 提交日期
Mimingguang c7d7b4c61e docs(changelog): #8813 前端标记 implemented
changelog-filename-gate / validate (push) Failing after 2s
2026-10-10 14:35:29 +08:00
yaosutu aa11cd2610 docs(changelog): 团期核单明细带出/暂存合并 + bizKey 溯源(#8813)
changelog-filename-gate / validate (push) Failing after 2s
- GET/PUT/POST settlement 三端点行为变化:已暂存 tab 持续带出未暂存预览行
- 行出参加 bizKey,暂存入参可透传 sourceType/bizKey
- PUT 响应新增 warnMessage/missedCarryOverCount,新错误码 589756/589757
- 前端必须同步上线(否则双显双计)
2026-10-10 12:44:09 +08:00
Mimingguang effa929648 docs(changelog): #8818 前端标记 implemented
changelog-filename-gate / validate (push) Failing after 2s
2026-10-10 10:22:00 +08:00
Mimingguang a6dfbd4719 docs(changelog): #8818 前端标记 implemented
changelog-filename-gate / validate (push) Failing after 1s
2026-10-10 10:21:45 +08:00
Mimingguang dcd914c8a5 docs(changelog): #8818 前端标记 implemented
changelog-filename-gate / validate (push) Failing after 3s
2026-10-10 10:14:05 +08:00
yaosutu 5409e499cd Merge branch 'main' of https://git.1814.love/wx/hl-api-changelog
changelog-filename-gate / validate (push) Failing after 1s
2026-10-10 10:04:29 +08:00
yaosutu 12d25a7f35 补充核单付款方式取值约定(settlement_payment_method 字典,单体/团期一致)及与 settleType 区分说明(#8818) 2026-10-10 10:04:11 +08:00
Mimingguang c55453fb89 docs(changelog): #8815 前端标记 implemented
changelog-filename-gate / validate (push) Failing after 2s
2026-10-10 09:54:08 +08:00
yaosutu aeb5106d90 新增团期核单酒店候选查询接口(#8818)
changelog-filename-gate / validate (push) Failing after 1s
2026-10-10 09:45:47 +08:00
yaosutu c87e079286 docs(changelog): 账户流水与微信商户对账双向查询关联(#8815,PR #8816)
changelog-filename-gate / validate (push) Failing after 2s
新增 2 个只读接口:对账→流水(/v3/admin/payment/wx-bill/records/{billRecordId}/fund-flow)
与流水→对账(/admin/finance/fund-flows/{flowId}/wx-bill-records),管理后台目录 changelogs-v2。
2026-10-09 17:55:39 +08:00
Mimingguang 90b18a1594 docs(changelog): 7 月 11 个历史文件 frontmatter 迁移 hl-changelog/v2(过远端门禁,补 change_type/backend_status/gateway_status/target_release)
changelog-filename-gate / validate (push) Failing after 2s
2026-10-09 16:28:06 +08:00
Mimingguang 3bf207769a docs(changelog): #5205 前端标记 implemented 2026-10-09 16:24:03 +08:00
Mimingguang d2b34953bc docs(changelog): #5200 前端标记 implemented 2026-10-09 16:23:48 +08:00
Mimingguang dd6794e4b8 docs(changelog): #5199 前端标记 implemented 2026-10-09 16:23:23 +08:00
Mimingguang c36c051667 docs(changelog): #5194 前端标记 implemented 2026-10-09 16:23:09 +08:00
Mimingguang a0dde3e6ad docs(changelog): #5186 前端标记 implemented 2026-10-09 16:22:55 +08:00
Mimingguang ed192a98a4 docs(changelog): #5149 前端标记 implemented 2026-10-09 16:22:29 +08:00
Mimingguang fe4fa3b70b docs(changelog): #5146 前端标记 implemented 2026-10-09 16:22:06 +08:00
Mimingguang a785dcbae6 docs(changelog): #5145 前端标记 implemented 2026-10-09 16:21:49 +08:00
Mimingguang 9ab4794549 docs(changelog): #5141 前端标记 implemented 2026-10-09 16:21:32 +08:00
Mimingguang f8fb2f34b7 docs(changelog): #5139 前端标记 implemented 2026-10-09 16:21:18 +08:00
Mimingguang 575292337b docs(changelog): #5132 前端标记 implemented 2026-10-09 16:20:53 +08:00
Mimingguang c96526f098 docs(changelog): 8809 回写前端交付(implemented,ref 1f96d87b5)
changelog-filename-gate / validate (push) Failing after 1s
2026-10-09 15:47:24 +08:00
yaosutu 0b6467b2c3 docs(changelog): 出纳付款联动司导收款账户(报账款线选/手填建档,#8809)
changelog-filename-gate / validate (push) Failing after 2s
- 修改 POST /admin/finance/cashier/reimburse/pay:入参加 payeeAccountId/newPayeeAccount,出参加收款账户留痕
- 新增 GET /admin/finance/payee-accounts/list-by-payee 选账户下拉数据源
- 队列/台账/报账详情出参加报账人档案与收款账户字段
- 新增错误码 598612-598615
2026-10-09 14:07:38 +08:00
Mimingguang 3ce7ef580a docs(changelog): 8807 回写前端交付(implemented,ref cb0d22fa3)
changelog-filename-gate / validate (push) Failing after 2s
2026-10-09 11:21:33 +08:00
yaosutu 1029581523 docs(changelog): 8807 微信商户对账逐笔账单记录出参新增 appid 字段(PR #8808)
changelog-filename-gate / validate (push) Failing after 1s
2026-10-09 11:01:46 +08:00
yaosutu 068e6ef2d7 docs(changelog): 8807 微信商户对账逐笔账单记录出参新增 appid 字段(PR #8808) 2026-10-09 11:01:06 +08:00
共修改 16 个文件,包含 1665 行新增和 44 行删除
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5186"
title: "排车中订单恢复派车派人入口并补齐改派上下文"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "158f728ce9110ca6a9e0185c137969f84b522c6f"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-23 交付(恢复排车中订单派车派人入口,158f728ce 起含 2f4d278a6/54c007856 后续修正),证据为交付 commit 正文标注 #5186"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-23T16:10:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5194"
title: "待确认详情补全多车多司机与按槽位改派"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "bfb4e3cde018d14b2b5dedaacacb83128cc7714d"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-23 交付(多槽位改派与司机确认,bfb4e3cde),证据为交付 commit 正文标注 #5194"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-23T18:02:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5199"
title: "车务首页订单去重与字段补全"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "3c4f8619bbc569d72e73cedba9a74b2cefc67402"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-24 交付(车务首页订单聚合契约对齐,3c4f8619b),证据为交付 commit 正文标注 #5199"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-24T09:46:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5200"
title: "用车需求驳回历史与重新提交"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "d7afc25146ed1bceea3e3722c521babd85c1345e"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-24 交付(用车需求驳回历史与重新提交,d7afc2514),证据为交付 commit 正文标注 #5200"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-24T11:05:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5205"
title: "车务首页汇总与看板人员类型展示"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "78cbb1565b6555788607b34f591f0291a87af96d"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-24 交付(车务汇总与人员类型展示对齐,78cbb1565),证据为交付 commit 正文标注 #5205"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-24T10:45:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5132"
title: "车务派单详情补全产品、行程节点、出行人与大交通"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "524cfb21d24074e8e22c5ca7a89cbbd693f59abb"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(派单详情新增字段展示/出行人类型区分,524cfb21d 起 3 个 commit),证据为交付 commit 正文标注 #5132"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T10:35:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5139"
title: "车务派单候选筛选、分页与任意车辆选择"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "a0e8a6ef15a5a1983ddab3ca2958444e7971ae77"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(派单候选筛选与任意车辆选择接入,a0e8a6ef1),证据为交付 commit 正文标注 #5139"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T13:25:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5141"
title: "车务派单司机保险类型与行程保障状态"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "29ab0d6562413be6ab715d8b1e1eb2d4521a9eff"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(司机保险保障状态与线上投保接入,29ab0d656),证据为交付 commit 正文标注 #5141"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T14:07:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5145"
title: "选车后常驻司机默认配对"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "b9994da9308ae84d361e79e86f74200813ec06cc"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(选车后常驻司机默认配对接入,b9994da93),证据为交付 commit 正文标注 #5145"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T15:30:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5146"
title: "司机待确认通知与可选确认凭证"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "089e55ffb6f4336c19ffd2ed2eade131bdb35bf7"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(司机确认登记与可选凭证接入,089e55ffb),证据为交付 commit 正文标注 #5146"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T15:20:00+08:00"
---
@@ -1,11 +1,18 @@
---
schema: "hl-changelog/v1"
schema: "hl-changelog/v2"
ticket: "5149"
title: "派单详情补充团号与产品类型"
consumer: "admin"
backend: "verified"
gateway: "verified"
frontend: "pending"
change_type: "修改接口"
backend_status: "verified"
gateway_status: "verified"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "ccbdd0bd1b7c903846c8ae9e4e9e15d80266b9a4"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "7 月历史补标:已于 2026-07-22 交付(派单详情补充团号与产品类型,ccbdd0bd1),证据为交付 commit 正文标注 #5149"
updated_at: "2026-10-09"
base: "dev-v3"
generated: "2026-07-22T16:22:00+08:00"
---
@@ -0,0 +1,216 @@
---
schema: "hl-changelog/v2"
ticket: "8807"
title: "微信商户对账·逐笔账单记录出参新增 appid(区分小程序/企业微信收款渠道)"
consumer: "admin"
author: "yst"
change_type: "修改接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude-opus-4-8)"
frontend_ref: "cb0d22fa3"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "后端 PR #8808 已合并 dev-v3(squash 56715ad31e),含 Flyway 迁移 V20261009_001(wx_bill_record 加 appid 列),需部署 order-v3 后新拉取的账单才有值;历史数据与资金账单恒 null。前端已交付:逐笔账单抽屉加「收款渠道」列(wx=小程序/ww=企业微信前缀映射,null 显 —)。"
updated_at: "2026-10-09"
base: "dev-v3"
---
# 微信商户对账 · 逐笔账单记录出参新增 appid 字段
> 面向:管理后台前端(hl-admin)
> 日期:2026-10-09 | 后端:order-v3,PR 已合并 dev-v3
> 原始接口契约(同仓):`changelogs-v2/2026-10/01_8690_微信商户对账-新增接口-管理后台.md`(本篇只写增量变化,字段表自包含)
## 1. 接口背景
微信商户对账(#8690 上线的 6 端点之一)的「逐笔原始账单记录」接口,出参每条记录新增 `appid` 字段,用于区分**收款渠道**——同一商户号下,钱可能是从小程序(`wx...`)付的,也可能是企业微信(`ww...`)收的。此前前端无法从账单记录判断来源渠道,差异排查和渠道维度统计只能靠人工认商户单号。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 逐笔原始账单记录 | GET | /v3/admin/payment/wx-bill/records | 修改接口 | 出参 `records[]` 新增字段 `appid`(String,可空)。入参 / 其余出参 / 枚举均无变化 |
## 3. 接口详情
### 3.1 逐笔原始账单记录 GET /v3/admin/payment/wx-bill/records
- **使用场景**:对账汇总行「明细」抽屉,看该商户该日微信账单原始逐笔(交易账单/资金账单统一出参,billType 区分)。
- **认证**:需管理后台 JWT。
- **幂等性**:只读。
- **限流**:无。
## 4. 接口入参(无变化)
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|----------|
| page | Integer | 否 | 页码,默认 1 | 最小 1 |
| pageSize | Integer | 否 | 每页条数,默认 20 | 1-100 |
| billDate | Date | 否 | 账单日期(yyyy-MM-dd,单日) | — |
| mchId | String | 否 | 微信商户号(精确匹配) | — |
| billType | String | 否 | 账单类型 | 仅 TRADE / FUND_FLOW |
## 5. 出参字段
统一 `Result` 包装;分页为 `PageResult`(records / total / page / pageSize)。Long 主键 id 以 **JSON 字符串**返回。
### 5.1 账单原始记录行 WxBillRecordRespVO(变化后)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String(Long) | 记录ID |
| mchId | String | 微信商户号 |
| billDate | Date | 账单日期 |
| billType | String | 账单类型:TRADE / FUND_FLOW |
| wxTransactionId | String | 微信支付单号(交易账单有,资金账单可空) |
| outTradeNo | String | 商户单号(关联本地支付流水) |
| fundFlowId | String | 资金流水单号(资金账单有,交易账单可空) |
| tradeTime | DateTime | 交易/记账时间 |
| tradeType | String | 交易类型/业务类型(如 JSAPI) |
| tradeState | String | 交易状态/收支方向(如 SUCCESS) |
| amount | BigDecimal | 交易金额(元) |
| payerAmount | BigDecimal | 用户实付(元) |
| feeAmount | BigDecimal | 手续费(元) |
| **appid** | **String,可空** | **新增**:微信公众账号ID。交易账单(billType=TRADE)有值——小程序为 `wx` 前缀(如 `wx4711a76772deff36`)、企业微信为 `ww` 前缀(如 `ww0123456789abcdef`);资金账单(FUND_FLOW)恒 null |
## 6. 枚举 / 数据字典
- 本次**无枚举/字典变化**。`appid` 返回的是微信原始值字符串,**不做翻译**。
- 渠道中文名(小程序 / 企业微信)后期会以数据字典补充;本次前端如需展示渠道名,可自行按前缀映射(`wx` 开头 = 小程序,`ww` 开头 = 企业微信),或暂展示原始值等字典。
## 7. 错误码
- 本次**无新增错误码**。沿用 #8690 既有:参数校验错误(billType 非法值等)走统一参数校验响应。
## 8. 示例
### 8.1 典型成功(交易账单,小程序收款)
请求:
```
GET /v3/admin/payment/wx-bill/records?billDate=2026-10-08&billType=TRADE&page=1&pageSize=20
```
响应(节选一条 records):
```json
{
"code": 0,
"data": {
"records": [
{
"id": "1976123456789012345",
"mchId": "1600000001",
"billDate": "2026-10-08",
"billType": "TRADE",
"wxTransactionId": "4200001234202610081234567890",
"outTradeNo": "HL20261008120000123456",
"fundFlowId": null,
"tradeTime": "2026-10-08 12:00:01",
"tradeType": "JSAPI",
"tradeState": "SUCCESS",
"amount": 1280.00,
"payerAmount": 1280.00,
"feeAmount": 7.68,
"appid": "wx4711a76772deff36"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}
```
### 8.2 边界情况
**8.2a 企业微信收款(ww 前缀 appid)**:
```json
{
"billType": "TRADE",
"tradeType": "JSAPI",
"tradeState": "SUCCESS",
"amount": 500.00,
"appid": "ww0123456789abcdef"
}
```
**8.2b 资金账单(appid 恒 null)**:
```
GET /v3/admin/payment/wx-bill/records?billDate=2026-10-08&billType=FUND_FLOW
```
```json
{
"billType": "FUND_FLOW",
"wxTransactionId": null,
"fundFlowId": "110000000120261008000001",
"tradeState": "IN",
"amount": 1280.00,
"appid": null
}
```
### 8.3 业务失败(billType 非法值)
```
GET /v3/admin/payment/wx-bill/records?billType=FOO
```
```json
{
"code": 400001,
"msg": "请求参数不合法",
"data": null
}
```
(错误码数值以线上实际为准,属统一参数校验段位;非法 billType 只允许 TRADE / FUND_FLOW)
## 9. 业务边界
- **适用**:汇总行「明细」抽屉展示逐笔账单时,可按 `appid` 前缀区分/标注收款渠道。
- **仅交易账单有值**:`appid` 只在 billType=TRADE 的记录上有值;资金账单(FUND_FLOW)没有公众账号维度,恒为 null,前端不要对资金账单渲染渠道。
- **部署时序**:本字段依赖 Flyway 迁移 `V20261009_001`(`wx_bill_record` 表加 `appid` 列)。**order-v3 未部署到本版本前,前端拿到的响应里该字段为 null(或缺失)**;部署后**新拉取**的账单才有值,已落库的历史账单不回填。
- **null 兼容**:前端渲染时 `appid` 一律按可空处理。
## 10. 修改前后对比
### 10.1 字段级
| 字段 | 原来 | 现在 |
|------|------|------|
| records[].appid | **不存在** | 新增,String 可空;TRADE 有值(wx/ww 前缀),FUND_FLOW 恒 null |
### 10.2 行为级
| 项 | 原来 | 现在 |
|----|------|------|
| 入参 | — | 无变化 |
| 其余出参字段 | — | 无变化 |
| 渠道识别 | 无法从账单记录判断收款渠道 | 可按 appid 前缀(wx=小程序 / ww=企业微信)区分 |
## 11. 影响评估 / 回滚
- **破坏性**:**无**。纯出参字段新增,不改名、不删字段、不改类型、不改枚举。前端不接此字段可完全无感。
- **前端同步上线**:**不要求**。前端按自己节奏接入即可;未接期间字段躺平不影响现有功能。
- **回滚方案**:如需回滚,order-v3 回退到 #8808 之前的版本即可——appid 字段消失,已接前端按可空读取自动退化为原行为,无需前端配合回滚。DB 列保留无害(Flyway 不做列回退)。
## 12. 注意事项
1. **appid 是原始值,不是中文名**:展示渠道名需前端自行按前缀映射或等后期数据字典。
2. **判空再渲染**:资金账单恒 null,未部署时全量 null,务必可空处理。
3. **Long id 字符串化**不变:records[].id 仍是 JSON 字符串。
4. 接口其余 5 个对账端点(summaries / diffs / diffs 详情 / handle / reconcile/run)本次**无任何变化**。
## 13. 关联 / 联系人
- Issue:https://git.1814.love/wx/HL/issues/8807
- PR:https://git.1814.love/wx/HL/pulls/8808
- Commit:https://git.1814.love/wx/HL/commit/56715ad31e
- 后端负责人:腰苏图(yst)
@@ -0,0 +1,415 @@
---
schema: "hl-changelog/v2"
ticket: "8809"
title: "出纳付款联动司导收款账户(选已建档账户 / 手填自动建档,报账款线)"
consumer: "admin"
author: "yst"
change_type: "修改接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude-opus-4-8)"
frontend_ref: "1f96d87b5df0c4e56e9db65f43856b96fb849269"
target_release: "v2.1"
verified_at: "2026-10-09"
status_note: "后端 PR #8810 已合并 dev-v3(squash e50a43b72a),含 Flyway 迁移 V20261009_136(fin_reimburse 加收款账户两列 + fin_payee_account 唯一索引),需部署 order-v3 后生效;存量报账单新字段为 null。前端已交付:付款弹窗 REIMBURSE 门控加收款账户「选已建档(list-by-payee 下拉+默认预选)/手填建档」二选一,CASH 隐藏清空,账户类型按付款方式过滤;台账加收款账户留痕列、报账详情加快照展示;checkpoint 全量通过"
updated_at: "2026-10-09"
base: "dev-v3"
---
# 出纳付款联动司导收款账户(报账款线)
> 面向:管理后台前端(hl-admin)
> 日期:2026-10-09 | 后端:hl-finance(编译进 order-v3 同进程,端口 8086)
> 范围:只动 **REIMBURSE 报账款付款线**;其余 7 条出纳付款线(advance / company-loan / expense / payment / nonbiz 等)入参壳共用但对新字段**静默忽略**,行为零变化。
## 1. 接口背景
报账款是出纳付款给司导/报账人的钱。此前出纳付款表单只有「公司出账账户」(payAccountId),**没有「司导收款账户」**——出纳把钱打到哪张卡/哪个收款码全靠线下沟通,系统无留痕。
本次让出纳在付款弹窗里:
1. **选账户**:下拉列出该报账人已建档的收款账户(新接口 list-by-payee 提供数据源,队列行已自动带出默认账户);
2. **手填建档**:现场登记新收款账户,付款成功同时自动落入收款账户主数据(fin_payee_account),账户名强制取服务人员档案真名,不允许手填名称。
付款成功后台账、报账详情均可回查「这单钱打给了哪个账户」。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 报账款登记付款 | POST | /admin/finance/cashier/reimburse/pay | 修改接口 | 入参新增 payeeAccountId 与嵌套 newPayeeAccount(二选一);出参加 payeeAccountId / payeeAccountLabel |
| 2 | 按收款方查账户全集 | GET | /admin/finance/payee-accounts/list-by-payee | **新增接口** | 付款弹窗「选账户」下拉数据源,轻量不分页 |
| 3 | 报账款待付款队列 | GET | /admin/finance/cashier/reimburse/queue | 修改接口 | 出参加 bizType / reporterAssignmentId / reporterRefId / reporterPayeeType / defaultPayeeAccountId / defaultPayeeAccountLabel |
| 4 | 出纳台账 | GET | /admin/finance/cashier/payments/page | 修改接口 | 出参行(仅 REIMBURSE 行)回填 payeeAccountId / payeeAccountLabel 留痕 |
| 5 | 报账详情 | GET | /admin/finance/reimburses/{id} | 修改接口 | 出参加 payeeAccountId / payeeAccountSnapshot 留痕 |
其余出纳付款线(advance / company-loan / expense / payment / nonbiz 等 7 条)的 /pay 接口:入参壳上能看到 payeeAccountId / newPayeeAccount 两个字段,但**传了会被静默忽略,不报错**,行为与之前完全一致——前端不要在这些页签渲染收款账户控件。
## 3. 接口详情
### 3.1 报账款登记付款 POST /admin/finance/cashier/reimburse/pay
- **使用场景**:出纳在「财务出纳-报账款」页签对待付款报账单(status=APPROVED 且 direction=PAYABLE)执行付款,记资金流水 OUT + 回写 PAID。
- **认证**:需管理后台 JWT。
- **幂等性**:有。重复付款由 CAS 条件更新兜底(重复提交报 598602 状态冲突),重复手填建档由收款账户唯一索引兜底(不重复建档)。
- **限流**:无。
### 3.2 按收款方查账户全集 GET /admin/finance/payee-accounts/list-by-payee
- **使用场景**:付款弹窗打开时,用队列行的 reporterPayeeType + reporterRefId 拉该报账人全部收款账户,渲染「选账户」下拉。
- **认证**:需管理后台 JWT。
- **幂等性**:只读。
- **限流**:无。
### 3.3 报账款待付款队列 GET /admin/finance/cashier/reimburse/queue
- **使用场景**:报账款页签列表;行数据现在直接带主报账人档案信息与默认收款账户,前端打开付款弹窗时**无需二次查询**即可预选默认账户。
- **认证 / 幂等 / 限流**:同上,只读。
## 4. 接口入参
### 4.1 付款入参 CashierPayFormReqVO(变化后全量,仅报账款线语义)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| bizId | String(Long) | 是 | 业务单据ID(报账单ID) |
| payAccountId | String(Long) | 是 | 出账公司账户ID(fin_fund_account) |
| payMethod | String | 否 | 付款方式(字典 fin_pay_way:CASH 现金 / BANK 银行转账 / THIRD_PARTY 三方支付) |
| payChannel | String | 条件 | 付款渠道(WXPAY / ALIPAY;仅 payMethod=THIRD_PARTY 时传,不超过 20 字符) |
| amount | Number | 是 | 付款金额(大于 0,须等于单据结算金额,不一致 598610 硬拦) |
| fee | Number | 否 | 手续费(不小于 0) |
| voucherNo | String | 否 | 付款凭证号 |
| voucherUrl | String | 否 | 付款凭证影像 URL |
| payDate | Date | 是 | 付款日期(yyyy-MM-dd,可回溯补录) |
| **payeeAccountId** | **String(Long)** | **条件必填** | **新增**:已建档收款账户ID。payMethod=BANK/THIRD_PARTY 时与 newPayeeAccount **二选一必填**;CASH 时**禁传**(598612) |
| **newPayeeAccount** | **Object** | **条件必填** | **新增**:手填建档对象,与 payeeAccountId **互斥**(同传 598612),子字段见 4.2 |
#### 4.2 newPayeeAccount 子字段(手填建档)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| payeeRefId | String(Long) | 是 | 服务人员档案ID(司机=driverId,其余角色=staffId)。**强制挂档案**,不允许纯手填名称 |
| accountType | String | 是 | 账户类型:WECHAT_QR / ALIPAY_QR / BANK_CARD(对公 CORP_ACCOUNT 在报账款线被拦,见 §6) |
| qrUrl | String | 条件 | 收款码图 URL(QR 类账户必填) |
| bankAccount | String | 条件 | 银行卡号(BANK_CARD 必填) |
| bankName | String | 否 | 开户行 |
**不传 payeeName**:账户名一律以服务人员档案真名覆盖,出纳手填的名称不生效(防止同名串账)。
### 4.3 list-by-payee 查询参数
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| payeeType | String | 是 | 收款方类型:DRIVER / GUIDE / PHOTOGRAPHER / LEADER(直接取队列行 reporterPayeeType) |
| payeeRefId | Long | 是 | 关联ID(司机=driverId,其余=staffId;直接取队列行 reporterRefId) |
### 4.4 队列入参
无变化:仅分页参数 page / pageSize。
## 5. 出参字段
统一 Result 包装;分页为 PageResult(records / total / page / pageSize)。Long 主键一律以 **JSON 字符串**返回。
### 5.1 付款响应 CashierPayRespVO(变化后)
| 字段 | 类型 | 说明 |
|------|------|------|
| flowId | String(Long) | 资金流水ID |
| flowNo | String | 资金流水单号 |
| balanceAfter | Number | 出账账户付后余额 |
| bizId | String(Long) | 业务单据ID(已回写 PAID) |
| **payeeAccountId** | **String(Long),可空** | **新增**:实际落账的收款账户ID(手填建档场景返回新建ID);现金付款为 null |
| **payeeAccountLabel** | **String,可空** | **新增**:收款账户快照(类型+脱敏尾号+收款人名,如「银行卡 ****0123 巴特尔」);现金付款为 null |
### 5.2 list-by-payee 响应(PayeeAccountRowRespVO 数组)
| 字段 | 类型 | 说明 |
|------|------|------|
| id | String(Long) | 收款账户ID |
| payeeType | String | 收款方类型 |
| payeeRefId | String(Long) | 关联ID |
| payeeName | String | 收款方名称(档案真名快照) |
| accountType | String | 账户类型:WECHAT_QR / ALIPAY_QR / BANK_CARD / CORP_ACCOUNT |
| qrUrl | String | 收款码图 URL |
| bankAccount | String | 银行卡号/对公账号(展示层脱敏) |
| bankName | String | 开户行 |
| isDefault | Integer | 是否默认账户:0 否 / 1 是 |
### 5.3 队列行 ReimburseQueueRowRespVO(新增字段)
| 字段 | 类型 | 说明 |
|------|------|------|
| **bizType** | **String** | **新增**:业务对象类型(ORDER 订单 / GROUP_BATCH 团期批次) |
| **reporterAssignmentId** | **String(Long),可空** | **新增**:主报账人人员分配ID(多态口径,无主报账人为空)。**注意:这是分配行ID,不能直接当 payeeRefId 用** |
| **reporterRefId** | **String(Long),可空** | **新增**:主报账人档案ID(司机=driverId,其余=staffId),后端已按 bizType 反查好,前端直接用 |
| **reporterPayeeType** | **String,可空** | **新增**:主报账人收款方类型(DRIVER/GUIDE/PHOTOGRAPHER/LEADER;不可映射角色为空) |
| **defaultPayeeAccountId** | **String(Long),可空** | **新增**:该报账人默认收款账户ID(无默认账户为空) |
| **defaultPayeeAccountLabel** | **String,可空** | **新增**:默认账户标签(类型+脱敏尾号,如「银行卡 ****0123」;无默认账户为空) |
其余既有字段(reimburseId / bizNo / reporterName / settleAmount / status 等)无变化。
### 5.4 台账行 / 报账详情(留痕字段)
- 台账 GET /admin/finance/cashier/payments/page 行(CashierPaymentRowRespVO)新增 payeeAccountId(String 可空)+ payeeAccountLabel(String 可空),**仅 REIMBURSE 报账款行有值**,其余付款线恒 null。
- 报账详情 GET /admin/finance/reimburses/{id} 新增 payeeAccountId(String 可空)+ payeeAccountSnapshot(String 可空,类型+脱敏尾号+收款人名)。现金付款两字段为 null。
## 6. 枚举 / 数据字典
### 6.1 账户类型 accountType(PayeeAccountTypeEnum)
| 码值 | 中文 | 报账款线可用 |
|------|------|------|
| WECHAT_QR | 微信收款码 | 可用 |
| ALIPAY_QR | 支付宝收款码 | 可用 |
| BANK_CARD | 银行卡 | 可用 |
| CORP_ACCOUNT | 对公账户 | 不可用(报账款对私,手填传对公被拦 595202) |
### 6.2 收款方类型 payeeType / reporterPayeeType
DRIVER 司机 / GUIDE 导游 / PHOTOGRAPHER 摄影师 / LEADER 领队。角色映射:GUIDE_ASSISTANT(副导游)归并为 GUIDE;其余不可映射角色 reporterPayeeType 为空,付款时选账户会被归属校验拦(598614)。
### 6.3 付款方式 payMethod(字典 fin_pay_way,无变化)
CASH 现金 / BANK 银行转账 / THIRD_PARTY 三方支付。
### 6.4 账户类型与付款方式强校验(已拍板硬拦)
| payMethod | 允许的账户类型 | 错位报错 |
|---|---|---|
| BANK | BANK_CARD | 598615 |
| THIRD_PARTY | WECHAT_QR / ALIPAY_QR | 598615 |
| CASH | 不允许传任何收款账户 | 598612 |
## 7. 错误码
### 7.1 新增(出纳域 5986 段顺延)
| 码 | 常量 | 文案 | 触发场景 |
|---|---|---|---|
| 598612 | CASHIER_PAYEE_ACCOUNT_CONFLICT | 收款账户参数非法(现金付款无需收款账户;选账户与手填建档二选一) | CASH 传了 payeeAccountId/newPayeeAccount;或两者同传 |
| 598613 | CASHIER_PAYEE_ACCOUNT_REQUIRED | 该付款方式须选择或登记报账人收款账户 | BANK/THIRD_PARTY 但两者皆空 |
| 598614 | CASHIER_PAYEE_ACCOUNT_NOT_MATCH | 收款账户不属于本报账人 | 所选账户的 payeeRefId 不等于报账单主报账人档案ID |
| 598615 | CASHIER_PAYEE_ACCOUNT_TYPE_MISMATCH | 收款账户类型与付款方式不匹配(银行转账须选银行卡账户,三方支付须选收款码账户) | 如 payMethod=BANK 却选了微信收款码账户 |
### 7.2 复用(收款账户域 5952xx,手填建档链抛出)
| 码 | 说明 |
|---|---|
| 595201 | 收款账户不存在或已停用(选了已被删除/停用的账户) |
| 595202 | 账户要素组合校验失败(QR 类缺 qrUrl / BANK_CARD 缺 bankAccount / 报账款线传 CORP_ACCOUNT 等) |
| 595204 | 服务人员档案不存在或不在册(手填建档 payeeRefId 校验 fail-fast) |
### 7.3 既有(本次不变)
598602 状态冲突(重复付款)/ 598610 金额与单据应付不一致 / 598604 账户余额不足 等沿用。
## 8. 示例
### 8.1 典型成功(银行转账 + 手填建档银行卡)
请求:
```
POST /admin/finance/cashier/reimburse/pay
```
```json
{
"bizId": "88010001",
"payAccountId": "3101",
"payMethod": "BANK",
"amount": 1500.00,
"payDate": "2026-10-09",
"newPayeeAccount": {
"payeeRefId": "6601",
"accountType": "BANK_CARD",
"bankAccount": "6222021234567890123",
"bankName": "工商银行呼和浩特支行"
}
}
```
响应:
```json
{
"code": 0,
"data": {
"flowId": "990001",
"flowNo": "FL2026100912300001",
"balanceAfter": 48500.00,
"bizId": "88010001",
"payeeAccountId": "7701",
"payeeAccountLabel": "银行卡 ****0123 巴特尔"
},
"msg": ""
}
```
队列行带默认账户(付款弹窗直接预选):
```json
{
"reimburseId": "88010001",
"bizNo": "BX202610090001",
"reporterName": "巴特尔",
"bizType": "ORDER",
"reporterAssignmentId": "5501",
"reporterRefId": "6601",
"reporterPayeeType": "DRIVER",
"defaultPayeeAccountId": "7701",
"defaultPayeeAccountLabel": "银行卡 ****0123",
"settleAmount": 1500.00
}
```
list-by-payee 下拉数据源:
```
GET /admin/finance/payee-accounts/list-by-payee?payeeType=DRIVER&payeeRefId=6601
```
```json
{
"code": 0,
"data": [
{
"id": "7701",
"payeeType": "DRIVER",
"payeeRefId": "6601",
"payeeName": "巴特尔",
"accountType": "BANK_CARD",
"qrUrl": null,
"bankAccount": "****0123",
"bankName": "工商银行呼和浩特支行",
"isDefault": 1
}
]
}
```
### 8.2 边界情况
**8.2a 现金付款(不带收款账户,两新字段回 null)**:
```json
{
"bizId": "88010002",
"payAccountId": "3101",
"payMethod": "CASH",
"amount": 300.00,
"payDate": "2026-10-09"
}
```
```json
{
"code": 0,
"data": {
"flowId": "990002",
"flowNo": "FL2026100912300002",
"balanceAfter": 48200.00,
"bizId": "88010002",
"payeeAccountId": null,
"payeeAccountLabel": null
}
}
```
**8.2b 报账人无默认账户 / 角色不可映射**:队列行 defaultPayeeAccountId / defaultPayeeAccountLabel / reporterPayeeType 为 null,下拉为空数组,出纳须手填建档。
### 8.3 业务失败
**8.3a 银行转账不传收款账户(598613)**:
```json
{
"bizId": "88010001",
"payAccountId": "3101",
"payMethod": "BANK",
"amount": 1500.00,
"payDate": "2026-10-09"
}
```
```json
{ "code": 598613, "msg": "该付款方式须选择或登记报账人收款账户", "data": null }
```
**8.3b 银行转账选了微信收款码账户(598615)**:
请求体同上但改为传 `"payeeAccountId": "7702"`(7702 是该报账人的 WECHAT_QR 账户)。
```json
{ "code": 598615, "msg": "收款账户类型与付款方式不匹配(银行转账须选银行卡账户,三方支付须选收款码账户)", "data": null }
```
**8.3c 选了别的报账人的账户(598614)**:
```json
{ "code": 598614, "msg": "收款账户不属于本报账人", "data": null }
```
**8.3d 现金付款传了收款账户 / 选账户与手填同传(598612)**:
```json
{ "code": 598612, "msg": "收款账户参数非法(现金付款无需收款账户;选账户与手填建档二选一)", "data": null }
```
## 9. 业务边界
- **适用**:报账款(REIMBURSE)页签、direction=PAYABLE(付给司导)的付款单。
- **不适用**:
- 反向收款(confirm-in,司导向公司上交余款)**零改动**,不涉及收款账户。
- 其余 7 条出纳付款线不接联动,壳入参上的两个新字段传了**静默忽略**。
- **强制挂档案**:手填建档必须选服务人员档案(payeeRefId 必填),账户名=档案真名,出纳不能手填收款人姓名。
- **例外通道**:收款人不是司导本人(如家属代收)时,**不**走付款弹窗手填,先去「收款账户管理」用组织类/手填入口建档,再回到付款弹窗选择(归属校验口径:账户须挂到该报账人档案下,否则 598614)。
- **首个账户自动置默认**:手填建档的账户若是该收款方名下首个账户,自动成为默认;已有账户时不抢既有默认。
- **重复提交安全**:双击/重试不会产生重复账户(唯一索引兜底),也不会重复扣款(598602)。
## 10. 修改前后对比
### 10.1 字段级
| 位置 | 原来 | 现在 |
|------|------|------|
| 付款入参 | 只有出账账户 payAccountId | 新增 payeeAccountId / newPayeeAccount(BANK/THIRD_PARTY 二选一必填,CASH 禁传) |
| 付款响应 | flowId/flowNo/balanceAfter/bizId | 新增 payeeAccountId / payeeAccountLabel(CASH 为 null) |
| 队列行 | 无报账人档案/账户信息 | 新增 bizType / reporterAssignmentId / reporterRefId / reporterPayeeType / defaultPayeeAccountId / defaultPayeeAccountLabel |
| 台账行 | 无收款账户留痕 | REIMBURSE 行回填 payeeAccountId / payeeAccountLabel,其余线 null |
| 报账详情 | 无收款账户留痕 | 新增 payeeAccountId / payeeAccountSnapshot |
| 选账户下拉数据源 | 不存在 | 新增 GET /admin/finance/payee-accounts/list-by-payee |
### 10.2 行为级
| 项 | 原来 | 现在 |
|----|------|------|
| BANK/THIRD_PARTY 付款 | 只填出账账户即可 | **必须**选收款账户或手填建档,否则 598613 |
| CASH 付款 | 不变 | 传收款账户参数会被 598612 拦(前端应禁用控件) |
| 账户与付款方式 | 无校验 | 错位强拦 598615(银行转账须银行卡,三方须收款码) |
| 账户归属 | 无校验 | 只能选/建本报账人名下账户,否则 598614 |
## 11. 影响评估 / 回滚
- **破坏性**:**有(行为级)**。报账款 BANK/THIRD_PARTY 付款从「不填收款账户也能付」变为「必填」,前端付款弹窗**必须同步改造**(加选账户下拉 + 手填建档表单 + CASH 时禁用),否则出纳付款会被 598613 全量拦截。
- **前端同步上线**:**要求**。后端部署后旧版前端的报账款银行/三方付款会全部报 598613,前端改版须与后端部署同步或紧随。
- **回滚方案**:后端回退到 #8810 之前版本即可恢复旧行为;前端新版代码对旧后端兼容(新字段不传即走旧链路)。DB 新增两列可空,保留无害;fin_payee_account 唯一索引不影响既有数据。
## 12. 注意事项
1. **金额/ID 一律字符串**:所有 Long ID(bizId / payeeAccountId / payeeRefId / defaultPayeeAccountId 等)JSON 序列化为字符串,前端按 string 处理。
2. **CASH 禁用收款账户控件**:payMethod 切到 CASH 时隐藏/禁用选账户与手填入口,并清空已填值,避免 598612。
3. **下拉数据源用队列行字段**:打开付款弹窗时直接拿 reporterPayeeType + reporterRefId 调 list-by-payee,**不要**用 reporterAssignmentId(那是分配行ID,口径不同)。
4. **默认账户预选**:队列行 defaultPayeeAccountId 非空时直接预选,出纳可改选或改手填。
5. **新字段全部可空**:存量报账单、现金付款、其余付款线场景下 payeeAccountId/Label 均为 null,渲染一律判空。
6. **部署时序**:依赖 Flyway 迁移 V20261009_136__fin_reimburse_payee_account_link.sql(fin_reimburse 加两列 + fin_payee_account 加唯一索引),order-v3 部署时自动执行;**未部署前新字段不生效(598613 不会触发,行为同旧版)**。
7. **手填建档幂等**:同一报账人同一账户要素重复提交不会重复建档,返回已有账户ID,前端无需防重逻辑(但仍建议提交后禁用按钮)。
## 13. 关联 / 联系人
- Issue:https://git.1814.love/wx/HL/issues/8809
- PR:https://git.1814.love/wx/HL/pulls/8810
- Commit:https://git.1814.love/wx/HL/commit/e50a43b72a68e93b993154d1bb45ad4aa185d92b
- 关联:#8679(收款账户关联服务人员档案基建)
- 后端负责人:腰苏图(yst)
@@ -0,0 +1,259 @@
---
schema: "hl-changelog/v2"
ticket: "8815"
title: "账户流水与微信商户对账双向查询关联(对账↔流水互查)"
consumer: "admin"
author: "yst"
change_type: "新增接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "be239b050946d54d74234a77fe1d7470c95c5c86"
target_release: "v2.1"
verified_at: "2026-10-10"
status_note: "后端 PR #8816 已合并 dev-v3(squash aad53d1fa3)。A 方案纯只读查询,零写、无 DDL、不碰资金;新增 2 个互查接口(对账→流水、流水→对账),部署 order-v3 后可用。 / 前端已交付:对账抽屉加「关联流水」(复用 FlowDetailDrawer,null 显无关联流水),流水页加「关联微信账单」(LinkedWxBillsModal 多条列表,空数组显无关联微信账单);checkpoint 全量过"
updated_at: "2026-10-10"
base: "dev-v3"
---
# 账户流水与微信商户对账双向查询关联
> 面向:管理后台前端(hl-admin)
> 日期:2026-10-09 | 后端:order-v3,PR 已合并 dev-v3
> 关联契约(同仓):`changelogs-v2/2026-10/01_8690_微信商户对账-新增接口-管理后台.md`(本篇字段表自包含)
## 1. 接口背景
微信商户对账页(#8690)能看微信侧逐笔账单,账户流水页能看我方收款流水,但两边此前**互相不通**——对账发现差异时,财务要手工拿商户单号去流水里搜,或在流水里看到一笔收款想查微信侧原始账单也得手工翻。本次新增 **2 个只读查询接口**,实现双向跳转核对:
- **对账 → 流水**:在对账页的账单记录上,一键查我方是否已记收款流水(接口 1)。
- **流水 → 对账**:在账户流水页的流水行上,一键查微信侧的原始账单记录(接口 2)。
关联链路:`wx_bill_record.out_trade_no → payment_transaction → transaction_id → fin_fund_flow.biz_id`(仅 biz_type=ORDER_PAY 的订单支付流水能关联),反向同理。两接口**纯只读**,零写、无 DDL、不碰资金。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 对账→流水:按账单记录查我方收款流水 | GET | /v3/admin/payment/wx-bill/records/{billRecordId}/fund-flow | 新增接口 | 单值对象,无关联时 data=null |
| 2 | 流水→对账:按账户流水查微信侧账单记录 | GET | /admin/finance/fund-flows/{flowId}/wx-bill-records | 新增接口 | 列表(容错退款/部分支付多笔),无关联返回空数组 |
## 3. 接口详情
### 3.1 对账→流水 GET /v3/admin/payment/wx-bill/records/{billRecordId}/fund-flow
- **使用场景**:对账页逐笔账单记录行的「关联流水」操作,查我方账户是否已针对该笔微信收款记账。
- **认证**:需管理后台 JWT。
- **幂等性**:只读。
- **限流**:无。
### 3.2 流水→对账 GET /admin/finance/fund-flows/{flowId}/wx-bill-records
- **使用场景**:账户流水页的流水行「关联微信账单」操作,查该笔收款对应的微信侧原始账单记录(可能多笔:退款/部分支付场景)。
- **认证**:需管理后台 JWT。
- **幂等性**:只读。
- **限流**:无。
## 4. 接口入参
### 4.1 接口 1(对账→流水)
| 字段 | 位置 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|------|----------|
| billRecordId | 路径 | Long | 是 | 微信账单记录ID(wx_bill_record 主键) | 不存在则报 582407 |
### 4.2 接口 2(流水→对账)
| 字段 | 位置 | 类型 | 必填 | 说明 | 校验规则 |
|------|------|------|------|------|----------|
| flowId | 路径 | Long | 是 | 账户流水ID(fin_fund_flow 主键) | — |
两接口均无 Query 参数、无请求体。
## 5. 出参字段
统一 `Result` 包装。Long 主键以 **JSON 字符串**返回。
### 5.1 接口 1 出参 WxBillRecordFundFlowRespVO(单值对象,无关联时 data=null)
| 字段 | 类型 | 说明 |
|------|------|------|
| flowId | String(Long) | 账户流水ID |
| flowNo | String | 流水号 |
| amount | BigDecimal | 金额(元) |
| fundAccountId | String(Long) | 资金账户ID |
| fundAccountName | String | 资金账户名称 |
| direction | String | 收支方向(如 IN 收 / OUT 支) |
| flowAt | DateTime | 流水发生时间 |
| bizType | String | 业务类型(关联场景为 ORDER_PAY) |
| orderNo | String | 关联订单号 |
**无关联时 `data` 为 null**(两类情况:billType=FUND_FLOW 的资金账单没有此关联;或微信有该笔账单但我方尚未记账)。前端据此显示「无关联流水」。
### 5.2 接口 2 出参 FundFlowWxBillRecordRespVO(列表,无关联时 data=[])
| 字段 | 类型 | 说明 |
|------|------|------|
| billRecordId | String(Long) | 微信账单记录ID |
| mchId | String | 微信商户号 |
| billDate | Date | 账单日期 |
| billType | String | 账单类型:TRADE / FUND_FLOW |
| wxTransactionId | String | 微信支付单号 |
| outTradeNo | String | 商户单号 |
| tradeTime | DateTime | 交易时间 |
| tradeType | String | 交易类型(如 JSAPI) |
| tradeState | String | 交易状态(如 SUCCESS) |
| amount | BigDecimal | 交易金额(元) |
| appid | String,可空 | 微信公众账号ID(小程序 wx 前缀 / 企业微信 ww 前缀) |
**返回列表**:退款、部分支付等场景同一笔流水可能对应微信侧多笔账单记录,故用数组。仅 **biz_type=ORDER_PAY 且有关联交易**的流水才有数据;其他业务类型流水(如调账、提现等)返回空数组 `[]`。
## 6. 枚举 / 数据字典
- **本次无枚举/字典新增**。沿用对账模块既有值:
- `billType`:TRADE(交易账单)/ FUND_FLOW(资金账单)
- `direction`:IN(收)/ OUT(支)
- `bizType`:关联场景为 ORDER_PAY(订单支付)
## 7. 错误码
| 错误码 | 触发场景 | 说明 |
|--------|----------|------|
| 582407 | 接口 1 的 billRecordId 非法/不存在 | 账单记录不存在 |
接口 2 无新增业务错误码;flowId 不存在等场景走统一响应。
## 8. 示例
### 8.1 接口 1 典型成功(交易账单已关联我方流水)
```
GET /v3/admin/payment/wx-bill/records/1976123456789012345/fund-flow
```
```json
{
"code": 0,
"data": {
"flowId": "1977000111222333444",
"flowNo": "LS20261008120001",
"amount": 1280.00,
"fundAccountId": "1976000000000000001",
"fundAccountName": "微信商户-主账户",
"direction": "IN",
"flowAt": "2026-10-08 12:00:05",
"bizType": "ORDER_PAY",
"orderNo": "HL20261008120000123456"
}
}
```
### 8.2 接口 1 边界情况(无关联,data=null)
资金账单(FUND_FLOW)或微信有账单但我方未记账时:
```json
{
"code": 0,
"data": null
}
```
前端显示「无关联流水」。**注意 code 仍是 0**,判空看 data 是否为 null。
### 8.3 接口 1 业务失败(billRecordId 不存在)
```
GET /v3/admin/payment/wx-bill/records/9999999999999999999/fund-flow
```
```json
{
"code": 582407,
"msg": "账单记录不存在",
"data": null
}
```
### 8.4 接口 2 典型成功(流水关联到微信交易账单)
```
GET /admin/finance/fund-flows/1977000111222333444/wx-bill-records
```
```json
{
"code": 0,
"data": [
{
"billRecordId": "1976123456789012345",
"mchId": "1600000001",
"billDate": "2026-10-08",
"billType": "TRADE",
"wxTransactionId": "4200001234202610081234567890",
"outTradeNo": "HL20261008120000123456",
"tradeTime": "2026-10-08 12:00:01",
"tradeType": "JSAPI",
"tradeState": "SUCCESS",
"amount": 1280.00,
"appid": "wx4711a76772deff36"
}
]
}
```
### 8.5 接口 2 边界情况(无关联,返回空数组)
非 ORDER_PAY 类型的流水(如调账流水),或该流水无关联交易:
```
GET /admin/finance/fund-flows/1977000999888777666/wx-bill-records
```
```json
{
"code": 0,
"data": []
}
```
**注意 code 仍是 0**,判空看数组长度,前端显示「无关联微信账单」。
### 8.6 接口 2 多笔关联(退款/部分支付场景)
同一笔流水可能对应微信侧多笔账单记录,列表会有多条元素,前端按列表渲染即可,勿假设只有一条。
## 9. 业务边界
- **适用**:对账页账单记录 ↔ 账户流水页流水行的互相跳转核对。
- **不适用**:不用于触发任何资金/数据改动——两接口均为纯只读查询,不产生对账处理、不改流水状态。
- **关联范围有限**:只有 biz_type=ORDER_PAY(订单支付)且经由 payment_transaction 关联的流水才能查到微信侧账单;其他业务类型流水(调账、提现、盘盈亏等)接口 2 恒返回空数组。
- **资金账单无反向关联**:billType=FUND_FLOW 的微信账单记录调用接口 1 恒返回 data=null。
## 10. 修改前后对比
新增接口类,跳过(无旧版本对比)。
## 11. 影响评估 / 回滚
新增接口类,跳过。补充说明:
- **破坏性**:无。纯新增 2 个只读端点,不改任何既有接口的入参/出参/枚举。
- **前端同步上线**:不要求。未接期间对账页/流水页维持现状即可。
- **回滚方案**:order-v3 回退到 #8816 之前版本即可,两新端点 404;前端按可空/兜底处理即可,无需配合回滚。
## 12. 注意事项
1. **两个「无关联」都是成功响应**:接口 1 无关联返回 `code:0, data:null`;接口 2 无关联返回 `code:0, data:[]`。不要当成错误处理,也不要把 582407(记录不存在)与「无关联」混淆——前者是入参非法,后者是合法但无关联数据。
2. **接口 2 是列表**:即使常见场景只有一条,也必须按数组渲染(退款/部分支付会多条)。
3. **Long 主键字符串化**:flowId / fundAccountId / billRecordId 均为 JSON 字符串,勿当 number 处理。
4. **路径前缀不一致是有意的**:接口 1 在对账域 `/v3/admin/payment/wx-bill/*` 下,接口 2 在财务域 `/admin/finance/fund-flows/*` 下(两域各自的既有前缀),前端网关路由按各自前缀走。
5. 部署时序:需 order-v3 部署到本版本后两端点可用,未部署前调用会 404。
## 13. 关联 / 联系人
- Issue:https://git.1814.love/wx/HL/issues/8815
- PR:https://git.1814.love/wx/HL/pulls/8816
- Commit:https://git.1814.love/wx/HL/commit/aad53d1fa3
- 后端负责人:腰苏图(yst)
@@ -0,0 +1,305 @@
---
schema: "hl-changelog/v2"
ticket: "8813"
title: "团期核单明细带出/暂存合并:已暂存 tab 也持续带出未暂存预览行,新增 bizKey 溯源"
consumer: "admin"
author: "yst"
change_type: "修改接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "b09fb93a03a60f35acbb7e707b519cb888b65e90"
target_release: ""
verified_at: "2026-10-10"
status_note: "后端 PR #8817 已合并 dev-v3(squash 4734715671),含 Flyway 迁移 V20261009_747(8 张 order_group_settlement_* 明细表加 biz_key 列),需部署 order-v3 后生效;前端必须同步改造:进入 tab 整份编辑 lines 数组(含 lineId=null 待带出行),暂存时原样透传 sourceType/bizKey,否则已暂存 tab 会出现旧暂存行与重新带出行双显双计 / 预览行整份同权编辑+底色标识,sourceType/bizKey 纯透传,PUT 回执 warnMessage toast,589756 重读全量;checkpoint 全量+生产构建过"
updated_at: "2026-10-10"
base: "dev-v3"
---
# 团期核单明细带出/暂存合并 + bizKey 溯源(管理后台)
> ⚠️ **修改接口(前端必须同步上线)**:团期核单 8 类 tab 的「带出」语义从「暂存过的 tab 不再带出」改为「**始终合并返回**」——已暂存行照原样显示、未暂存的带出预览行继续带入(lineId=null)。前端交互改为「整份 lines 编辑 + 暂存时透传 sourceType/bizKey」。**新旧前端混跑会出现双显双计**,务必同步上线。
## 1. 接口背景
团期核单 8 类费用 tab(住宿/门票/餐食/车辆/导游/摄影师/其他支出/其他收入)里,大量明细行是系统从订单、资源准备、批次成本里**自动带出**的预览行(如各订单的房费、门票费、团车公摊),运营只需确认或微调后暂存。
旧行为的问题:一旦某个 tab 暂存过,系统就**不再带出**该 tab 的后续预览行——后续新报名的订单费用、新产生的批次成本就「消失」了,运营完全感知不到漏了钱。
本次(#8813)改为:
- **带出与暂存合并**:无论何时进入 tab,已暂存的行照常显示;**尚未被暂存覆盖的带出预览行继续带入**(lineId=null、带 sourceType/bizKey 标识),运营一眼看到「还有 N 条带出成本没纳入」。
- **bizKey 溯源**:每条行新增 bizKey(来源业务键),暂存时原样回传,后端据此判断「这条暂存行对应哪条带出成本」,已暂存的预览行不再重复带出,防止重复计入。
- **漏带提醒**:暂存时如果漏掉了某些带出行,响应里给 warnMessage / missedCarryOverCount 做 WARN 提示(不阻断)。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 分类 tab 明细查询 | GET | /v3/admin/order/group-batch/{groupBatchId}/settlement/{category} | ⚠️ 行为变化 | **暂存过的 tab 也持续带出未暂存预览行**;行出参加 bizKey;sourceType 正确区分 CARRY_OVER/BATCH_COST/MANUAL |
| 2 | 整 tab 暂存 | PUT | /v3/admin/order/group-batch/{groupBatchId}/settlement/{category} | ⚠️ 行为变化 | 入参行可透传 sourceType/bizKey;响应新增 warnMessage/missedCarryOverCount;新增硬拦错误码 589756/589757 |
| 3 | 新增单行 | POST | /v3/admin/order/group-batch/{groupBatchId}/settlement/{category}/lines | 修改 | 入参同样可透传 sourceType/bizKey(接受待带出行时使用),同 589756 校验 |
category 8 值:hotels / activities / meals / vehicles / guide-fees / photographer-fees / other-expenses / other-incomes。
DDL:8 张 order_group_settlement_* 明细表加 biz_key 列(迁移 V20261009_747),Flyway 部署时自动执行,**前端无需任何动作**。
## 3. 接口详情
| 项 | 说明 |
|---|---|
| 服务 | hl-order-service-v3(端口 8086) |
| 使用场景 | 管理后台 → 团期详情 → 核单 Tab:8 类费用明细查看/编辑/暂存 |
| 认证 | 管理后台登录态(JWT);网关既有路由 /v3/admin/**,无新增网关配置 |
| 权限码 | 沿用核单既有权限:group-batch:audit:view(GET)、group-batch:audit:edit(PUT / lines 新增) |
| 幂等性 | PUT 整 tab 暂存仍走 main 主行锁 + expectedVersion CAS(过期仍吃 589573);新增单行天然幂等(无去重键) |
| 限流 | 无特殊限流 |
## 4. 接口入参
### 4.1 路径参数(三个端点共有)
| 参数 | 位置 | 类型 | 必填 | 说明 |
|------|------|------|------|------|
| groupBatchId | Path | Long | 是 | 团期 ID |
| category | Path | String | 是 | 费用类别 8 值之一(见 §6) |
### 4.2 PUT 整 tab 暂存请求体(GroupSettleTabSaveReqVO,仅列变化点)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| expectedVersion | Number(int) | 是 | 乐观锁版本号,取 GET 响应里的 version;过期返 589573 |
| lines | Array | 是 | **整份明细行数组(全量替换语义)**,行结构见 4.3 |
### 4.3 行结构 GroupSettleLineSaveReqVO(PUT 与 POST lines 共用)
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| lineId | String(Long) | 否 | 已暂存行带原 lineId;**带出预览行(GET 里 lineId=null 的行)暂存时不传**,由后端建档 |
| itemName / spec / quantity / unitPrice / totalAmount / remark 等业务字段 | — | 按原契约 | 无变化,沿用 #8714 契约 |
| **sourceType** | String | 否 | **新增可透传**:带出预览行暂存时**原样回传** GET 给的值(CARRY_OVER/BATCH_COST);手加行**不传**(后端默认 MANUAL) |
| **bizKey** | String | 否 | **新增可透传**:带出预览行暂存时**原样回传** GET 给的 bizKey;手加行不传 |
> **铁律**:sourceType/bizKey 前端**不解析、不构造、不修改**,只做「GET 拿到什么 → PUT 原样回传什么」的透传。
## 5. 出参字段
### 5.1 GET 分类 tab 明细(GroupSettleTabRespVO,仅列变化点)
| 字段 | 类型 | 说明 |
|------|------|------|
| status / version / editable / summary 等 | — | 无变化;editable 仍 = status==DRAFT |
| lines[] | Array | **行为变化**:现在 = 已暂存行 ∪ 未暂存带出预览行(合并返回,不再互斥) |
| lines[].lineId | String(Long) 或 null | 已暂存行=实际行 ID;**带出预览行=null**(前端据此区分并可渲染「待带出」样式) |
| lines[].sourceType | String | **现在会正确区分**:CARRY_OVER(订单带出)/ BATCH_COST(批次成本带出)/ MANUAL(手加) |
| **lines[].bizKey** | **String 或 null** | **新增**:来源业务键。带出行有值,手加行=null。**前端不解析**,仅透传 |
金额与 ID 字段一律以 **JSON 字符串**返回(防 JS 精度丢失)。
bizKey 格式参考(仅供理解,**不需要解析**):
- 单价型:`D1|驯鹿拉车体验|成人票|59`(slot|名称|规格|单价)
- 总额型:`TOTAL:VEHICLE:SHARED:BUS` / `TOTAL:{类别}:L{序号}`
### 5.2 PUT 暂存响应(GroupSettleTabWriteRespVO,仅列变化点)
| 字段 | 类型 | 说明 |
|------|------|------|
| version / lineIds 等既有字段 | — | 无变化 |
| **warnMessage** | **String 或 null** | **新增**:暂存漏了某些带出行时的提示文案,如「有 2 条带出成本未纳入本次暂存」;无遗漏时为 null |
| **missedCarryOverCount** | **Number(int)** | **新增**:漏带的带出行条数;0 = 无遗漏 |
> WARN 只是提醒,**暂存本身成功**(HTTP 200)。前端建议 toast 展示 warnMessage,不打断流程。
## 6. 枚举 / 数据字典
### 6.1 category(路径段,8 值)
hotels(住宿)/ activities(门票·游玩)/ meals(餐食)/ vehicles(车辆)/ guide-fees(导游)/ photographer-fees(摄影师)/ other-expenses(其他支出)/ other-incomes(其他收入)。中文名走数据字典 settlement_category。
### 6.2 sourceType(行来源,3 值)
| 值 | 含义 | 出现场景 |
|---|---|---|
| CARRY_OVER | 订单带出(从各子订单费用明细带出) | 带出预览行 / 由带出行暂存落库的行 |
| BATCH_COST | 批次成本带出(团级公摊成本,如团车) | 同上 |
| MANUAL | 手加行 | 前端手工新增的行(默认,不传即 MANUAL) |
## 7. 错误码
| 错误码 | 触发场景 | 前端处理 |
|---|---|---|
| **589756** | **明细行来源标识与本团带出成本不匹配**:提交的 sourceType/bizKey 不在本 tab 当前带出预览集内(如过期预览行、伪造 bizKey) | 硬拦。提示用户刷新 tab 重新加载最新带出集后再暂存 |
| **589757** | **存在重复的来源成本行**:同一次提交里出现两条相同 bizKey 的行 | 硬拦。前端去重逻辑自查(正常整份编辑不会触发) |
| 589573 | expectedVersion 过期(并发暂存冲突) | 沿用原处理:刷新重载 |
## 8. 示例
### 8.1 典型:tab 已暂存过,新带出预览行继续带入
GET /v3/admin/order/group-batch/1024/settlement/hotels
```json
{
"code": 0,
"data": {
"status": "DRAFT",
"editable": true,
"version": 3,
"lines": [
{
"lineId": "88001",
"sourceType": "CARRY_OVER",
"bizKey": "D1|驯鹿拉车体验|成人票|59",
"itemName": "驯鹿拉车体验-成人票",
"quantity": "10",
"unitPrice": "59.00",
"totalAmount": "590.00"
},
{
"lineId": "88002",
"sourceType": "MANUAL",
"bizKey": null,
"itemName": "临时加床费",
"quantity": "1",
"unitPrice": "200.00",
"totalAmount": "200.00"
},
{
"lineId": null,
"sourceType": "CARRY_OVER",
"bizKey": "D2|星空蒙古包|标间|480",
"itemName": "星空蒙古包-标间",
"quantity": "5",
"unitPrice": "480.00",
"totalAmount": "2400.00"
}
]
}
}
```
> 第 1 条是已暂存的带出行(有 lineId + bizKey);第 2 条是手加行(bizKey=null);第 3 条是**新带出的预览行**(lineId=null),等待运营确认。
### 8.2 边界:暂存整份提交,带出行透传 sourceType/bizKey,漏带 1 条给 WARN
PUT /v3/admin/order/group-batch/1024/settlement/hotels
```json
{
"expectedVersion": 3,
"lines": [
{
"lineId": "88001",
"sourceType": "CARRY_OVER",
"bizKey": "D1|驯鹿拉车体验|成人票|59",
"itemName": "驯鹿拉车体验-成人票",
"quantity": "10",
"unitPrice": "59.00",
"totalAmount": "590.00"
},
{
"lineId": "88002",
"itemName": "临时加床费",
"quantity": "1",
"unitPrice": "200.00",
"totalAmount": "200.00"
}
]
}
```
响应(漏了 8.1 里那条 lineId=null 的带出行,**暂存仍成功**):
```json
{
"code": 0,
"data": {
"version": 4,
"warnMessage": "有 1 条带出成本未纳入本次暂存",
"missedCarryOverCount": 1
}
}
```
> 手加行(第 2 条)不传 sourceType/bizKey;带出行(第 1 条)原样透传。下次再 GET,那条漏掉的预览行仍会带出(lineId=null),不会丢。
### 8.3 业务失败:bizKey 不在带出集触发 589756
暂存时提交了一条 bizKey=D9|不存在的项目|票|1(不在本 tab 当前带出预览集):
```json
{
"code": 589756,
"msg": "明细行来源标识与本团带出成本不匹配"
}
```
## 9. 业务边界
**适用**:
- 核单 DRAFT 状态下的 8 类 tab 查看/编辑/暂存/单行新增。
- 「暂存过但仍想看到新带出成本」的场景——本次核心。
**不适用**:
- 核单 CONFIRMED 后:editable=false,带出行不再带入(tab 已锁定),写口全部被既有门禁拦截。
- 前端自行拼装 bizKey:bizKey 由后端生成,前端伪造会吃 589756。
**特殊边界**:
- 带出预览行**不落库**,只有暂存后才成为正式行;所以 GET 里 lineId=null 的行在「刷新页面」后可能因业务数据变化而增减,属正常。
- 同 tab 多次暂存:已暂存的带出行(bizKey 已落库)不会重复带出。
## 10. 修改前后对比
### 行为级
| 场景 | 修改前 | 修改后 |
|---|---|---|
| tab 已暂存过,再次进入 | **不再带出任何预览行**,新产生的订单费用/批次成本不可见 | **已暂存行 + 未暂存带出预览行合并返回**,新成本持续可见 |
| 带出预览行标识 | sourceType 区分不可靠 | sourceType 正确区分 CARRY_OVER/BATCH_COST/MANUAL,且带 **bizKey** 溯源键 |
| 暂存漏掉带出行 | 静默漏掉,运营无感知 | 响应给 warnMessage + missedCarryOverCount(WARN 不阻断) |
| 伪造/过期 bizKey | 无校验 | 589756 硬拦 |
| 同次提交 bizKey 重复 | 无校验 | 589757 硬拦 |
### 字段级
| 字段 | 修改前 | 修改后 |
|---|---|---|
| GET 出参 lines[].bizKey | 无 | **新增**(String,带出行有值,手加行 null) |
| PUT/POST 入参 lines[].sourceType / bizKey | 不接收 | **新增可透传**(带出预览行暂存时原样回传) |
| PUT 响应 warnMessage / missedCarryOverCount | 无 | **新增** |
## 11. 影响评估 / 回滚
**兼容性**:⚠️ **前端必须同步上线,属破坏性行为变化**。
- 新后端 + 旧前端:已暂存 tab 会同时出现「旧暂存行(bizKey=null)+ 重新带出的同类预览行」,**双显双计**,金额虚高。
- 新前端 + 旧后端:bizKey 无处可取,退化为旧行为(可接受但不解决问题)。
**前端改造要点**:
1. 进入 tab 拿到的**完整 lines 数组(含 lineId=null 待带出行)整份编辑**,不再区分「带出区/暂存区」两个数据源。
2. 暂存时把带出预览行的 sourceType/bizKey **原样透传**回去,整份提交。
3. 渲染上可用 lineId==null 标「待带出」样式;用 missedCarryOverCount>0 toast warnMessage。
4. 收到 589756 提示刷新重载(多半是预览集已变)。
**回滚方案**:后端回滚本 PR 即恢复旧行为(biz_key 列保留无害);前端若已上线透传逻辑,旧后端会忽略多余字段,不报错。建议前后端同窗口发布。
## 12. 注意事项
- bizKey 前端**不解析、不构造、不修改**,纯透传。格式见 §5.1 仅作理解参考。
- 金额/ID 一律字符串处理。
- editable 判定未变:仍 = status==DRAFT。
- 部署依赖:Flyway 迁移 V20261009_747 随 order-v3 部署自动执行,**联调前先确认测试服已部署本 PR 之后的构建**。
## 13. 关联 / 联系人
- Issue: https://git.1814.love/wx/HL/issues/8813
- PR: https://git.1814.love/wx/HL/pulls/8817
- Commit: https://git.1814.love/wx/HL/commit/4734715671
- 后端负责人:yst(腰苏图)
- 前置契约:#8714 团期核单重做 8 类 tab(本仓 changelogs-v2/2026-10/04_8714_团期核单重做8类tab明细+公摊拆账-修改接口-管理后台.md)
@@ -0,0 +1,349 @@
---
schema: "hl-changelog/v2"
ticket: "8818"
title: "团期核单酒店候选查询(无 orderId,住宿行选酒店带协议价/结算价/库存)"
consumer: "admin"
author: "yst"
change_type: "新增接口"
backend_status: "merged"
gateway_status: "not_required"
frontend_status: "implemented"
frontend_owner: "hl-admin(claude)"
frontend_ref: "e519d5ccb57b4e1c2cb4e91062ca030f797d74f4"
target_release: "v2.1"
verified_at: "2026-10-10"
status_note: "后端 PR #8819 已合并 dev-v3(squash 09f9dc8c4c),并已部署测试服、网关实调验证新端点生效(401 鉴权,非 404)。无 DDL、不动现有 /v3/admin/hotel-candidates 配房接口。 / 住宿 tab 酒店/房型下拉有 stayDate 走 #8818 候选口,选房型按当日协议价带出单价,满房/未开放选项标状态;无日期回退管理域读口;日期后补单价为空白愈回填 / aaae21f86 住宿 tab 酒店/房型有 stayDate 走候选口带协议价;e519d5ccb 对齐 §12.1:付款方式走字典 settlement_payment_method+paymentMethodName 优先+settleType 映射默认推荐 / aaae21f86 住宿 tab 酒店/房型有 stayDate 走候选口带协议价;e519d5ccb 对齐 §12.1:付款方式走字典 settlement_payment_method+paymentMethodName 优先+settleType 映射默认推荐(更正 ref 为真实全 hash)"
updated_at: "2026-10-10"
base: "dev-v3"
---
# 团期核单酒店候选查询(无 orderId)
> 面向:管理后台前端(hl-admin)
> 日期:2026-10-10 | 后端:hl-order-service-v3(端口 8086)
> 范围:**纯新增**一个酒店候选查询接口,供团期核单「住宿(HOTEL)」明细行选酒店/房型下拉使用。
## 1. 接口背景
团期核单录入住宿明细行时,前端需要「选酒店 / 选房型的下拉候选」,选中后带出酒店名、房型名、协议价(作为单价)等快照值填进核单行。
现有的酒店候选接口 `GET /v3/admin/hotel-candidates` **必须传 orderId**,它服务的是「给某个订单的某一天配房」场景,返回里带产品池徽章、定制师点名、已配回显、快速配房等配房专属逻辑,且日期可由订单推算。核单站在**团 / 资源视角**选房,**没有也不该伪造 orderId**,无法复用该接口。
因此新增一个**无需 orderId** 的核单酒店候选接口:只按「入住日期 + 城市/关键词 + 需要房数」查在售酒店及当日房型价格库存,数据与配房候选同源(同一资源价格日历),但**不含任何配房个性化字段**。
## 2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 团期核单酒店候选查询 | GET | `/v3/admin/order/group-batch/hotel-candidates` | 新增 | 无 orderId,按入住日查在售酒店 + 当日房型价格库存 |
## 3. 接口详情
### 3.1 团期核单酒店候选查询
- **使用场景**:团期核单录入住宿(HOTEL)明细行时,前端弹层/下拉加载可选酒店列表,选中某酒店某房型后取快照值(酒店名、房型名、协议价作单价)填行。
- **认证**:需要管理后台 JWT(`/v3/admin/*` 走网关鉴权),需具备团期查看权限(`group-batch:view`)。
- **幂等性**:是(只读查询,无副作用)。
- **限流**:沿用网关统一限流,无单独 QPS 限制。
- **HTTP 方法 / 路径**:`GET /v3/admin/order/group-batch/hotel-candidates`
- **请求方式**:Query 参数,无请求体。
## 4. 接口入参
### 4.1 Query 参数
| 字段 | 类型 | 必填 | 说明 |
|------|------|------|------|
| `stayDate` | String(yyyy-MM-dd) | ✅ 是 | 入住日期。**价格 / 库存都按这一天取**(当天酒店价格日历)。缺参返回 400。 |
| `city` | String | 否 | 城市名(前缀模糊,如「呼伦贝尔」命中「呼伦贝尔市」)。**空 = 查全部在售酒店,不限城市**。 |
| `keyword` | String | 否 | 关键词搜索。**非空时突破单城限制**,跨城/省按关键词匹配酒店名/城市/省份/地址,此时 `city` 被忽略可不传。 |
| `hotelIds` | Long[] | 否 | 酒店 ID 白名单(逗号分隔多个)。传入则只查这批酒店(如限定某个池子内的酒店);不传 = 不限定。 |
| `roomCount` | Integer | 否 | 需要的房间数(≥1,默认 1)。用于过滤「够房」的房型;不传按 1 间处理。 |
| `roomCategory` | String | 否 | 房型大类(字典 `room_category` 的 value,如 STANDARD/TWIN/KING/SUITE/FAMILY)。传入后只在同大类房型内选取匹配项;不传不限大类。 |
| `page` | Integer | 否 | 页码,从 1 开始,默认 1。 |
| `pageSize` | Integer | 否 | 每页条数,默认 50,**最大 200**(超过按 200 计)。 |
> `city` 与 `keyword` 二选一生效:`keyword` 非空时走跨地区关键词搜(忽略 `city`);`keyword` 为空时按 `city` 筛(`city` 也空 = 全部在售)。
## 5. 出参(响应)
响应为统一分页包装 `PageResult`,结构:`{ code, message, success, data: { list: [...], total, pageNo, pageSize } }`。
`data.list` 每个元素为一家酒店候选,字段如下。
### 5.1 酒店候选字段(`data.list[]`)
| 字段 | 类型 | 说明 |
|------|------|------|
| `hotelId` | String | 酒店 ID(Long 序列化为 String,防 JS 精度丢失) |
| `hotelName` | String | 酒店名 |
| `starLevel` | String | 星级(如 FIVE_STAR/FOUR_STAR,字典值) |
| `city` | String | 城市 |
| `district` | String | 区/县 |
| `address` | String | 详细地址 |
| `hotelType` | String | 住宿形态(字典 `hotel_type`,见 §6.1) |
| `tags` | String[] | 运营标签名列表(如 ["协议酒店","近景区"];无标签为空数组) |
| `contactPerson` | String | 联系人姓名 |
| `contactWechat` | String | 联系人微信号 |
| `settleType` | String | 结算类型(字典 `resource_settle_type`,见 §6.2) |
| `coverMaterialId` | String | 封面素材 ID(前端可取封面图;无则为 null) |
| `protoPrice` | String | 酒店代表价(协议价,元/间·晚)= 当日**可订**房型里最低协议价;当天无可订协议价为 null。金额 BigDecimal 序列化为 String。 |
| `protoPriceRoomTypeId` | String | 产生 `protoPrice` 的那个房型 ID;无有效可售协议价为 null |
| `settlementPrice` | String | 与 `protoPriceRoomTypeId` 同一房型、同一天的结算价;未维护为 null |
| `roomTypes` | Object[] | 该酒店**当日房型列表**(含每房型库存/价格,见 §5.2);无房型数据为空数组 |
### 5.2 房型字段(`data.list[].roomTypes[]`)
| 字段 | 类型 | 说明 |
|------|------|------|
| `roomTypeId` | String | 房型 ID |
| `name` | String | 房型名称(如「豪华大床房」)——**核单行 `roomTypeName` 快照取这里** |
| `description` | String | 房型描述 |
| `maxOccupancy` | Integer | 最大入住人数 |
| `bedType` | String | 床型(如 KING/TWIN) |
| `roomCategory` | String | 房型大类(字典 `room_category` 的 value,如 TWIN/KING) |
| `stock` | Integer | 当前可用房(库存);无当日价格日历记录为 null |
| `stockUsed` | Integer | 已占用数;无记录为 null(仅记录,不参与 available 计算) |
| `available` | Integer | 当日可订房数;`unlimited=true`(不限库存)时为 null(语义「不限」而非具体数);无记录为 null |
| `unlimited` | Boolean | 是否不限库存(资源侧库存列留空 = 永远可售);true 时 `available=null`、`inventoryStatus=AVAILABLE`,前端展示「不限」 |
| `protocolPrice` | String | 该房型当日**协议价**(元/间·晚)——**核单行 `unitPrice`(单价)建议取这里**;无当日记录为 null |
| `settlementPrice` | String | 该房型当日结算价;无记录为 null |
| `basePrice` | String | 标价/挂牌价;当日无记录时回退到房型基础价 |
| `inventoryStatus` | String | 库存状态,见 §6.3 |
> **核单行快照取值建议**:选中某酒店某房型后,取 `hotelId` + `hotelName` + `roomTypes[].roomTypeId` + `roomTypes[].name`(房型名)+ `roomTypes[].protocolPrice`(单价)填入核单住宿行对应快照字段。
## 6. 枚举 / 数据字典
### 6.1 `hotelType`(字典 `hotel_type`,酒店住宿形态)
**所属字段**:`data.list[].hotelType` | **类型**:`String` | **必填**:❌(可空)
| 值 | 中文 | 说明 |
|----|------|------|
| `HOTEL` | 酒店 | 标准酒店 |
| `GUESTHOUSE` | 客栈/宾馆 | — |
| `RESORT` | 度假村 | — |
| `CAMP` | 营地 | — |
| `HOMESTAY` | 民宿 | — |
### 6.2 `settleType`(字典 `resource_settle_type`,结算类型)
**所属字段**:`data.list[].settleType` | **类型**:`String` | **必填**:❌(可空)
| 值 | 中文 | 说明 |
|----|------|------|
| `cash` | 现结 | 现场现金/即时结算 |
| `sign` | 签单 | 挂账签单,周期结算 |
| `company` | 对公 | 对公转账结算 |
### 6.3 `inventoryStatus`(库存状态,资源价格日历派生)
**所属字段**:`data.list[].roomTypes[].inventoryStatus` | **类型**:`String` | **必填**:✅
| 值 | 中文 | 说明 |
|----|------|------|
| `AVAILABLE` | 可订 | 当日价格日历可售且有余量,或 `unlimited=true` 不限库存 |
| `FULL` | 满房 | 当日可售但余量为 0,或当日已售罄 |
| `CLOSED` | 关闭 | 未配置当日价格日历(漏配)、当日已关闭、或状态异常 |
> FULL / CLOSED 时 `available=0`、`unlimited=false`,但**价格字段照常返回**(前端可展示「满房」「未开放」并 still 显示参考价)。
## 7. 错误码
| code | 含义 | 触发场景 |
|------|------|----------|
| `400` | 参数校验失败 | `stayDate` 缺失(必填)、`pageSize`/`roomCount`/`page` 超出范围(如 pageSize>200 之外非法值、roomCount<1) |
| `401` | 未登录 / 缺少 Authorization 头 | 未带管理后台 JWT 或 token 过期 |
| `200`(空列表) | 查询成功但无候选 | 当日该城市/关键词下无在售可订酒店,或资源侧暂无数据 —— **资源服务不可用时本接口降级返回空列表(不报错)**,前端据此展示「暂无可选酒店」 |
> 本接口是查询类,资源服务故障时**降级返回空列表**(HTTP 200 + 空 `data.list`),不会抛 5xx 阻断核单页面。前端需能区分「真的没酒店」和「服务暂不可用」(两者都返回空列表,后者可稍候重试)。
## 8. 示例
### 8.1 典型成功(按城市 + 入住日查)
**请求**:
```http
GET /v3/admin/order/group-batch/hotel-candidates?stayDate=2026-10-27&city=呼伦贝尔&roomCount=2&page=1&pageSize=20
Authorization: Bearer {admin-token}
(无请求体)
```
**响应**:
```json
{
"code": 200,
"message": "ok",
"success": true,
"data": {
"total": 3,
"pageNo": 1,
"pageSize": 20,
"list": [
{
"hotelId": "70123",
"hotelName": "海拉尔金鼎大酒店",
"starLevel": "FOUR_STAR",
"city": "呼伦贝尔市",
"district": "海拉尔区",
"address": "海拉尔区中央大街123号",
"hotelType": "HOTEL",
"tags": ["协议酒店", "近景区"],
"contactPerson": "张经理",
"contactWechat": "zhang_mgr",
"settleType": "sign",
"coverMaterialId": "90001",
"protoPrice": "328.00",
"protoPriceRoomTypeId": "80125",
"settlementPrice": "300.00",
"roomTypes": [
{
"roomTypeId": "80125",
"name": "豪华大床房",
"description": "含双早",
"maxOccupancy": 2,
"bedType": "KING",
"roomCategory": "KING",
"stock": 10,
"stockUsed": 3,
"available": 7,
"unlimited": false,
"protocolPrice": "328.00",
"settlementPrice": "300.00",
"basePrice": "568.00",
"inventoryStatus": "AVAILABLE"
}
]
}
]
}
}
```
### 8.2 边界情况(关键词跨地区搜 + 不限库存房型)
**场景说明**:用 `keyword` 跨城搜索;命中酒店含「不限库存」房型(`unlimited=true`、`available=null`)。
**请求**:
```http
GET /v3/admin/order/group-batch/hotel-candidates?stayDate=2026-10-27&keyword=蒙古包&roomCount=1
Authorization: Bearer {admin-token}
(无请求体)
```
**响应**:
```json
{
"code": 200,
"message": "ok",
"success": true,
"data": {
"total": 1,
"pageNo": 1,
"pageSize": 50,
"list": [
{
"hotelId": "70200",
"hotelName": "图嘎营地",
"starLevel": null,
"city": "陈巴尔虎旗",
"district": null,
"address": "呼和诺尔湖畔",
"hotelType": "CAMP",
"tags": [],
"contactPerson": null,
"contactWechat": null,
"settleType": "cash",
"coverMaterialId": null,
"protoPrice": "380.00",
"protoPriceRoomTypeId": "80210",
"settlementPrice": "350.00",
"roomTypes": [
{
"roomTypeId": "80210",
"name": "蒙古包",
"description": null,
"maxOccupancy": 4,
"bedType": null,
"roomCategory": "YURT",
"stock": null,
"stockUsed": null,
"available": null,
"unlimited": true,
"protocolPrice": "380.00",
"settlementPrice": "350.00",
"basePrice": null,
"inventoryStatus": "AVAILABLE"
}
]
}
]
}
}
```
### 8.3 业务失败(缺 stayDate)
**场景说明**:必填参数 `stayDate` 未传。
**请求**:
```http
GET /v3/admin/order/group-batch/hotel-candidates?city=呼伦贝尔
Authorization: Bearer {admin-token}
(无请求体)
```
**响应**:
```json
{
"code": 400,
"message": "stayDate 不能为空",
"success": false,
"data": null
}
```
## 9. 业务边界
- ✅ **适用场景**:团期核单住宿行选酒店;任何「无需订单上下文、按入住日查在售酒店价格库存」的管理后台场景。
- ✅ **价格口径**:所有价格(`protoPrice`/`settlementPrice`/`protocolPrice`/`basePrice`)都按 `stayDate` 当天的价格日历取值,**换一天价格可能不同**——必须传准确的入住日期。
- ⚠️ **降级**:资源服务不可用时返回空列表(HTTP 200),不抛错——前端展示「暂无可选酒店」并可提示稍候重试。
- ⚠️ **不限库存**:`unlimited=true` 的房型 `available=null`(表示「不限」),前端不要用 `available==null` 判满房,应看 `inventoryStatus`。
- ❌ **不适用**:需要订单个性化推荐(产品池/定制师点名/已配回显/快速配房)的配房场景 → 请用 `GET /v3/admin/hotel-candidates`(需 orderId)。
## 12. 注意事项
- **纯新增接口**,不影响任何现有接口;`/v3/admin/hotel-candidates`(配房候选)保持原样。
- **已部署测试服**(order-v3,squash commit `09f9dc8c4c`),前端可直接联调。
- 所有 `hotelId`/`roomTypeId`/`protoPriceRoomTypeId`/`coverMaterialId` 为 String(Long 序列化),所有金额字段为 String(BigDecimal 序列化),前端按字符串接收即可。
- 本接口只读、幂等,可在下拉搜索框高频调用(建议前端做防抖)。
### 12.1 核单行「付款方式」取值约定(关联提示)
选中酒店后,核单住宿行保存时需填「付款方式」`paymentMethod`(保存走核单明细接口,不在本候选接口范围)。该字段约定如下,**单体核单与团期核单完全同一套**:
- **走数据字典取值**:字典类型 `settlement_payment_method`,前端调字典接口取下拉选项(**不要前端硬编码三个值**)。
- **提交**:把字典的 `value` 填进 `paymentMethod`。
- **展示**:列表/详情用后端带回的 `paymentMethodName`(中文名后端已拼好,前端不必再翻译)。
字典 `settlement_payment_method` 标准值(3 个):
| value | 中文 | 说明 |
|-------|------|------|
| `CASH_PAID` | 现金已付 | 已现金/即时付掉 |
| `COMPANY_PAID` | 公司支付 | 已对公转账付掉 |
| `SIGNED` | 签单 | 挂账,周期与酒店/供应商结算 |
**⚠️ 与候选接口 `settleType` 的区别(勿混用)**:本候选接口带回的 `settleType`(值域 `cash`/`sign`/`company`,字典 `resource_settle_type`)是**酒店主数据上的习惯结算方式**,与核单行 `paymentMethod`(本次实际付款动作)是**两个字典、两套值**。前端可用 `settleType` 给 `paymentMethod` 做**默认推荐**(如酒店 `settleType=sign` 默认选中 `SIGNED`),但**必须把值做映射后再填,不能直接拿 `settleType` 的值塞进 `paymentMethod`**(`cash`≠`CASH_PAID`,值域不同)。
## 13. 关联 / 联系人
### 13.1 链接
- **Issue**: [#8818](https://git.1814.love/wx/HL/issues/8818)
- **PR**: [#8819](https://git.1814.love/wx/HL/pulls/8819)
- **Merge commit**: [09f9dc8c4c](https://git.1814.love/wx/HL/commit/09f9dc8c4c)
### 13.2 联系人
- **后端负责人**: @yst