修改原因:管理后台已完成 #5356 契约适配,需要同步可追溯的消费终态。\n\n修改内容:将 frontend_status 更新为 implemented,记录业务提交 9b9865a50304b1acc55ab5e4f94bb7dfe52293c0 与验证说明。\n\n实际验证:业务仓库 pnpm checkpoint 全量通过,且提交已确认可从 origin/v2.1 到达。\n\nChangelog:changelogs-v2/2026-07/30_5356_核单八类来源确认状态与车辆Step3接口统一-修改接口-管理后台.md
42 KiB
schema, ticket, title, consumer, 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 | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 5356 | 核单八类来源确认状态与车辆 Step3 接口统一 | admin | 修改接口 | deployed | verified | implemented | Pi | 9b9865a50304b1acc55ab5e4f94bb7dfe52293c0 | 2026-07-30 | 管理后台已统一八类核单来源与逐行确认状态,车辆改用 Order Step3 GET/PUT 并携带 version、保护 FLEET 权威字段;pnpm checkpoint 全量通过,业务提交 9b9865a50304b1acc55ab5e4f94bb7dfe52293c0 已推送至 origin/v2.1。后端车辆 DTO 正向数据与 Full E2E 仍受测试订单无可核单车辆费用限制。 | 2026-07-30 | dev-v3 |
⚠️【修改接口·管理后台】核单八类来源确认状态与车辆 Step3 接口统一 (#5356)
PR: #5362 | 服务: hl-order-service-v3 | 更新时间: 2026-07-30 15:46
1. 接口背景
核单页面需要用同一套规则识别“手工行”和“系统来源行”,并逐行完成确认。此前各 Tab 的 sourceType、确认状态和车辆费用入口不一致,车辆数据还残留过已下线接口的字段口径。本次统一八类核单来源与逐行确认语义,并新增 Order 侧车辆 Step3 草稿查询、全量保存接口。
管理后台应以本文列出的 Order 侧接口为准;已删除的
GET /v3/admin/order/:orderId/settlement/vehicle-fees 和
POST /v3/admin/order/:orderId/settlement/vehicle-fees/confirm
继续保持下线,不得恢复调用。
变更接口(2. 变更清单)
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | Step 1 查询住宿核单明细 | GET | /v3/admin/order/:orderId/settlement/step1 |
修改 | 来源值统一;系统派生行初始为未确认;ID 按字符串返回 |
| 2 | Step 1 保存住宿核单明细 | PUT | /v3/admin/order/:orderId/settlement/step1 |
修改 | 支持逐行 UNCONFIRMED/CONFIRMED;手工新行必须先未确认 |
| 3 | Step 2 查询门票核单明细 | GET | /v3/admin/order/:orderId/settlement/step2 |
修改 | 响应新增逐行确认状态;ID 按字符串返回 |
| 4 | Step 2 保存门票核单明细 | PUT | /v3/admin/order/:orderId/settlement/step2 |
修改 | 请求新增逐行确认状态;手工新行必须先未确认 |
| 5 | 查询车辆核单草稿 | GET | /v3/admin/order/:orderId/settlement/step3/vehicles |
新增 | Order 侧车辆 Step3 唯一查询入口 |
| 6 | 全量保存车辆核单草稿 | PUT | /v3/admin/order/:orderId/settlement/step3/vehicles |
新增 | 带 version 全量保存,支持车务行确认和手工行维护 |
| 7 | 查询领队人员费用 | GET | /v3/admin/order/:orderId/settlement/staff-fees/leaders |
修改 | 新增来源与确认状态名称 |
| 8 | 保存领队人员费用 | PUT | /v3/admin/order/:orderId/settlement/staff-fees/leaders |
修改 | 请求行新增 id/sourceType/settlementConfirmStatus |
| 9 | 查询司机人员费用 | GET | /v3/admin/order/:orderId/settlement/staff-fees/drivers |
修改 | 新增来源与确认状态名称 |
| 10 | 保存司机人员费用 | PUT | /v3/admin/order/:orderId/settlement/staff-fees/drivers |
修改 | 请求行新增 id/sourceType/settlementConfirmStatus |
| 11 | 查询导游人员费用 | GET | /v3/admin/order/:orderId/settlement/staff-fees/guides |
修改 | 新增来源与确认状态名称 |
| 12 | 保存导游人员费用 | PUT | /v3/admin/order/:orderId/settlement/staff-fees/guides |
修改 | 请求行新增 id/sourceType/settlementConfirmStatus |
| 13 | 查询摄影师人员费用 | GET | /v3/admin/order/:orderId/settlement/staff-fees/photographers |
修改 | 新增来源与确认状态名称 |
| 14 | 保存摄影师人员费用 | PUT | /v3/admin/order/:orderId/settlement/staff-fees/photographers |
修改 | 请求行新增 id/sourceType/settlementConfirmStatus |
| 15 | 查询其他人员费用 | GET | /v3/admin/order/:orderId/settlement/staff-fees/others |
修改 | 新增来源与确认状态名称 |
| 16 | 保存其他人员费用 | PUT | /v3/admin/order/:orderId/settlement/staff-fees/others |
修改 | 请求行新增 id/sourceType/settlementConfirmStatus |
| 17 | 查询餐食费用 | GET | /v3/admin/order/:orderId/settlement/meals |
修改 | 响应新增来源、来源名称、确认状态名称 |
| 18 | 新增餐食费用 | POST | /v3/admin/order/:orderId/settlement/meals |
修改 | 请求新增来源和确认状态;新行必须先未确认 |
| 19 | 修改餐食费用 | PUT | /v3/admin/order/:orderId/settlement/meals/:settlementId |
修改 | 可把已存在未确认行保存为已确认 |
| 20 | 查询其他支出 | GET | /v3/admin/order/:orderId/settlement/other-expenses |
修改 | 响应新增来源、来源名称、确认状态名称 |
| 21 | 新增其他支出 | POST | /v3/admin/order/:orderId/settlement/other-expenses |
修改 | 手工新行必须先未确认 |
| 22 | 修改其他支出 | PUT | /v3/admin/order/:orderId/settlement/other-expenses/:settlementId |
修改 | 可把已存在未确认行保存为已确认 |
| 23 | 查询其他收入 | GET | /v3/admin/order/:orderId/settlement/other-incomes |
修改 | sourceType 从内部来源值改为 MANUAL/SYSTEM |
| 24 | 新增其他收入 | POST | /v3/admin/order/:orderId/settlement/other-incomes |
修改 | 手工新行必须先未确认 |
| 25 | 修改其他收入 | PUT | /v3/admin/order/:orderId/settlement/other-incomes/:incomeId |
修改 | 可把已存在未确认行保存为已确认 |
| 26 | 完成核单 | POST | /v3/admin/order/:orderId/settlement/finalize |
修改 | 只接受八类来源同步完成且所有非空明细均已确认的数据 |
3. 接口详情
以下接口均需登录态 JWT 和现有订单查看/核单权限;无接口级特殊限流。GET 为只读幂等;PUT 为全量替换幂等;POST 新增其他收入使用 requestId 保证同订单幂等。
3.1 住宿 Step 1
接口
| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/step1 |
打开住宿 Tab、刷新系统配房来源 |
| PUT | /v3/admin/order/:orderId/settlement/step1 |
全量保存住宿行及逐行确认状态 |
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
String(Long) | 是 | 订单 ID,必须大于 0 |
PUT 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
items |
HotelItem[] | 是 | 全量数组;缺少的手工现存行按删除处理 |
items[].id |
String(Long) | 更新时是 | 已保存行 ID;新行为空 |
items[].hotelAssignmentId |
String(Long) | 否 | 配房来源行 ID;手工行为空 |
items[].hotelId |
String(Long) | 否 | 酒店 ID |
items[].roomTypeId |
String(Long) | 否 | 房型 ID |
items[].stayDate |
Date | 是 | yyyy-MM-dd |
items[].hotelName |
String | 是 | 最长 200 字符 |
items[].roomType |
String | 否 | 房型摘要,最长 64 字符 |
items[].roomTypeName |
String | 否 | 房型/规格名称,最长 128 字符 |
items[].roomCount |
Integer | 是 | 总间数 |
items[].unitPrice |
Decimal | 否 | 核算单价,必须大于等于 0 |
items[].plannedCost |
Decimal | 是 | 计划成本,必须大于等于 0 |
items[].actualCost |
Decimal | 是 | 实际成本,必须大于等于 0 |
items[].paymentMethod |
String | 条件必填 | SIGNED/COMPANY_PAID/CASH_PAID;手工行必填 |
items[].settleType |
String | 否 | cash/sign/company,兼容配房来源付款口径 |
items[].sourceType |
String | 是 | HOUSE_ASSIGNMENT/MANUAL/SYSTEM;TEMPLATE 仅兼容旧入参 |
items[].sourceId |
String(Long) | 否 | 系统来源业务 ID |
items[].settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
items[].remark |
String | 否 | 最长 500 字符 |
items[].voucherUrls |
String[] | 否 | 凭证 URL |
GET 响应 data[]
除上述行字段外,还返回:
| 字段 | 类型 | 说明 |
|---|---|---|
paymentMethodName |
String | 付款方式名称 |
sourceTypeName |
String | 来源名称:配房结果/手工/系统 |
settlementConfirmStatusName |
String | 未确认/已确认 |
PUT 响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
addedIds |
Long[] | 新增行 ID |
updatedIds |
Long[] | 更新行 ID |
deletedIds |
Long[] | 删除行 ID |
totalActualCost |
String(Decimal) | 保存后住宿实际成本合计 |
业务边界
- 配房来源行首次进入草稿返回
UNCONFIRMED;带已有id保存时可改为CONFIRMED。 - 手工新行
id=null时只允许UNCONFIRMED;保存取得 ID 后,下一次 PUT 才可改为CONFIRMED。 TEMPLATE只兼容旧请求,响应统一为SYSTEM。
3.2 门票 Step 2
接口
| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/step2 |
打开门票/游玩项目 Tab、刷新行程来源 |
| PUT | /v3/admin/order/:orderId/settlement/step2 |
全量保存门票行及逐行确认状态 |
PUT 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
items |
TicketItem[] | 是 | 全量数组 |
items[].id |
String(Long) | 更新时是 | 已保存行 ID;新行为空 |
items[].sourceType |
String | 是 | SCENIC_ASSIGNMENT/ACTIVITY_ASSIGNMENT/MANUAL;CUSTOM_ASSIGNMENT 仅兼容旧入参 |
items[].scenicAssignmentId |
String(Long) | 系统行是 | 景区或活动来源 ID |
items[].dayNumber |
Integer | 否 | 行程第几天,响应派生 |
items[].dayDate |
Date | 是 | 行程日 |
items[].scenicName |
String | 是 | 项目名,最长 200 字符 |
items[].specName |
String | 否 | 票型/规格,最长 128 字符 |
items[].ticketCount |
Integer | 是 | 实际购票数量,可为 0 |
items[].ticketUnitPrice |
Decimal | 否 | 参考单价 |
items[].sellPrice |
Decimal | 否 | 客户成交单价,必须大于等于 0 |
items[].totalAmount |
Decimal | 否 | 客户成交小计,必须大于等于 0 |
items[].plannedCost |
Decimal | 是 | 计划成本,必须大于等于 0 |
items[].actualCost |
Decimal | 是 | 实际成本,必须大于等于 0 |
items[].paymentMethod |
String | 否 | SIGNED/COMPANY_PAID/CASH_PAID |
items[].voucherUrls |
String[] | 否 | 凭证 URL |
items[].remark |
String | 否 | 最长 500 字符 |
items[].settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
GET 响应 data[]
返回完整 TicketItem,并增加:
| 字段 | 类型 | 说明 |
|---|---|---|
sourceTypeName |
String | 景区/游玩项目/手工 |
paymentMethodName |
String | 付款方式名称 |
settlementConfirmStatusName |
String | 未确认/已确认 |
PUT 响应与住宿 Step 1 相同:addedIds/updatedIds/deletedIds/totalActualCost。
业务边界
- 系统来源首次同步为
UNCONFIRMED,来源事实变化后会重新变为UNCONFIRMED。 - 手工新行必须先保存为
UNCONFIRMED,已有 ID 后可保存为CONFIRMED。 - 响应不再返回
CUSTOM_ASSIGNMENT,历史自定义值统一返回MANUAL。
3.3 车辆 Step 3
接口
| 方法 | 路径 | 使用场景 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/step3/vehicles |
查询 Order 侧车辆核单草稿 |
| PUT | /v3/admin/order/:orderId/settlement/step3/vehicles |
带版本全量保存车辆行 |
PUT 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
version |
Long | 是 | GET 返回的草稿版本,最小 0 |
items |
VehicleItem[] | 是 | 全量明细;所有现存 FLEET 行必须原样带回 |
items[].id |
String(Long) | FLEET/更新时是 | 行 ID;手工新行为空 |
items[].sourceType |
String | 是 | FLEET/MANUAL |
items[].serviceDate |
Date | 是 | 服务日期 |
items[].vehicleId |
String(Long) | 否 | 车辆 ID |
items[].vehiclePlate |
String | 否 | 车牌,最长 64 字符 |
items[].vehicleModelId |
String(Long) | 否 | 车型 ID |
items[].vehicleModelName |
String | 否 | 车型名,最长 128 字符 |
items[].driverId |
String(Long) | 否 | 司机 ID |
items[].driverName |
String | 否 | 司机名,最长 64 字符 |
items[].amount |
Decimal | 是 | 金额,0~10 位整数、2 位小数 |
items[].paymentMethod |
String | 是 | CASH_PAID/SIGNED/COMPANY_PAID |
items[].settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
items[].remark |
String | 否 | 最长 500 字符 |
items[].voucherUrls |
String[] | 否 | 最多 9 个 http/https URL,单个最长 1024 字符 |
未知字段会被拒绝。
GET/PUT 响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
orderId |
String(Long) | 订单 ID |
version |
Long | 当前草稿版本 |
totalAmount |
Decimal | 当前全部明细金额合计 |
allConfirmed |
Boolean | 非空行是否全部已确认;合法空集为 true |
items |
VehicleItem[] | 当前全量明细 |
items[].id |
String(Long) | 行 ID |
items[].sourceType |
String | FLEET/MANUAL |
items[].sourceTypeName |
String | 车务/手工 |
items[].serviceDate |
Date | 服务日期 |
items[].vehicleId |
String(Long) | 车辆 ID,可空 |
items[].vehiclePlate |
String | 车牌,可空 |
items[].vehicleModelId |
String(Long) | 车型 ID,可空 |
items[].vehicleModelName |
String | 车型名,可空 |
items[].driverId |
String(Long) | 司机 ID,可空 |
items[].driverName |
String | 司机名,可空 |
items[].amount |
Decimal | 核单金额 |
items[].paymentMethod |
String | 付款方式编码 |
items[].paymentMethodName |
String | 付款方式名称 |
items[].settlementConfirmStatus |
String | 确认状态 |
items[].settlementConfirmStatusName |
String | 未确认/已确认 |
items[].remark |
String | 备注 |
items[].voucherUrls |
String[] | 凭证 URL |
业务边界
FLEET行的日期、车辆、司机、金额和付款方式不可修改或删除;只允许修改确认状态、备注、凭证。- 全量保存时必须带回全部
FLEET行。手工行可新增、修改或从全量数组中删除。 - 手工新行必须先保存为
UNCONFIRMED;已有 ID 后可保存为CONFIRMED。 version不匹配返回 584108,必须重新 GET 后再保存。- 旧响应字段
frozen/requirementId/settlementReady/totalVehicleFee及 Fleet 对账明细字段不再对管理后台输出。
3.4 人员费用五个 Tab
接口路径
| 角色 | GET/PUT 路径 | detail 结构 |
|---|---|---|
| 领队 | /v3/admin/order/:orderId/settlement/staff-fees/leaders |
days + per_day |
| 司机 | /v3/admin/order/:orderId/settlement/staff-fees/drivers |
days[] + extra_cost + extra_breakdown[] |
| 导游 | /v3/admin/order/:orderId/settlement/staff-fees/guides |
persons[] |
| 摄影师 | /v3/admin/order/:orderId/settlement/staff-fees/photographers |
persons[] |
| 其他 | /v3/admin/order/:orderId/settlement/staff-fees/others |
items[] |
PUT 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
items |
StaffItem[] | 是 | 当前角色全量数组;空数组清空该 Tab 可删除的行 |
items[].id |
String(Long) | 更新时是 | 已保存行 ID;新行为空 |
items[].sourceType |
String | 是 | STAFF_ASSIGNMENT/MANUAL/SYSTEM |
items[].staffId |
String(Long) | 否 | 人员安排 ID;聚合或手工行可空 |
items[].detail |
Object | 是 | 由路径角色固定,结构见下表 |
items[].reimburse |
Decimal | 否 | 小额报销,空按 0 |
items[].paymentMethod |
String | 否 | 空按 COMPANY_PAID |
items[].voucherUrls |
String[] | 否 | 最多 9 个 http/https URL |
items[].settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
items[].settleStatus |
String | 否 | 辅助人员 PENDING/COMPLETED,空按 PENDING |
items[].settledDate |
Date | 否 | 辅助人员结算日期 |
items[].transferRef |
String | 条件必填 | settleStatus=COMPLETED 时必填,最长 128 字符 |
items[].remark |
String | 否 | 最长 500 字符 |
角色 detail 字段
| 路径角色 | 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|---|
| leaders | days |
Integer | 是 | 天数,最小 0 |
| leaders | per_day |
Decimal | 是 | 每天费用,最小 0 |
| drivers | days |
DriverDay[] | 是 | 服务日明细 |
| drivers | days[].service_date |
Date | 是 | 服务日期 |
| drivers | days[].vehicle_brief |
String | 否 | 车辆摘要 |
| drivers | days[].daily_fee |
Decimal | 是 | 日费,仅回显,不计入人员费用 |
| drivers | days[].is_used |
Boolean | 否 | 是否使用 |
| drivers | days[].note |
String | 否 | 备注 |
| drivers | extra_cost |
Decimal | 否 | 额外费用,空按 0 |
| drivers | extra_breakdown |
ExtraItem[] | 否 | 合计必须等于 extra_cost |
| drivers | extra_breakdown[].name |
String | 是 | 费用名 |
| drivers | extra_breakdown[].amount |
Decimal | 是 | 金额,最小 0 |
| drivers | extra_breakdown[].note |
String | 否 | 备注 |
| guides/photographers | persons |
Person[] | 是 | 人员计费明细 |
| guides/photographers | persons[].name |
String | 是 | 姓名 |
| guides/photographers | persons[].days |
Integer | 是 | 天数,最小 0 |
| guides/photographers | persons[].per_day |
Decimal | 是 | 每天费用,最小 0 |
| guides/photographers | persons[].note |
String | 否 | 备注 |
| others | items |
OtherItem[] | 是 | 其他人员费用项 |
| others | items[].name |
String | 是 | 费用名称 |
| others | items[].amount |
Decimal | 是 | 金额,最小 0 |
| others | items[].note |
String | 否 | 备注 |
GET 响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
totalActualCost |
Decimal | 当前 Tab 实际费用合计 |
items |
StaffItem[] | 已保存行;未保存时可返回候选草稿 |
items[].id |
String(Long) | 行 ID;未保存候选为空 |
items[].sourceType |
String | 来源编码 |
items[].sourceTypeName |
String | 人员安排/手工/系统 |
items[].staffId |
String(Long) | 人员安排 ID,可空 |
items[].staffName |
String | 人员姓名或聚合摘要 |
items[].detail |
Object | 对应角色明细 |
items[].totalPlannedCost |
Decimal | 计划成本 |
items[].totalActualCost |
Decimal | 实际成本 |
items[].reimburse |
Decimal | 小额报销 |
items[].paymentMethod |
String | 付款方式 |
items[].voucherUrls |
String[] | 凭证 URL |
items[].settlementConfirmStatus |
String | 确认状态 |
items[].settlementConfirmStatusName |
String | 未确认/已确认 |
items[].settleStatus |
String | 辅助人员结算状态;主报账人为空 |
items[].settledDate |
Date | 结算日期 |
items[].transferRef |
String | 转账流水号 |
items[].isPrimaryReporter |
Boolean | 是否主报账人 |
items[].remark |
String | 备注 |
PUT 成功返回统一成功包,data=null。
3.5 餐食费用
接口
| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/meals |
data 为 MealItem[] |
| POST | /v3/admin/order/:orderId/settlement/meals |
请求 MealSave;响应 MealItem |
| PUT | /v3/admin/order/:orderId/settlement/meals/:settlementId |
请求 MealSave;响应 MealItem |
MealSave 请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mealType |
String | 是 | BREAKFAST/LUNCH/DINNER |
mealDate |
Date | 否 | 发生日期 |
mealName |
String | 是 | 最长 200 字符 |
quantity |
Integer | 是 | 1~10000 |
unitPrice |
Decimal | 是 | 最多 8 位整数、2 位小数,最小 0 |
paymentMethod |
String | 是 | CASH_PAID/COMPANY_PAID/SIGNED |
sourceType |
String | 是 | MEAL_ASSIGNMENT/MANUAL/SYSTEM;新增接口请传 MANUAL |
settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
voucherUrls |
String[] | 否 | 最多 9 个 http/https URL |
remark |
String | 否 | 最长 512 字符 |
unitPrice × quantity 不得超过 99999999.99。
MealItem 响应
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String(Long) | 餐食费用 ID |
mealType |
String | 餐型 |
mealDate |
Date | 发生日期 |
mealName |
String | 餐食名称 |
quantity |
Integer | 数量 |
unitPrice |
String(Decimal) | 单价 |
actualAmount |
String(Decimal) | 实际金额 |
paymentMethod |
String | 付款类型 |
sourceType |
String | 来源编码 |
sourceTypeName |
String | 餐饮安排/手工/系统 |
voucherUrls |
String[] | 凭证 URL |
settlementConfirmStatus |
String | 确认状态 |
settlementConfirmStatusName |
String | 未确认/已确认 |
remark |
String | 备注 |
POST 创建的是手工行,必须先传 UNCONFIRMED;PUT 已存在行时可传 CONFIRMED,且不得改变原 sourceType。
3.6 其他支出
接口
| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/other-expenses |
data 为 OtherExpenseItem[] |
| POST | /v3/admin/order/:orderId/settlement/other-expenses |
请求 OtherExpenseSave;响应 OtherExpenseItem |
| PUT | /v3/admin/order/:orderId/settlement/other-expenses/:settlementId |
请求 OtherExpenseSave;响应 OtherExpenseItem |
OtherExpenseSave 请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
expenseType |
String | 是 | FUEL/TOLL/PARKING/RENTAL/MAINTENANCE/OTHER |
projectName |
String | 是 | 最长 200 字符 |
expenseDate |
Date | 否 | 发生日期 |
actualAmount |
Decimal | 是 | 最多 8 位整数、2 位小数,最小 0 |
paymentMethod |
String | 是 | CASH_PAID/COMPANY_PAID/SIGNED |
settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
voucherUrls |
String[] | 否 | 最多 9 个 http/https URL |
remark |
String | 否 | 最长 512 字符 |
OtherExpenseItem 响应
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String(Long) | 其他支出 ID |
expenseType |
String | 支出类型 |
projectName |
String | 项目名称 |
expenseDate |
Date | 发生日期 |
actualAmount |
String(Decimal) | 实际金额 |
paymentMethod |
String | 付款类型 |
sourceType |
String | MANUAL/SYSTEM |
sourceTypeName |
String | 手工/系统 |
voucherUrls |
String[] | 凭证 URL |
settlementConfirmStatus |
String | 确认状态 |
settlementConfirmStatusName |
String | 未确认/已确认 |
remark |
String | 备注 |
POST 创建的是 MANUAL 行且必须先为 UNCONFIRMED;已有 ID 后通过 PUT 可改为 CONFIRMED。
3.7 其他收入
接口
| 方法 | 路径 | 请求/响应 |
|---|---|---|
| GET | /v3/admin/order/:orderId/settlement/other-incomes |
data 为列表聚合对象 |
| POST | /v3/admin/order/:orderId/settlement/other-incomes |
请求 Create;响应 OtherIncomeItem |
| PUT | /v3/admin/order/:orderId/settlement/other-incomes/:incomeId |
请求 Update;响应 OtherIncomeItem |
Create/Update 请求
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
requestId |
String | 仅 POST 是 | 同订单永久唯一,最长 64 字符 |
incomeDate |
Date | 是 | 收入日期 |
projectName |
String | 是 | 最长 100 字符 |
projectCategory |
String | 是 | 启用字典值,最长 64 字符 |
specification |
String | 否 | 票种/规格,最长 100 字符 |
quantity |
Decimal | 是 | 最多 8 位整数、4 位小数,最小 0 |
unitPrice |
Decimal | 是 | 最多 8 位整数、2 位小数,最小 0 |
settlementAmount |
Decimal | 是 | 最多 8 位整数、2 位小数,必须大于 0,且等于数量乘单价(四舍五入到 2 位) |
paymentMethod |
String | 是 | CASH_PAID/COMPANY_PAID/SIGNED |
settlementConfirmStatus |
String | 是 | UNCONFIRMED/CONFIRMED |
voucherUrls |
String[] | 否 | 最多 9 个 http/https URL |
remark |
String | 否 | 最长 500 字符 |
OtherIncomeItem 响应
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String(Long) | 其他收入 ID |
requestId |
String | 手工新增幂等 ID;系统投影为空 |
incomeDate |
Date | 收入日期 |
projectName |
String | 项目名称 |
projectCategory |
String | 项目类别 |
projectCategoryName |
String | 项目类别名称 |
specification |
String | 票种/规格 |
quantity |
Decimal | 数量 |
unitPrice |
Decimal | 核算单价 |
settlementAmount |
Decimal | 核算金额 |
paymentMethod |
String | 付款类型 |
paymentMethodName |
String | 付款类型名称 |
voucherUrls |
String[] | 凭证 URL |
settlementConfirmStatus |
String | 确认状态 |
settlementConfirmStatusName |
String | 未确认/已确认 |
remark |
String | 备注 |
sourceType |
String | MANUAL/SYSTEM |
sourceTypeName |
String | 手工/系统 |
sourceId |
String(Long) | 关联来源 ID |
GET 聚合响应
| 字段 | 类型 | 说明 |
|---|---|---|
items |
OtherIncomeItem[] | 其他收入明细 |
deductions |
Deduction[] | 只读减费明细 |
deductions[].id |
String(Long) | 减费 ID |
deductions[].discountName |
String | 减费名称 |
deductions[].discountAmount |
Decimal | 减费金额 |
deductions[].sourceType |
String | 减费来源 |
deductions[].sourceId |
String(Long) | 来源业务 ID |
deductions[].createdAt |
DateTime | 创建时间 |
summary.surchargeAmount |
Decimal | 有效增费合计 |
summary.discountAmount |
Decimal | 有效减费合计 |
summary.netAdjustmentAmount |
Decimal | 增费减去减费 |
手工新增行的公开来源为 MANUAL;系统自动投影行公开来源为 SYSTEM。原公开值 ORDER_SURCHARGE 不再返回。
3.8 完成核单
接口:POST /v3/admin/order/:orderId/settlement/finalize
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
remark |
String | 否 | 整体备注,最长 500 字符 |
reimbursementExpectedSourceFingerprint |
String | 是 | 主报账表 64 位小写 SHA-256 指纹 |
groupExpectedSourceFingerprint |
String | 是 | 单团核算表 64 位小写 SHA-256 指纹 |
reimbursementConfirmation |
Object | 是 | 主报账确认凭据 |
reimbursementConfirmation.transferDate |
Date | 条件必填 | 主报账净额非 0 时必填 |
reimbursementConfirmation.transferRef |
String | 条件必填 | 主报账净额非 0 时必填,最长 128 字符 |
reimbursementConfirmation.advanceSettledFlag |
Boolean | 是 | 预支是否已处理;false 是合法值 |
reimbursementConfirmation.signedVoucher |
Object | 是 | 签字凭证 |
reimbursementConfirmation.signedVoucher.files |
File[] | 是 | 1~9 项 |
reimbursementConfirmation.signedVoucher.files[].name |
String | 否 | 文件名,最长 255 字符 |
reimbursementConfirmation.signedVoucher.files[].url |
String | 是 | 文件 URL,最长 1024 字符 |
reimbursementConfirmation.signedVoucher.note |
String | 否 | 最长 500 字符 |
响应 data
| 字段 | 类型 | 说明 |
|---|---|---|
summaryId |
String(Long) | 核单汇总 ID |
finalSnapshotId |
String(Long) | 终态快照 ID |
finalSnapshotVersionNo |
Integer | 快照版本 |
finalSnapshotStatus |
String | 成功时为 FINALIZED |
orderId |
String(Long) | 订单 ID |
settledAt |
DateTime | 核单完成时间 |
totalAmount |
String(Decimal) | 订单总金额快照 |
paidAmount |
String(Decimal) | 已付金额快照 |
balanceAmount |
String(Decimal) | 尾款金额快照 |
roomCost |
String(Decimal) | 住宿实际成本 |
ticketCost |
String(Decimal) | 门票实际成本 |
staffCost |
String(Decimal) | 人员费用实际成本 |
subsidyCost |
String(Decimal) | 补助实际成本 |
mealCost |
String(Decimal) | 餐食实际成本 |
vehicleCost |
String(Decimal) | 车辆实际成本 |
otherExpenseCost |
String(Decimal) | 其他支出实际成本 |
insurancePremium |
String(Decimal) | 保险实际保费 |
totalActualCost |
String(Decimal) | 总实际成本 |
driverTransferAmount |
String(Decimal) | 给主报账人的转回金额 |
profitAmount |
String(Decimal) | 公司毛利 |
profitRate |
Decimal | 毛利率小数 |
orderStatusAfter |
String | 完成后的订单状态 |
mqTriggered |
Boolean | 当前固定为 false |
warnings |
String[] | 不阻塞完成核单的软预警 |
业务边界
- 住宿、门票/游玩、餐食、车辆、导游、摄影、其他收入、其他支出八类来源必须同步完成。
- 任一非空分类存在
UNCONFIRMED行时,finalize 返回 584310,不生成终态快照。 - 系统来源事实变化会使对应行重新变为
UNCONFIRMED;应刷新、复核并保存后再 finalize。
4. 接口入参汇总
| 输入类型 | 适用接口 | 关键变化 |
|---|---|---|
| 路径参数 | 全部 26 个接口 | orderId 必填;行级修改另有 settlementId/incomeId |
| 全量明细 | 住宿、门票、车辆、五个人员 Tab | 必须提交完整 items;新增手工行先传 UNCONFIRMED |
| 单行保存 | 餐食、其他支出、其他收入 | POST 新增先未确认,PUT 已有行可确认 |
| 车辆版本 | 车辆 PUT | 必须原样回传最近 GET 的 version |
| 完成核单凭据 | finalize | 双报告指纹 + 主报账转账/签字凭据 |
完整字段、必填性和校验已分别内联在 §3.1~§3.8。
5. 出参字段汇总
| 变化 | 适用响应 |
|---|---|
新增 sourceType/sourceTypeName |
人员、餐食、其他支出;其他收入的来源语义调整 |
新增 settlementConfirmStatus/settlementConfirmStatusName |
门票;餐食、其他支出、人员补齐名称 |
| Long ID 按字符串返回 | 住宿、门票、人员、车辆以及已有明确字符串序列化的资金明细 |
| 新车辆草稿结构 | orderId/version/totalAmount/allConfirmed/items |
| 删除车辆内部/兼容字段 | 不再输出 frozen/requirementId/settlementReady/totalVehicleFee 及 Fleet 对账字段 |
6. 枚举 / 数据字典
6.1 住宿 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
HOUSE_ASSIGNMENT |
配房结果 | 系统配房来源 |
MANUAL |
手工 | 管理后台手工新增 |
SYSTEM |
系统 | 其他系统来源;替代旧公开值 TEMPLATE |
TEMPLATE |
历史兼容 | 仅请求兼容,响应不返回 |
6.2 门票 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
SCENIC_ASSIGNMENT |
景区 | 景区安排来源 |
ACTIVITY_ASSIGNMENT |
游玩项目 | 活动安排来源 |
MANUAL |
手工 | 手工新增;响应统一值 |
CUSTOM_ASSIGNMENT |
历史兼容 | 仅请求兼容,响应归一为 MANUAL |
6.3 餐食 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
MEAL_ASSIGNMENT |
餐饮安排 | 系统餐饮安排来源 |
MANUAL |
手工 | 管理后台新增 |
SYSTEM |
系统 | 其他系统来源 |
6.4 车辆 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
FLEET |
车务 | 车务同步行,业务字段不可改删 |
MANUAL |
手工 | 核单页手工补录 |
6.5 人员 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
STAFF_ASSIGNMENT |
人员安排 | 系统人员安排来源 |
MANUAL |
手工 | 手工新增 |
SYSTEM |
系统 | 其他系统来源 |
6.6 其他收入/其他支出 sourceType
| 值 | 中文 | 说明 |
|---|---|---|
MANUAL |
手工 | 管理后台创建 |
SYSTEM |
系统 | 自动投影或系统来源 |
6.7 settlementConfirmStatus
| 值 | 中文 | 说明 |
|---|---|---|
UNCONFIRMED |
未确认 | 首次系统同步或手工新行的初始状态 |
CONFIRMED |
已确认 | 已有行复核后保存的状态 |
6.8 paymentMethod
| 值 | 中文 | 说明 |
|---|---|---|
CASH_PAID |
现付 | 现场/主报账人支付 |
COMPANY_PAID |
公司付款 | 公司直接支付 |
SIGNED |
签单 | 签单结算 |
6.9 餐型 mealType
| 值 | 中文 |
|---|---|
BREAKFAST |
早餐 |
LUNCH |
午餐 |
DINNER |
晚餐 |
6.10 支出类型 expenseType
| 值 | 中文 |
|---|---|
FUEL |
油费 |
TOLL |
过路费 |
PARKING |
停车费 |
RENTAL |
租赁费 |
MAINTENANCE |
维修保养 |
OTHER |
其他 |
6.11 人员辅助结算状态 settleStatus
| 值 | 中文 | 说明 |
|---|---|---|
PENDING |
待结算 | 默认值 |
COMPLETED |
已结算 | 必须同时提交 transferRef |
7. 错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 必填缺失、格式/长度/枚举错误、车辆或人员请求出现未知字段 |
584001/584010/584020/584050/584070 |
订单不存在 | 对应住宿、门票、人员、finalize 或通用查询找不到订单 |
584002/584011/584021 |
当前状态不可写 | 住宿、门票、人员费用不在允许的核单阶段 |
584006/584012/584022 |
枚举或角色非法 | 住宿付款、门票来源、人员角色非法 |
584067/584068/584069 |
住宿资源无效或暂不可用 | 手工酒店/房型无效或资源服务不可用 |
584071 |
无权访问该订单 | 公司隔离或现有订单权限不满足 |
584073 |
其他收入不存在 | incomeId 不属于当前订单 |
584074 |
当前状态不允许修改其他收入 | 核单状态不可写 |
584076 |
其他收入金额不一致 | settlementAmount != quantity × unitPrice |
584077 |
存在未确认的其他收入 | 生成报告或完成核单前仍有其他收入未确认 |
584086 |
无权修改核单资金数据 | 非主管、管理员或财务 |
584087 |
requestId 冲突 |
同订单相同 requestId 被另一笔请求占用 |
584089 |
核单或结算已完成 | 再次修改资金明细 |
584090/584091 |
餐食/其他支出不存在 | 行 ID 不属于当前订单 |
584092 |
存在未确认的人员费用 | 生成报告或完成核单前仍有人员费用未确认 |
584094/584095 |
餐食/其他支出字段非法 | 字段越界或试图改变系统来源 |
584096/584097 |
付款方式/凭证非法 | 枚举错误或 URL 数量、格式错误 |
584098 |
餐食或其他支出存在未确认记录 | 生成报告或完成核单前仍有餐食/其他支出未确认 |
584100/584101/584102 |
车辆来源不可用/未就绪/为空 | 车务数据不可读、未完结/未确认、无可核单费用 |
584103/584104/584105 |
其他收入字典非法或不可用 | 项目类别、规格无效或字典不可用 |
584106 |
确认状态非法 | 非 UNCONFIRMED/CONFIRMED |
584107 |
手工新增行必须先未确认 | id=null 的手工新行直接传 CONFIRMED |
584108 |
车辆草稿版本冲突 | PUT 的 version 已过期 |
584109 |
车务来源字段不可改删 | 修改/漏传 FLEET 行的权威字段 |
584310 |
八类核单未全部确认或数据已变化 | finalize 前有非空未确认行、来源未就绪 |
584315 |
报告来源已变化 | finalize 双报告指纹过期 |
584317 |
当前报告状态不允许操作 | finalize 凭据结构或状态不满足 |
584325 |
完成核单必须提交当前指纹 | 双报告指纹缺失 |
验证证据(8. 示例:典型 / 边界 / 异常)
已完成以下测试环境路由与业务负向验证:
- 部署任务
80f1695a成功,order-v3 主、副实例滚动完成。 - 两个核算中订单实调
GET /v3/admin/order/:orderId/settlement/step3/vehicles均进入新代码并返回业务前置码584102,证明新路由已生效。 - 旧
GET /v3/admin/order/:orderId/settlement/vehicle-fees返回404,证明旧入口已下线。
本节下列 JSON 是按已合并 Controller/VO 契约给出的自包含调用示例。由于测试订单缺少可核单车辆费用,本次未取得车辆 DTO 正向数据,也未完成车辆链路 Full E2E;不得把上述 584102 负向结果描述为正向业务通过。
8.1 典型成功:确认车辆系统来源行
请求
PUT /v3/admin/order/900000000001/settlement/step3/vehicles
Authorization: Bearer <JWT>
Content-Type: application/json
{
"version": 3,
"items": [
{
"id": "930000000001",
"sourceType": "FLEET",
"serviceDate": "2026-07-30",
"vehicleId": "880000000001",
"vehiclePlate": "藏A12345",
"vehicleModelId": "870000000001",
"vehicleModelName": "七座商务车",
"driverId": "860000000001",
"driverName": "张师傅",
"amount": 1200.00,
"paymentMethod": "COMPANY_PAID",
"settlementConfirmStatus": "CONFIRMED",
"remark": "金额已核对",
"voucherUrls": []
}
]
}
响应
{
"code": 200,
"message": "成功",
"data": {
"orderId": "900000000001",
"version": 4,
"totalAmount": 1200.00,
"allConfirmed": true,
"items": [
{
"id": "930000000001",
"sourceType": "FLEET",
"sourceTypeName": "车务",
"serviceDate": "2026-07-30",
"vehicleId": "880000000001",
"vehiclePlate": "藏A12345",
"vehicleModelId": "870000000001",
"vehicleModelName": "七座商务车",
"driverId": "860000000001",
"driverName": "张师傅",
"amount": 1200.00,
"paymentMethod": "COMPANY_PAID",
"paymentMethodName": "公司付款",
"settlementConfirmStatus": "CONFIRMED",
"settlementConfirmStatusName": "已确认",
"remark": "金额已核对",
"voucherUrls": []
}
]
},
"success": true
}
8.2 边界情况:合法空车辆草稿
无当前用车需求时,GET 可返回合法空集;allConfirmed=true 表示“空集中没有未确认行”,不表示存在车辆费用。
请求
GET /v3/admin/order/900000000001/settlement/step3/vehicles
Authorization: Bearer <JWT>
无请求体。
响应
{
"code": 200,
"message": "成功",
"data": {
"orderId": "900000000001",
"version": 1,
"totalAmount": 0.00,
"allConfirmed": true,
"items": []
},
"success": true
}
8.3 业务失败:手工新行直接确认
请求
POST /v3/admin/order/900000000001/settlement/meals
Authorization: Bearer <JWT>
Content-Type: application/json
{
"mealType": "LUNCH",
"mealDate": "2026-07-30",
"mealName": "团队午餐",
"quantity": 10,
"unitPrice": 50.00,
"paymentMethod": "CASH_PAID",
"sourceType": "MANUAL",
"settlementConfirmStatus": "CONFIRMED",
"voucherUrls": [],
"remark": null
}
响应
{
"code": 584107,
"message": "手工新增核单明细必须先保存为未确认",
"data": null,
"success": false
}
9. 业务边界
- ✅ 系统来源首次同步:生成已有 ID 的
UNCONFIRMED行;复核后可直接在对应 PUT 中保存为CONFIRMED。 - ✅ 手工新增:第一次必须保存为
UNCONFIRMED;接口返回 ID 后,第二次更新才允许保存为CONFIRMED。 - ✅ 来源统一:手工行统一公开为
MANUAL;系统行公开为各分类系统来源值,无法细分的系统行为SYSTEM。 - ❌ 不可混用确认和辅助结算状态:
settlementConfirmStatus表示核单确认;人员settleStatus表示辅助人员款项是否结清。 - ❌ 不可修改系统权威字段:系统来源事实变化后应重新 GET;车辆
FLEET行不得由前端改删。 - ⚠️ finalize 门禁:八个核单分类来源必须就绪,且每个非空分类全部逐行
CONFIRMED。 - ⚠️ 空分类:合法空分类没有未确认行,但来源同步仍必须就绪;
allConfirmed=true不等于有费用。
10. 修改前后对比
10.1 字段级对比
| 范围 | 改前 | 改后 |
|---|---|---|
| 住宿系统来源 | TEMPLATE |
SYSTEM;TEMPLATE 仅兼容旧入参 |
| 门票手工来源 | 可能返回 CUSTOM_ASSIGNMENT |
统一返回 MANUAL |
| 其他收入来源 | ORDER_SURCHARGE |
MANUAL 或 SYSTEM |
| 门票行确认 | 无逐行确认字段 | 新增 settlementConfirmStatus/Name |
| 人员请求行 | 无 id/sourceType/settlementConfirmStatus |
三字段纳入全量保存契约 |
| 餐食请求/响应 | 无公开来源,确认名称不完整 | 增加 sourceType/sourceTypeName/settlementConfirmStatusName |
| 其他支出响应 | 无公开来源和确认名称 | 增加 sourceType/sourceTypeName/settlementConfirmStatusName |
| 住宿/门票/人员 ID | 部分按 JSON 数字输出 | 明细 id、来源 ID 按字符串输出 |
| 车辆顶层响应 | frozen/requirementId/settlementReady/totalVehicleFee/items |
orderId/version/totalAmount/allConfirmed/items |
| 车辆行响应 | 暴露 Fleet 对账、冻结和自动车费字段 | 仅输出核单需要的来源、车辆、司机、金额、付款、确认、凭证字段 |
10.2 行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 系统派生行初始状态 | 分类规则不一致,部分直接视为已确认 | 统一先 UNCONFIRMED,用户复核后保存为 CONFIRMED |
| 手工新增并确认 | 部分接口允许一次保存即确认 | 必须先未确认,取得 ID 后再确认 |
| 车辆入口 | 旧 /settlement/vehicle-fees 已下线且无新独立编辑入口 |
使用 /settlement/step3/vehicles GET/PUT |
| 车辆并发保存 | 无前端草稿版本 | 必须携带 version,冲突时刷新 |
| 完成核单 | 分类确认来源不完全统一 | 只接受八类来源就绪且非空行全部已确认的数据 |
11. 影响评估 / 回滚
11.1 影响评估
- 是否破坏向后兼容:是。车辆入口和响应结构为新契约;其他收入
sourceType值发生变化;多个请求/响应新增确认与来源字段。 - 前端是否必须同步上线:是。需切换车辆接口、适配字符串 ID、新来源枚举和“先保存未确认、再确认”的交互。
11.2 回滚说明
若后端回滚,前端需同时回滚车辆 Step3 新入口及新增字段依赖;旧
/settlement/vehicle-fees 两个接口在本次变更前已下线,不能作为回滚兜底。
12. 注意事项
- 删除对旧
GET /settlement/vehicle-fees和POST /settlement/vehicle-fees/confirm的任何残留调用。 - 车辆保存必须回传最近 GET 的
version和全部FLEET行;584108 时刷新后让用户重新确认。 - 不再把
ORDER_SURCHARGE、TEMPLATE、CUSTOM_ASSIGNMENT当作新响应值。 - 所有 Long 类型字符串 ID 按字符串比较、传递,不转为 JavaScript Number。
- finalize 返回 584310 时,应刷新相关 Tab;来源变化可能已把已确认行重新置为未确认。
13. 关联 / 联系人
13.1 链接
- Issue: #5356
- PR: #5362
- Feature commit: 6e396f6fc4
- Merge commit: cfac945db2
13.2 联系人
- 后端负责人: @yaosutu