所有检测均成功
changelog-filename-gate / validate (push) Successful in 1s
修改原因:hl-ui changelog loop 需要把前端消费进度回写到契约源,避免领取、实现状态与实际交付脱节。 修改内容:将 frontend_status 与已有 legacy frontend 同步为 implemented,记录负责人 hl-ui-codex,并关联 mmg/hl-ui@00ea8e2ad187b6b91626f069fc3a4a5ba5a76e06;发布和验收字段保持不变。 实际验证:回写器已校验目标文件、状态单调性、提交范围和 Front Matter 内容,提交只包含当前 changelog。 Frontend-Status: tools/mcp-api-sync/.changelog-repo/changelogs-v2/2026-07/25_5219_核单原型流迁移-修改接口-管理后台.md
34 KiB
34 KiB
frontend_status, frontend_owner, frontend_ref, updated_at
| frontend_status | frontend_owner | frontend_ref | updated_at |
|---|---|---|---|
| implemented | hl-ui-codex | mmg/hl-ui@00ea8e2ad1 | 2026-07-25T08:37:46.681Z |
【修改接口·管理后台】核单原型流迁移 (#5219)
PR: #5243 | 服务: hl-order-service-v3 | 更新时间: 2026-07-25 16:08
1. 接口背景
核单流程从旧的“财务总览/优惠逐条确认后直接 Step6 提交”调整为原型流:先确认 8 个核单分类,再生成并确认主报账人报账表,再生成并确认单团核算表,最后完成核单。
2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 查询八分类确认状态 | GET | /v3/admin/order/{orderId}/settlement/category-checks |
新增 | 返回 8 个核单分类的确认状态、行数和来源指纹 |
| 2 | 确认单个核单分类 | POST | /v3/admin/order/{orderId}/settlement/category-checks/{category}/confirm |
新增 | 用最近读取的分类指纹确认单个分类 |
| 3 | 查询主报账人报账表 | GET | /v3/admin/order/{orderId}/settlement/reports/reimbursement |
新增 | 查询主报账表当前快照 |
| 4 | 生成主报账人报账表 | POST | /v3/admin/order/{orderId}/settlement/reports/reimbursement/generate |
新增 | 8 分类全部确认后生成 |
| 5 | 确认主报账人报账表 | POST | /v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm |
新增 | 确认转账、预支和签字单 |
| 6 | 查询单团核算表 | GET | /v3/admin/order/{orderId}/settlement/reports/group |
新增 | 查询单团核算表当前快照 |
| 7 | 生成单团核算表 | POST | /v3/admin/order/{orderId}/settlement/reports/group/generate |
新增 | 主报账表确认后生成 |
| 8 | 确认单团核算表 | POST | /v3/admin/order/{orderId}/settlement/reports/group/confirm |
新增 | 用最近读取的报告指纹确认 |
| 9 | 完成核单 | POST | /v3/admin/order/{orderId}/settlement/finalize |
新增 | 单团核算表确认后提交核单 |
| 10 | 查询核单应收财务总览 | GET | /v3/admin/order/{orderId}/settlement/financial-overview |
修改 | 仍保留查询,但不再返回确认状态/指纹 |
| 11 | 确认财务总览 | POST | /v3/admin/order/{orderId}/settlement/financial-overview/confirm |
删除 | 旧 POST 契约下线 |
| 12 | 确认单条优惠 | POST | /v3/admin/order/{orderId}/settlement/financial-overview/discounts/{discountId}/confirm |
删除 | 旧 POST 契约下线 |
3. 接口详情
3.1 查询八分类确认状态
- 方法 + 路径:
GET /v3/admin/order/{orderId}/settlement/category-checks - 接口名: 查询原型八个核单分类确认状态
- 认证: 管理后台 JWT;房务角色不可访问
- 幂等性: 幂等,只读
- 限流: 无接口级特殊限流
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long/String | 是 | 订单 ID,必须大于 0 |
请求体: 无
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
orderId |
String | 订单 ID |
allConfirmed |
Boolean | 8 个分类是否全部已确认 |
items |
Array | 分类确认项 |
items[].category |
String | 分类枚举,见 §6.1 |
items[].categoryName |
String | 分类中文名 |
items[].rowCount |
Integer | 当前分类参与核单的行数 |
items[].empty |
Boolean | 当前分类是否为空 |
items[].sourceFingerprint |
String | 当前分类来源事实 SHA-256 |
items[].confirmStatus |
String | UNCONFIRMED / CONFIRMED |
items[].confirmedBy |
String/null | 确认人 ID |
items[].confirmedByName |
String/null | 确认人名称 |
items[].confirmedAt |
String/null | 确认时间,未确认时为 null |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
| 400 | 参数校验失败 | orderId <= 0 |
| 403 | 无访问权限 | 房务角色访问 |
典型成功示例
请求:
GET /v3/admin/order/1914050000000001/settlement/category-checks
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"orderId": "1914050000000001",
"allConfirmed": false,
"items": [
{
"category": "HOTEL",
"categoryName": "住宿",
"rowCount": 2,
"empty": false,
"sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"confirmStatus": "CONFIRMED",
"confirmedBy": "10001",
"confirmedByName": "张三",
"confirmedAt": "2026-07-25T15:20:00"
},
{
"category": "OTHER_EXPENSE",
"categoryName": "其他支出",
"rowCount": 0,
"empty": true,
"sourceFingerprint": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
"confirmStatus": "UNCONFIRMED",
"confirmedBy": null,
"confirmedByName": null,
"confirmedAt": null
}
]
}
}
边界示例
请求:
GET /v3/admin/order/1914050000000001/settlement/category-checks
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"orderId": "1914050000000001",
"allConfirmed": false,
"items": [
{
"category": "MEAL",
"categoryName": "餐食",
"rowCount": 0,
"empty": true,
"sourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"confirmStatus": "UNCONFIRMED",
"confirmedBy": null,
"confirmedByName": null,
"confirmedAt": null
}
]
}
}
异常示例
请求:
GET /v3/admin/order/0/settlement/category-checks
Authorization: Bearer <token>
响应:
{
"code": 400,
"msg": "订单 ID 必须大于 0",
"data": null
}
业务边界
- 适用:进入核单流程后读取 8 个分类状态。
- 不适用:房务角色不可查看。
- 特殊边界:空分类仍需要走显式确认,不能因为
rowCount=0自动通过。
3.2 确认单个核单分类
- 方法 + 路径:
POST /v3/admin/order/{orderId}/settlement/category-checks/{category}/confirm - 接口名: 按最近读取指纹确认单个核单分类
- 认证: 管理后台 JWT;需要财务写权限;房务角色不可访问
- 幂等性: 非纯幂等;相同指纹重复确认不会改变来源事实
- 限流: 无接口级特殊限流
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long/String | 是 | 订单 ID,必须大于 0 |
category |
String | 是 | 分类枚举,大小写不敏感,见 §6.1 |
请求体字段
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
expectedSourceFingerprint |
String | 是 | 最近读取的分类来源事实 SHA-256 | 64 位小写十六进制 |
confirmEmpty |
Boolean | 是 | 是否明确确认空分类 | 空分类传 true,非空分类传 false |
响应字段: 同 §3.1 items[] 单项。
错误码
| code | 含义 | 触发场景 |
|---|---|---|
| 400 | 参数校验失败 | 指纹不是 64 位十六进制、缺少 confirmEmpty |
| 584315 | 核单来源数据已变化,请刷新后重新生成 | 前端传入的指纹与当前分类来源不一致 |
| 584318 | 空分类必须显式确认,非空分类不得按空分类确认 | confirmEmpty 与当前分类是否为空不匹配 |
| 584319 | 核单存在未知分类或历史迁移数据不完整 | category 不是 8 分类枚举 |
典型成功示例
请求:
POST /v3/admin/order/1914050000000001/settlement/category-checks/HOTEL/confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"confirmEmpty": false
}
响应:
{
"code": 200,
"msg": "success",
"data": {
"category": "HOTEL",
"categoryName": "住宿",
"rowCount": 2,
"empty": false,
"sourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"confirmStatus": "CONFIRMED",
"confirmedBy": "10001",
"confirmedByName": "张三",
"confirmedAt": "2026-07-25T15:24:00"
}
}
边界示例
请求:
POST /v3/admin/order/1914050000000001/settlement/category-checks/MEAL/confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"expectedSourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"confirmEmpty": true
}
响应:
{
"code": 200,
"msg": "success",
"data": {
"category": "MEAL",
"categoryName": "餐食",
"rowCount": 0,
"empty": true,
"sourceFingerprint": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
"confirmStatus": "CONFIRMED",
"confirmedBy": "10001",
"confirmedByName": "张三",
"confirmedAt": "2026-07-25T15:25:00"
}
}
异常示例
请求:
POST /v3/admin/order/1914050000000001/settlement/category-checks/UNKNOWN/confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"expectedSourceFingerprint": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"confirmEmpty": false
}
响应:
{
"code": 584319,
"msg": "核单存在未知分类或历史迁移数据不完整",
"data": null
}
业务边界
- 适用:确认当前分类数据没有变化。
- 不适用:前端未先读取
sourceFingerprint时不应直接确认。 - 特殊边界:
OTHER_INCOME确认前会校验其他收入可提交状态。
3.3 主报账人报账表
- 查询:
GET /v3/admin/order/{orderId}/settlement/reports/reimbursement - 生成:
POST /v3/admin/order/{orderId}/settlement/reports/reimbursement/generate - 确认:
POST /v3/admin/order/{orderId}/settlement/reports/reimbursement/confirm - 接口名: 查询主报账人报账表 / 八分类确认后生成主报账人报账表 / 完成转账、预支及签字门禁后确认主报账人报账表
- 认证: 管理后台 JWT;房务角色不可访问
- 幂等性: 查询幂等;生成/确认会更新报告状态和确认信息
- 限流: 无接口级特殊限流
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long/String | 是 | 订单 ID,必须大于 0 |
确认请求体字段
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
expectedSourceFingerprint |
String | 是 | 最近读取的主报账表来源 SHA-256 | 64 位小写十六进制 |
transferStatus |
String | 否 | 转账状态 | 后端按字符串保存 |
transferDate |
String/date | 否 | 转账日期 | YYYY-MM-DD |
transferRef |
String | 否 | 转账流水/备注 | 字符串 |
advanceSettledFlag |
Boolean | 是 | 预支是否已结清 | 不能为空 |
signedVoucher |
Object | 是 | 签字单回收信息 | 不能为空 |
signedVoucher.files |
Array | 否 | 文件列表 | 元素见下 |
signedVoucher.files[].name |
String | 否 | 文件名 | 字符串 |
signedVoucher.files[].url |
String | 否 | OSS 文件地址 | 字符串 |
signedVoucher.note |
String | 否 | 备注 | 字符串 |
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String/null | 主报账记录 ID;未生成时可为 null |
orderId |
String | 订单 ID |
reportStatus |
String | 报告状态,见 §6.2 |
sourceFingerprint |
String | 当前来源 SHA-256 |
primaryReporterId |
String/null | 主报账人 ID |
primaryReporterName |
String/null | 主报账人姓名 |
primaryReporterRole |
String/null | 主报账人角色 |
primaryReporterCollectedAmount |
Decimal | 主报账人代收金额 |
publicPrepaidAmount |
Decimal | 公共预支金额 |
primaryReporterDueAmount |
Decimal | 主报账人应报账金额 |
advanceOutstandingAmount |
Decimal | 未结清预支金额 |
reconNetAmount |
Decimal | 报账净额 |
transferDirection |
String/null | 转账方向 |
transferAmount |
Decimal | 转账金额 |
incomeLines |
Array | 收入明细行 |
expenseLines |
Array | 支出明细行 |
advanceLines |
Array | 预支明细行 |
transferStatus |
String/null | 转账状态 |
transferDate |
String/date/null | 转账日期 |
transferRef |
String/null | 转账流水/备注 |
advanceSettledFlag |
Boolean/null | 预支是否已结清 |
signedVoucher |
Object/null | 签字单信息 |
generatedBy / generatedByName / generatedAt |
String/String/String | 生成信息 |
confirmedBy / confirmedByName / confirmedAt |
String/String/String | 确认信息 |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
| 584310 | 八个核单分类尚未全部确认或数据已变化 | 生成主报账表前 8 分类未全部确认或指纹已失效 |
| 584311 | 主报账表尚未生成 | 查询/确认时还没有可用主报账表 |
| 584315 | 核单来源数据已变化,请刷新后重新生成 | 确认时传入的报告指纹已过期 |
| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 |
| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能生成或确认 |
典型成功示例
请求:
POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"expectedSourceFingerprint": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"transferStatus": "TRANSFERRED",
"transferDate": "2026-07-25",
"transferRef": "BANK-20260725-001",
"advanceSettledFlag": true,
"signedVoucher": {
"files": [
{
"name": "签字单.pdf",
"url": "https://oss.example.com/voucher.pdf"
}
],
"note": "签字单已回收"
}
}
响应:
{
"code": 200,
"msg": "success",
"data": {
"id": "9200000000001",
"orderId": "1914050000000001",
"reportStatus": "CONFIRMED",
"sourceFingerprint": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
"primaryReporterId": "3001",
"primaryReporterName": "王司机",
"primaryReporterRole": "DRIVER",
"primaryReporterCollectedAmount": 2000.00,
"publicPrepaidAmount": 500.00,
"primaryReporterDueAmount": 1500.00,
"advanceOutstandingAmount": 0.00,
"reconNetAmount": 1500.00,
"transferDirection": "COMPANY_TO_REPORTER",
"transferAmount": 1500.00,
"incomeLines": [],
"expenseLines": [],
"advanceLines": [],
"transferStatus": "TRANSFERRED",
"transferDate": "2026-07-25",
"transferRef": "BANK-20260725-001",
"advanceSettledFlag": true,
"signedVoucher": {
"files": [
{
"name": "签字单.pdf",
"url": "https://oss.example.com/voucher.pdf"
}
],
"note": "签字单已回收"
},
"generatedBy": "10001",
"generatedByName": "张三",
"generatedAt": "2026-07-25T15:30:00",
"confirmedBy": "10001",
"confirmedByName": "张三",
"confirmedAt": "2026-07-25T15:35:00"
}
}
边界示例
请求:
GET /v3/admin/order/1914050000000001/settlement/reports/reimbursement
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"id": "9200000000001",
"orderId": "1914050000000001",
"reportStatus": "STALE",
"sourceFingerprint": "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
"primaryReporterId": null,
"primaryReporterName": null,
"primaryReporterRole": null,
"primaryReporterCollectedAmount": 0.00,
"publicPrepaidAmount": 0.00,
"primaryReporterDueAmount": 0.00,
"advanceOutstandingAmount": 0.00,
"reconNetAmount": 0.00,
"transferDirection": null,
"transferAmount": 0.00,
"incomeLines": [],
"expenseLines": [],
"advanceLines": [],
"transferStatus": null,
"transferDate": null,
"transferRef": null,
"advanceSettledFlag": null,
"signedVoucher": null,
"generatedBy": "10001",
"generatedByName": "张三",
"generatedAt": "2026-07-25T15:30:00",
"confirmedBy": null,
"confirmedByName": null,
"confirmedAt": null
}
}
异常示例
请求:
POST /v3/admin/order/1914050000000001/settlement/reports/reimbursement/generate
Authorization: Bearer <token>
响应:
{
"code": 584310,
"msg": "八个核单分类尚未全部确认或数据已变化",
"data": null
}
业务边界
- 适用:8 分类全部确认后生成主报账表。
- 不适用:8 分类未确认完成时不能生成。
- 特殊边界:查询返回
STALE时表示来源已变化,应重新生成后再确认。
3.4 单团核算表
- 查询:
GET /v3/admin/order/{orderId}/settlement/reports/group - 生成:
POST /v3/admin/order/{orderId}/settlement/reports/group/generate - 确认:
POST /v3/admin/order/{orderId}/settlement/reports/group/confirm - 接口名: 查询单团核算表 / 主报账表确认后生成单团核算表 / 按最近读取指纹确认单团核算表
- 认证: 管理后台 JWT;房务角色不可访问
- 幂等性: 查询幂等;生成/确认会更新报告状态和确认信息
- 限流: 无接口级特殊限流
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long/String | 是 | 订单 ID,必须大于 0 |
确认请求体字段
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
expectedSourceFingerprint |
String | 是 | 最近读取的单团核算表来源 SHA-256 | 64 位小写十六进制 |
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String/null | 单团核算记录 ID |
orderId |
String | 订单 ID |
reportStatus |
String | 报告状态,见 §6.2 |
sourceFingerprint |
String | 当前来源 SHA-256 |
baseOrderAmount |
Decimal | 订单基础金额 |
otherIncomeAmount |
Decimal | 其他收入金额 |
discountAmount |
Decimal | 优惠金额 |
adjustedReceivableAmount |
Decimal | 调整后应收 |
paidAmount |
Decimal | 已收金额 |
actualRefundedAmount |
Decimal | 实际退款金额 |
netRevenueAmount |
Decimal | 净收入 |
netReceivedAmount |
Decimal | 净已收 |
outstandingAmount |
Decimal | 待收金额 |
hotelCost |
Decimal | 住宿成本 |
ticketCost |
Decimal | 门票/游玩项目成本 |
mealCost |
Decimal | 餐食成本 |
vehicleCost |
Decimal | 车辆成本 |
guideCost |
Decimal | 导游成本 |
photographerCost |
Decimal | 摄影成本 |
otherExpenseCost |
Decimal | 其他支出成本 |
insurancePremium |
Decimal | 保险保费 |
totalCost |
Decimal | 总成本 |
paidCost |
Decimal | 已付成本 |
unpaidCost |
Decimal | 未付成本 |
grossProfit |
Decimal | 毛利 |
grossProfitRate |
Decimal | 毛利率 |
travelerCount |
Integer | 出行人数 |
perCapitaRevenue |
Decimal | 人均收入 |
perCapitaCost |
Decimal | 人均成本 |
perCapitaProfit |
Decimal | 人均利润 |
incomeLines |
Array | 收入明细行 |
costCategories |
Array | 成本分类行 |
generatedBy / generatedByName / generatedAt |
String/String/String | 生成信息 |
confirmedBy / confirmedByName / confirmedAt |
String/String/String | 确认信息 |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
| 584312 | 主报账表尚未确认或数据已变化 | 生成单团核算表前主报账表未确认或已过期 |
| 584313 | 单团核算表尚未生成 | 查询/确认时还没有可用单团核算表 |
| 584315 | 核单来源数据已变化,请刷新后重新生成 | 确认时传入的报告指纹已过期 |
| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 |
| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能生成或确认 |
典型成功示例
请求:
POST /v3/admin/order/1914050000000001/settlement/reports/group/confirm
Authorization: Bearer <token>
Content-Type: application/json
{
"expectedSourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
}
响应:
{
"code": 200,
"msg": "success",
"data": {
"id": "9300000000001",
"orderId": "1914050000000001",
"reportStatus": "CONFIRMED",
"sourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"baseOrderAmount": 24800.00,
"otherIncomeAmount": 500.00,
"discountAmount": 300.00,
"adjustedReceivableAmount": 25000.00,
"paidAmount": 25000.00,
"actualRefundedAmount": 0.00,
"netRevenueAmount": 25000.00,
"netReceivedAmount": 25000.00,
"outstandingAmount": 0.00,
"hotelCost": 4280.00,
"ticketCost": 3680.00,
"mealCost": 860.00,
"vehicleCost": 1260.00,
"guideCost": 800.00,
"photographerCost": 600.00,
"otherExpenseCost": 300.00,
"insurancePremium": 180.00,
"totalCost": 11960.00,
"paidCost": 11960.00,
"unpaidCost": 0.00,
"grossProfit": 13040.00,
"grossProfitRate": 0.521600,
"travelerCount": 5,
"perCapitaRevenue": 5000.00,
"perCapitaCost": 2392.00,
"perCapitaProfit": 2608.00,
"incomeLines": [],
"costCategories": [],
"generatedBy": "10001",
"generatedByName": "张三",
"generatedAt": "2026-07-25T15:40:00",
"confirmedBy": "10001",
"confirmedByName": "张三",
"confirmedAt": "2026-07-25T15:45:00"
}
}
边界示例
请求:
GET /v3/admin/order/1914050000000001/settlement/reports/group
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"id": "9300000000001",
"orderId": "1914050000000001",
"reportStatus": "GENERATED",
"sourceFingerprint": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"baseOrderAmount": 0.00,
"otherIncomeAmount": 0.00,
"discountAmount": 0.00,
"adjustedReceivableAmount": 0.00,
"paidAmount": 0.00,
"actualRefundedAmount": 0.00,
"netRevenueAmount": 0.00,
"netReceivedAmount": 0.00,
"outstandingAmount": 0.00,
"hotelCost": 0.00,
"ticketCost": 0.00,
"mealCost": 0.00,
"vehicleCost": 0.00,
"guideCost": 0.00,
"photographerCost": 0.00,
"otherExpenseCost": 0.00,
"insurancePremium": 0.00,
"totalCost": 0.00,
"paidCost": 0.00,
"unpaidCost": 0.00,
"grossProfit": 0.00,
"grossProfitRate": 0.000000,
"travelerCount": 0,
"perCapitaRevenue": 0.00,
"perCapitaCost": 0.00,
"perCapitaProfit": 0.00,
"incomeLines": [],
"costCategories": [],
"generatedBy": "10001",
"generatedByName": "张三",
"generatedAt": "2026-07-25T15:40:00",
"confirmedBy": null,
"confirmedByName": null,
"confirmedAt": null
}
}
异常示例
请求:
POST /v3/admin/order/1914050000000001/settlement/reports/group/generate
Authorization: Bearer <token>
响应:
{
"code": 584312,
"msg": "主报账表尚未确认或数据已变化",
"data": null
}
业务边界
- 适用:主报账表已确认后生成单团核算表。
- 不适用:主报账表未确认或为
STALE时不能生成。 - 特殊边界:金额为 0 时仍返回完整字段,数组字段为空数组。
3.5 完成核单
- 方法 + 路径:
POST /v3/admin/order/{orderId}/settlement/finalize - 接口名: 单团核算表确认后完成核单
- 认证: 管理后台 JWT;房务角色不可访问
- 幂等性: 非幂等;完成后订单进入核单提交后的状态
- 限流: 无接口级特殊限流
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long/String | 是 | 订单 ID,必须大于 0 |
请求体: 无
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
summaryId |
String | 新写入的 settlement summary 主键 |
orderId |
String | 订单 ID |
settledAt |
String | 核单完成时间 |
totalAmount |
Decimal | 订单总金额快照 |
paidAmount |
Decimal | 已付金额快照 |
balanceAmount |
Decimal | 尾款金额快照 |
roomCost |
Decimal | 住宿实际成本 |
ticketCost |
Decimal | 门票实际成本 |
staffCost |
Decimal | 人员费用实际成本 |
subsidyCost |
Decimal | 补助实际成本 |
mealCost |
Decimal | 餐食实际成本 |
otherExpenseCost |
Decimal | 其他支出实际成本,含车辆费用 |
insurancePremium |
Decimal | 保险实际保费 |
totalActualCost |
Decimal | 总实际成本 |
driverTransferAmount |
Decimal | 给司机/主报账人转回金额 |
profitAmount |
Decimal | 公司毛利 |
profitRate |
Decimal | 毛利率 |
orderStatusAfter |
String | 结算后订单状态 |
mqTriggered |
Boolean | 结算事件是否成功触发 |
warnings |
Array | 软预警列表 |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
| 584314 | 单团核算表尚未确认或数据已变化 | finalize 前单团核算表未确认或已过期 |
| 584317 | 当前报告状态不允许执行该操作 | 当前流程状态不能完成核单 |
典型成功示例
请求:
POST /v3/admin/order/1914050000000001/settlement/finalize
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"summaryId": "9400000000001",
"orderId": "1914050000000001",
"settledAt": "2026-07-25T15:50:00",
"totalAmount": 25000.00,
"paidAmount": 25000.00,
"balanceAmount": 0.00,
"roomCost": 4280.00,
"ticketCost": 3680.00,
"staffCost": 1400.00,
"subsidyCost": 0.00,
"mealCost": 860.00,
"otherExpenseCost": 1560.00,
"insurancePremium": 180.00,
"totalActualCost": 11960.00,
"driverTransferAmount": 11780.00,
"profitAmount": 13040.00,
"profitRate": 0.5216,
"orderStatusAfter": "待财务复核",
"mqTriggered": true,
"warnings": []
}
}
边界示例
请求:
POST /v3/admin/order/1914050000000001/settlement/finalize
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"summaryId": "9400000000002",
"orderId": "1914050000000001",
"settledAt": "2026-07-25T15:55:00",
"totalAmount": 0.00,
"paidAmount": 0.00,
"balanceAmount": 0.00,
"roomCost": 0.00,
"ticketCost": 0.00,
"staffCost": 0.00,
"subsidyCost": 0.00,
"mealCost": 0.00,
"otherExpenseCost": 0.00,
"insurancePremium": 0.00,
"totalActualCost": 0.00,
"driverTransferAmount": 0.00,
"profitAmount": 0.00,
"profitRate": 0,
"orderStatusAfter": "待财务复核",
"mqTriggered": true,
"warnings": ["住宿 D2 现付缺凭证"]
}
}
异常示例
请求:
POST /v3/admin/order/1914050000000001/settlement/finalize
Authorization: Bearer <token>
响应:
{
"code": 584314,
"msg": "单团核算表尚未确认或数据已变化",
"data": null
}
业务边界
- 适用:单团核算表已确认后完成核单。
- 不适用:单团核算表未确认、已过期或流程状态不允许。
- 特殊边界:
warnings为软预警,不阻塞成功响应。
3.6 查询核单应收财务总览
- 方法 + 路径:
GET /v3/admin/order/{orderId}/settlement/financial-overview - 接口名: 查询核单应收财务总览
- 变更点: 查询接口保留,但返回字段删除旧确认状态/指纹语义;确认动作迁移到 8 分类和报告流。
响应字段
| 字段 | 类型 | 说明 |
|---|---|---|
baseOrderAmount |
Decimal | 订单基础金额 |
otherIncomeAmount |
Decimal | 有效增费合计 |
discountAmount |
Decimal | 有效优惠合计 |
adjustedReceivableAmount |
Decimal | 调整后应收 |
onlinePaidAmount |
Decimal | 成功线上支付合计 |
offlinePaidAmount |
Decimal | 有效线下收款合计 |
primaryReporterCollectedAmount |
Decimal | 当前主报账司机正式代收 |
paidAmount |
Decimal | 已收合计 |
actualRefundedAmount |
Decimal | 实际退款合计 |
netPaidAmount |
Decimal | 净已收 |
outstandingAmount |
Decimal | 待收 |
surchargeMirrorMatched |
Boolean | 增费镜像是否匹配权威明细 |
discountMirrorMatched |
Boolean | 优惠镜像是否匹配权威明细 |
paidMirrorMatched |
Boolean | 已收镜像是否匹配权威明细 |
refundedMirrorMatched |
Boolean | 退款镜像是否匹配权威明细 |
discounts |
Array | 当前有效优惠 |
discounts[].discountId |
String | 优惠 ID |
discounts[].name |
String | 优惠名称 |
discounts[].type |
String | 优惠类型 |
discounts[].amount |
Decimal | 优惠金额 |
示例
请求:
GET /v3/admin/order/1914050000000001/settlement/financial-overview
Authorization: Bearer <token>
响应:
{
"code": 200,
"msg": "success",
"data": {
"baseOrderAmount": 24800.00,
"otherIncomeAmount": 500.00,
"discountAmount": 300.00,
"adjustedReceivableAmount": 25000.00,
"onlinePaidAmount": 22000.00,
"offlinePaidAmount": 3000.00,
"primaryReporterCollectedAmount": 2000.00,
"paidAmount": 25000.00,
"actualRefundedAmount": 0.00,
"netPaidAmount": 25000.00,
"outstandingAmount": 0.00,
"surchargeMirrorMatched": true,
"discountMirrorMatched": true,
"paidMirrorMatched": true,
"refundedMirrorMatched": true,
"discounts": [
{
"discountId": "9100000000001",
"name": "老客优惠",
"type": "CUSTOMER_DISCOUNT",
"amount": 300.00
}
]
}
}
6. 枚举 / 数据字典
6.1 category(SettlementCategory)
所属字段: 路径参数 category、响应 items[].category | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
HOTEL |
住宿 | 住宿核单分类 |
TICKET |
门票/游玩项目 | 门票及游玩项目核单分类 |
MEAL |
餐食 | 餐食核单分类 |
VEHICLE |
车辆 | 车辆费用核单分类 |
GUIDE |
导游 | 导游费用核单分类 |
PHOTOGRAPHER |
摄影 | 摄影费用核单分类 |
OTHER_INCOME |
其他收入 | 其他收入核单分类 |
OTHER_EXPENSE |
其他支出 | 其他支出核单分类 |
6.2 reportStatus(SettlementReportStatus)
所属字段: reportStatus | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
GENERATED |
已生成 | 报告已生成,尚未确认 |
CONFIRMED |
已确认 | 报告已确认 |
STALE |
已过期 | 来源事实已变化,需要重新生成 |
6.3 confirmStatus
所属字段: items[].confirmStatus | 类型: String
| 值 | 中文 | 说明 |
|---|---|---|
UNCONFIRMED |
未确认 | 分类尚未确认 |
CONFIRMED |
已确认 | 分类已确认 |
7. 错误码汇总
| code | 含义 | 触发场景 |
|---|---|---|
| 584310 | 八个核单分类尚未全部确认或数据已变化 | 生成主报账表前门禁失败 |
| 584311 | 主报账表尚未生成 | 主报账表查询/确认前置缺失 |
| 584312 | 主报账表尚未确认或数据已变化 | 生成单团核算表前门禁失败 |
| 584313 | 单团核算表尚未生成 | 单团核算表查询/确认前置缺失 |
| 584314 | 单团核算表尚未确认或数据已变化 | finalize 前门禁失败 |
| 584315 | 核单来源数据已变化,请刷新后重新生成 | 分类或报告指纹过期 |
| 584316 | 核单报告发生并发变化,请刷新后重试 | 并发确认冲突 |
| 584317 | 当前报告状态不允许执行该操作 | 当前状态不能执行生成/确认/finalize |
| 584318 | 空分类必须显式确认,非空分类不得按空分类确认 | confirmEmpty 与分类是否为空不匹配 |
| 584319 | 核单存在未知分类或历史迁移数据不完整 | 分类枚举非法或历史数据缺失 |
10. 修改前后对比
10.1 字段级对比
| 接口/字段 | 改前 | 改后 |
|---|---|---|
GET /settlement/financial-overview |
返回应收总览及逐条优惠确认状态 | 只返回应收总览和优惠明细,不再承载确认流 |
POST /settlement/financial-overview/confirm 请求体 |
expectedOverviewFingerprint |
接口删除,改用 POST /settlement/category-checks/{category}/confirm 的 expectedSourceFingerprint + confirmEmpty |
POST /settlement/financial-overview/discounts/{discountId}/confirm 请求体 |
expectedSourceFingerprint |
接口删除,优惠纳入 8 分类/报告流 |
| 主报账表 | 无独立响应结构 | 新增 SettlementReimbursementReportRespVO |
| 单团核算表 | 无独立响应结构 | 新增 SettlementGroupReportRespVO |
| 完成核单 | 旧入口为 POST /settlement/step6/submit |
新增原型流入口 POST /settlement/finalize,返回 SettlementSubmitRespVO |
10.2 行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 核单确认门禁 | 财务总览/优惠逐条确认 | 8 分类全部确认 |
| 报账表 | 无独立生成/确认步骤 | 先生成并确认主报账人报账表 |
| 单团核算 | 无独立生成/确认步骤 | 主报账表确认后生成并确认单团核算表 |
| 最终提交 | 直接 Step6 提交 | 单团核算表确认后调用 finalize |
| 旧 POST 接口 | 可调用 | 下线,调用方需迁移 |
11. 影响评估 / 回滚
11.1 影响评估
- 是否破坏向后兼容: 是。两个旧 POST 确认接口删除;财务总览查询响应语义收窄。
- 前端是否必须同步上线: 是。核单页面需要按 8 分类确认、主报账表、单团核算表、finalize 的顺序对接。
- 影响已有数据: 前端接口层不需要处理数据库迁移细节;历史订单在接口层按当前数据计算状态。
11.2 回滚方案
- 回滚方式: 回滚 PR #5243 后恢复旧确认流。
- 回滚后清理: 前端需恢复旧
financial-overview/confirm和discounts/{discountId}/confirm调用。 - 回滚耗时: 取决于后端回滚与重新部署;前端接口调用需同步回退。
12. 注意事项
- 前端不要继续调用已删除的两个旧 POST 确认接口。
- 8 分类确认必须使用刚查询到的
sourceFingerprint,报告确认必须使用刚查询/生成返回的sourceFingerprint。 STALE表示来源已变化,不能继续确认。- 空分类确认必须传
confirmEmpty=true;非空分类必须传false。
13. 关联 / 联系人
13.1 链接
13.2 联系人
- 后端负责人: @yst