37 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, generated
| 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 | generated |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 5320 | 核单其他收支确认状态与项目类别 | admin | 修改接口 | deployed | not_required | pending | 其他收入、其他支出保存时必须提交确认状态并原样保存;其他收入项目类别和非空票种规格改为启用字典值校验。 | 2026-07-29 | dev-v3 | 2026-07-29T09:19:00+08:00 |
【⚠️ 修改接口·管理后台】核单其他收支确认状态与项目类别(#5320)
PR: #5330 | 服务: hl-order-service-v3 | 更新时间: 2026-07-29 09:19
1. 接口背景
核单「其他收入」「其他支出」需要在保存时明确记录当前确认状态,不能再由后端统一改成已确认。四个保存接口新增必填字段 settlementConfirmStatus,调用方传 UNCONFIRMED 或 CONFIRMED,响应返回实际保存值。
其他收入同时收紧项目类别和票种/规格:
projectCategory必须提交settlement_other_income_project_category字典的启用dictValue;specification可空,非空时必须提交settlement_ticket_spec字典的启用dictValue;- 响应
projectCategoryName返回项目类别字典的中文dictLabel。
关键变化:#5310 的“保存即确认”规则已被本次变更替代。保存接口不再强制写
CONFIRMED,调用方必须明确提交确认状态。此前删除的两个独立确认接口不恢复。
路径中的 :orderId、:incomeId、:settlementId 表示对应的路径参数。
变更接口
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 新增其他收入并原子创建订单增费 | POST | /v3/admin/order/:orderId/settlement/other-incomes |
修改 | settlementConfirmStatus 改为必填;项目类别和非空规格按启用字典校验 |
| 2 | 修改其他收入 | PUT | /v3/admin/order/:orderId/settlement/other-incomes/:incomeId |
修改 | settlementConfirmStatus 改为必填并保存传入值;项目类别和非空规格按启用字典校验 |
| 3 | 新增其他支出 | POST | /v3/admin/order/:orderId/settlement/other-expenses |
修改 | settlementConfirmStatus 改为必填并保存传入值 |
| 4 | 修改其他支出 | PUT | /v3/admin/order/:orderId/settlement/other-expenses/:settlementId |
修改 | settlementConfirmStatus 改为必填并保存传入值 |
3. 接口详情
3.1 新增其他收入
- 方法 + 路径:
POST /v3/admin/order/:orderId/settlement/other-incomes - 接口名:新增其他收入并原子创建订单增费
- 使用场景:在核单其他收入页新增一条记录,并明确该记录当前是未确认还是已确认。
- 认证:需要管理后台登录态;需要资金写入权限。
- 幂等性:是;同一订单内
requestId永久唯一。相同requestId且完整请求载荷一致时返回同一条记录;确认状态也是幂等载荷的一部分。 - 限流:无接口专属限流。
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long | 是 | 订单 ID,必须大于 0 |
请求体
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
requestId |
String | 是 | 客户端生成的稳定幂等请求 ID,同订单内永久唯一 | 非空,最长 64 字符 |
incomeDate |
String(date) | 是 | 收入日期,格式 YYYY-MM-DD |
非空 |
projectName |
String | 是 | 项目名称 | 非空,最长 100 字符 |
projectCategory |
String | 是 | 项目类别编码 | 必须是 settlement_other_income_project_category 的启用 dictValue |
specification |
String | 否 | 票种/规格 | 最长 100 字符;非空时必须是 settlement_ticket_spec 的启用 dictValue |
quantity |
Decimal | 是 | 数量 | >= 0,最多 8 位整数、4 位小数 |
unitPrice |
Decimal | 是 | 核算单价 | >= 0,最多 8 位整数、2 位小数 |
settlementAmount |
Decimal | 是 | 核算金额 | > 0,最多 8 位整数、2 位小数;必须等于 quantity * unitPrice 四舍五入到 2 位 |
paymentMethod |
String | 是 | 付款类型 | CASH_PAID / COMPANY_PAID / SIGNED |
settlementConfirmStatus |
String | 是 | 本次保存的确认状态 | UNCONFIRMED / CONFIRMED |
voucherUrls |
String[] | 否 | 凭证 URL 列表 | 最多 9 项;每项最长 1024 字符;必须是 http/https |
remark |
String | 否 | 备注 | 最长 500 字符 |
响应 Result<SettlementOtherIncomeItemRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 业务状态码,成功为 200 |
message |
String | 响应消息,成功为“成功” |
data.id |
String | 其他收入 ID |
data.requestId |
String / null | 手工新增幂等请求 ID;自动投影记录为空 |
data.incomeDate |
String(date) | 收入日期 |
data.projectName |
String | 项目名称 |
data.projectCategory |
String | 项目类别 dictValue |
data.projectCategoryName |
String | 项目类别 dictLabel;标签缺失时回退为 projectCategory |
data.specification |
String / null | 票种/规格 dictValue |
data.quantity |
Decimal | 数量 |
data.unitPrice |
Decimal | 核算单价 |
data.settlementAmount |
Decimal | 核算金额 |
data.paymentMethod |
String | 付款类型 |
data.paymentMethodName |
String | 付款类型名称 |
data.voucherUrls |
String[] | 凭证 URL 列表 |
data.settlementConfirmStatus |
String | 实际保存的确认状态,与请求一致 |
data.settlementConfirmStatusName |
String | 未确认 / 已确认 |
data.remark |
String / null | 备注 |
data.sourceType |
String | 固定为 ORDER_SURCHARGE |
data.sourceTypeName |
String | 固定为“订单增费” |
data.sourceId |
String | 来源附加费 ID |
traceId |
String / null | 链路追踪 ID |
success |
Boolean | code=200 时为 true |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 必填缺失、字段长度超限、确认状态/付款类型格式非法、凭证 URL 非法、金额不等于数量乘单价 |
584074 |
当前核单状态不允许修改其他收入 | 订单核单状态不允许写入 |
584075 |
其他收入关联的附加费来源无效 | 无法得到有效来源记录 |
584076 |
其他收入核算金额必须等于数量乘以核算单价 | 金额关系不一致 |
584086 |
无权修改核单资金数据 | 当前账号不是允许写入资金数据的角色 |
584087 |
requestId 已用于另一笔其他收入 | 同订单重复使用 requestId,但请求载荷不同 |
584089 |
核单或结算已完成,资金数据不可再修改 | 订单资金数据已锁定 |
584103 |
其他收入项目类别不在启用字典范围内 | projectCategory 不是启用 dictValue |
584104 |
其他收入票种/规格不在启用字典范围内 | 非空 specification 不是启用 dictValue |
584105 |
结算字典暂时不可用,请稍后重试 | 字典服务失败、空响应或无启用项 |
584106 |
确认状态非法 | 服务层收到非 UNCONFIRMED / CONFIRMED;公开接口通常先由参数校验返回 400 |
业务边界
settlementConfirmStatus必须显式提交;不传不会默认成CONFIRMED。- 传
UNCONFIRMED时记录保存成功并返回未确认;提交核单前仍需改为CONFIRMED。 projectCategory提交英文dictValue,不能提交中文dictLabel。specification可省略或传null;非空时必须是票种规格启用值。requestId的重复请求必须连确认状态在内保持完整载荷一致,否则返回584087。
示例
典型成功:保存为已确认
请求:
POST /v3/admin/order/60001/settlement/other-incomes
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"requestId": "oi-20260729-0001",
"incomeDate": "2026-07-29",
"projectName": "临时加收门票",
"projectCategory": "TICKET",
"specification": "成人票",
"quantity": 2,
"unitPrice": 120.00,
"settlementAmount": 240.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "CONFIRMED",
"voucherUrls": ["https://cdn.example.com/vouchers/income-1.jpg"],
"remark": "现场补收"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "99001",
"requestId": "oi-20260729-0001",
"incomeDate": "2026-07-29",
"projectName": "临时加收门票",
"projectCategory": "TICKET",
"projectCategoryName": "门票/游玩项目",
"specification": "成人票",
"quantity": 2,
"unitPrice": 120.00,
"settlementAmount": 240.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现付",
"voucherUrls": ["https://cdn.example.com/vouchers/income-1.jpg"],
"settlementConfirmStatus": "CONFIRMED",
"settlementConfirmStatusName": "已确认",
"remark": "现场补收",
"sourceType": "ORDER_SURCHARGE",
"sourceTypeName": "订单增费",
"sourceId": "88001"
},
"traceId": "a1b2c3d4-e5f6-7890",
"success": true
}
边界成功:无规格、保存为未确认
请求:
POST /v3/admin/order/60001/settlement/other-incomes
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"requestId": "oi-20260729-0002",
"incomeDate": "2026-07-29",
"projectName": "其他收入",
"projectCategory": "OTHER",
"specification": null,
"quantity": 0.0001,
"unitPrice": 100.00,
"settlementAmount": 0.01,
"paymentMethod": "SIGNED",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": [],
"remark": null
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "99002",
"requestId": "oi-20260729-0002",
"incomeDate": "2026-07-29",
"projectName": "其他收入",
"projectCategory": "OTHER",
"projectCategoryName": "其他",
"specification": null,
"quantity": 0.0001,
"unitPrice": 100.00,
"settlementAmount": 0.01,
"paymentMethod": "SIGNED",
"paymentMethodName": "签单",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": null,
"sourceType": "ORDER_SURCHARGE",
"sourceTypeName": "订单增费",
"sourceId": "88002"
},
"traceId": "b2c3d4e5-f6a7-8901",
"success": true
}
业务失败:项目类别不是启用字典值
请求:
POST /v3/admin/order/60001/settlement/other-incomes
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"requestId": "oi-20260729-0003",
"incomeDate": "2026-07-29",
"projectName": "自由文本类别",
"projectCategory": "房差",
"quantity": 1,
"unitPrice": 100.00,
"settlementAmount": 100.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "CONFIRMED"
}
响应:
{
"code": 584103,
"message": "其他收入项目类别不在启用字典范围内",
"data": null,
"traceId": "c3d4e5f6-a7b8-9012",
"success": false
}
3.2 修改其他收入
- 方法 + 路径:
PUT /v3/admin/order/:orderId/settlement/other-incomes/:incomeId - 接口名:修改其他收入;金额或项目名变化时原子冲销并重建订单增费
- 使用场景:修改已有其他收入,或把记录的确认状态在
UNCONFIRMED与CONFIRMED之间明确切换。 - 认证:需要管理后台登录态;需要资金写入权限。
- 幂等性:否。
- 限流:无接口专属限流。
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long | 是 | 订单 ID,必须大于 0 |
incomeId |
Long | 是 | 其他收入 ID,必须大于 0 |
请求体
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
incomeDate |
String(date) | 是 | 收入日期,格式 YYYY-MM-DD |
非空 |
projectName |
String | 是 | 项目名称 | 非空,最长 100 字符 |
projectCategory |
String | 是 | 项目类别编码 | 必须是 settlement_other_income_project_category 的启用 dictValue |
specification |
String | 否 | 票种/规格 | 最长 100 字符;非空时必须是 settlement_ticket_spec 的启用 dictValue |
quantity |
Decimal | 是 | 数量 | >= 0,最多 8 位整数、4 位小数 |
unitPrice |
Decimal | 是 | 核算单价 | >= 0,最多 8 位整数、2 位小数 |
settlementAmount |
Decimal | 是 | 核算金额 | > 0,最多 8 位整数、2 位小数;必须等于 quantity * unitPrice 四舍五入到 2 位 |
paymentMethod |
String | 是 | 付款类型 | CASH_PAID / COMPANY_PAID / SIGNED |
settlementConfirmStatus |
String | 是 | 本次保存的确认状态 | UNCONFIRMED / CONFIRMED |
voucherUrls |
String[] | 否 | 凭证 URL 列表 | 最多 9 项;每项最长 1024 字符;必须是 http/https |
remark |
String | 否 | 备注 | 最长 500 字符 |
响应 Result<SettlementOtherIncomeItemRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 业务状态码,成功为 200 |
message |
String | 响应消息,成功为“成功” |
data.id |
String | 其他收入 ID |
data.requestId |
String / null | 原记录的幂等请求 ID |
data.incomeDate |
String(date) | 收入日期 |
data.projectName |
String | 项目名称 |
data.projectCategory |
String | 项目类别 dictValue |
data.projectCategoryName |
String | 项目类别 dictLabel;标签缺失时回退为 projectCategory |
data.specification |
String / null | 票种/规格 dictValue |
data.quantity |
Decimal | 数量 |
data.unitPrice |
Decimal | 核算单价 |
data.settlementAmount |
Decimal | 核算金额 |
data.paymentMethod |
String | 付款类型 |
data.paymentMethodName |
String | 付款类型名称 |
data.voucherUrls |
String[] | 凭证 URL 列表 |
data.settlementConfirmStatus |
String | 实际保存的确认状态,与请求一致 |
data.settlementConfirmStatusName |
String | 未确认 / 已确认 |
data.remark |
String / null | 备注 |
data.sourceType |
String | 固定为 ORDER_SURCHARGE |
data.sourceTypeName |
String | 固定为“订单增费” |
data.sourceId |
String | 来源附加费 ID |
traceId |
String / null | 链路追踪 ID |
success |
Boolean | code=200 时为 true |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 必填缺失、字段长度超限、确认状态/付款类型格式非法、凭证 URL 非法、金额不一致 |
584073 |
其他收入不存在或不属于当前订单 | incomeId 不存在或不属于 orderId |
584074 |
当前核单状态不允许修改其他收入 | 订单核单状态不允许写入 |
584076 |
其他收入核算金额必须等于数量乘以核算单价 | 金额关系不一致 |
584086 |
无权修改核单资金数据 | 当前账号不是允许写入资金数据的角色 |
584089 |
核单或结算已完成,资金数据不可再修改 | 订单资金数据已锁定 |
584103 |
其他收入项目类别不在启用字典范围内 | projectCategory 不是启用 dictValue |
584104 |
其他收入票种/规格不在启用字典范围内 | 非空 specification 不是启用 dictValue |
584105 |
结算字典暂时不可用,请稍后重试 | 字典服务失败、空响应或无启用项 |
584106 |
确认状态非法 | 服务层收到非 UNCONFIRMED / CONFIRMED;公开接口通常先由参数校验返回 400 |
业务边界
- PUT 是完整字段保存,不能只传
settlementConfirmStatus;请求体中的其他必填字段必须一起提交。 - 传
UNCONFIRMED会把该记录保存为未确认,不会被后端改回CONFIRMED。 - 编辑旧自由文本项目类别时,必须改为 7 个启用
dictValue之一。 - 修改项目名称或金额会同步更新关联订单增费,但不改变确认状态的“按请求保存”规则。
示例
典型成功:从未确认改为已确认
请求:
PUT /v3/admin/order/60001/settlement/other-incomes/99002
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"incomeDate": "2026-07-29",
"projectName": "其他收入",
"projectCategory": "OTHER",
"specification": null,
"quantity": 1,
"unitPrice": 88.00,
"settlementAmount": 88.00,
"paymentMethod": "COMPANY_PAID",
"settlementConfirmStatus": "CONFIRMED",
"voucherUrls": [],
"remark": "已复核"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "99002",
"requestId": "oi-20260729-0002",
"incomeDate": "2026-07-29",
"projectName": "其他收入",
"projectCategory": "OTHER",
"projectCategoryName": "其他",
"specification": null,
"quantity": 1,
"unitPrice": 88.00,
"settlementAmount": 88.00,
"paymentMethod": "COMPANY_PAID",
"paymentMethodName": "公司付款",
"voucherUrls": [],
"settlementConfirmStatus": "CONFIRMED",
"settlementConfirmStatusName": "已确认",
"remark": "已复核",
"sourceType": "ORDER_SURCHARGE",
"sourceTypeName": "订单增费",
"sourceId": "88002"
},
"traceId": "d4e5f6a7-b8c9-0123",
"success": true
}
边界成功:主动改回未确认
请求:
PUT /v3/admin/order/60001/settlement/other-incomes/99001
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"incomeDate": "2026-07-29",
"projectName": "临时加收门票",
"projectCategory": "TICKET",
"specification": "成人票",
"quantity": 2,
"unitPrice": 120.00,
"settlementAmount": 240.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": [],
"remark": "金额待复核"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "99001",
"requestId": "oi-20260729-0001",
"incomeDate": "2026-07-29",
"projectName": "临时加收门票",
"projectCategory": "TICKET",
"projectCategoryName": "门票/游玩项目",
"specification": "成人票",
"quantity": 2,
"unitPrice": 120.00,
"settlementAmount": 240.00,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现付",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": "金额待复核",
"sourceType": "ORDER_SURCHARGE",
"sourceTypeName": "订单增费",
"sourceId": "88001"
},
"traceId": "e5f6a7b8-c9d0-1234",
"success": true
}
业务失败:规格不是启用字典值
请求:
PUT /v3/admin/order/60001/settlement/other-incomes/99001
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"incomeDate": "2026-07-29",
"projectName": "临时加收门票",
"projectCategory": "TICKET",
"specification": "儿童票",
"quantity": 1,
"unitPrice": 80.00,
"settlementAmount": 80.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "CONFIRMED"
}
响应:
{
"code": 584104,
"message": "其他收入票种/规格不在启用字典范围内",
"data": null,
"traceId": "f6a7b8c9-d0e1-2345",
"success": false
}
3.3 新增其他支出
- 方法 + 路径:
POST /v3/admin/order/:orderId/settlement/other-expenses - 接口名:新增其他支出
- 使用场景:在核单其他支出页新增一条记录,并明确保存为未确认或已确认。
- 认证:需要管理后台登录态;需要资金写入权限。
- 幂等性:否。
- 限流:无接口专属限流。
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long | 是 | 订单 ID,必须大于 0 |
请求体
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
expenseType |
String | 是 | 支出类型 | FUEL / TOLL / PARKING / RENTAL / MAINTENANCE / OTHER |
projectName |
String | 是 | 项目名称 | 非空,最长 200 字符 |
expenseDate |
String(date) | 否 | 发生日期,格式 YYYY-MM-DD |
可空 |
actualAmount |
Decimal | 是 | 实际金额 | >= 0,最多 8 位整数、2 位小数 |
paymentMethod |
String | 是 | 付款类型 | CASH_PAID / COMPANY_PAID / SIGNED |
settlementConfirmStatus |
String | 是 | 本次保存的确认状态 | UNCONFIRMED / CONFIRMED |
voucherUrls |
String[] | 否 | 凭证 URL 列表 | 最多 9 项;每项非空、最长 1024 字符;必须是 http/https |
remark |
String | 否 | 备注 | 最长 512 字符 |
响应 Result<SettlementOtherExpenseRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 业务状态码,成功为 200 |
message |
String | 响应消息,成功为“成功” |
data.id |
String | 其他支出 ID |
data.expenseType |
String | 支出类型 |
data.projectName |
String | 项目名称 |
data.expenseDate |
String(date) / null | 发生日期 |
data.actualAmount |
String | 实际金额,按字符串返回 |
data.paymentMethod |
String | 付款类型 |
data.voucherUrls |
String[] | 凭证 URL 列表 |
data.settlementConfirmStatus |
String | 实际保存的确认状态,与请求一致 |
data.remark |
String / null | 备注 |
traceId |
String / null | 链路追踪 ID |
success |
Boolean | code=200 时为 true |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 必填缺失、字段长度超限、确认状态/支出类型/付款类型格式非法、金额或凭证格式非法 |
584086 |
无权修改核单资金数据 | 当前账号不是允许写入资金数据的角色 |
584089 |
核单或结算已完成,资金数据不可再修改 | 订单资金数据已锁定 |
584095 |
其他支出字段超出允许范围 | 支出类型、项目名、金额或备注超出业务允许范围 |
584096 |
付款类型不合法 | paymentMethod 不是允许值 |
584097 |
凭证 URL 格式或数量不合法 | 凭证数量、协议或单项长度不合法 |
584106 |
确认状态非法 | 服务层收到非 UNCONFIRMED / CONFIRMED;公开接口通常先由参数校验返回 400 |
584307 |
当前核单状态不允许写入餐食或其他支出 | 订单核单状态不允许写入 |
业务边界
settlementConfirmStatus必须显式提交;不传不会默认成CONFIRMED。actualAmount=0.00允许保存。expenseDate可为null。- 传
UNCONFIRMED时记录保存成功,但提交核单前仍需通过 PUT 改为CONFIRMED。
示例
典型成功:保存为未确认
请求:
POST /v3/admin/order/60001/settlement/other-expenses
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "TOLL",
"projectName": "过路费",
"expenseDate": "2026-07-29",
"actualAmount": 50.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": ["https://cdn.example.com/vouchers/toll.jpg"],
"remark": "金额待复核"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "801",
"expenseType": "TOLL",
"projectName": "过路费",
"expenseDate": "2026-07-29",
"actualAmount": "50.00",
"paymentMethod": "CASH_PAID",
"voucherUrls": ["https://cdn.example.com/vouchers/toll.jpg"],
"settlementConfirmStatus": "UNCONFIRMED",
"remark": "金额待复核"
},
"traceId": "07b8c9d0-e1f2-3456",
"success": true
}
边界成功:0 元、无日期、保存为已确认
请求:
POST /v3/admin/order/60001/settlement/other-expenses
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "OTHER",
"projectName": "0元备注支出",
"expenseDate": null,
"actualAmount": 0.00,
"paymentMethod": "SIGNED",
"settlementConfirmStatus": "CONFIRMED",
"voucherUrls": [],
"remark": null
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "802",
"expenseType": "OTHER",
"projectName": "0元备注支出",
"expenseDate": null,
"actualAmount": "0.00",
"paymentMethod": "SIGNED",
"voucherUrls": [],
"settlementConfirmStatus": "CONFIRMED",
"remark": null
},
"traceId": "18c9d0e1-f2a3-4567",
"success": true
}
参数失败:未提交确认状态
请求:
POST /v3/admin/order/60001/settlement/other-expenses
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "PARKING",
"projectName": "停车费",
"actualAmount": 30.00,
"paymentMethod": "CASH_PAID"
}
响应:
{
"code": 400,
"message": "确认状态不能为空",
"data": null,
"traceId": "29d0e1f2-a3b4-5678",
"success": false
}
3.4 修改其他支出
- 方法 + 路径:
PUT /v3/admin/order/:orderId/settlement/other-expenses/:settlementId - 接口名:修改其他支出
- 使用场景:修改已有其他支出,或明确切换该记录的确认状态。
- 认证:需要管理后台登录态;需要资金写入权限。
- 幂等性:否。
- 限流:无接口专属限流。
路径参数
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
orderId |
Long | 是 | 订单 ID,必须大于 0 |
settlementId |
Long | 是 | 其他支出 ID,必须大于 0 |
请求体
| 字段 | 类型 | 必填 | 说明 | 校验规则 |
|---|---|---|---|---|
expenseType |
String | 是 | 支出类型 | FUEL / TOLL / PARKING / RENTAL / MAINTENANCE / OTHER |
projectName |
String | 是 | 项目名称 | 非空,最长 200 字符 |
expenseDate |
String(date) | 否 | 发生日期,格式 YYYY-MM-DD |
可空 |
actualAmount |
Decimal | 是 | 实际金额 | >= 0,最多 8 位整数、2 位小数 |
paymentMethod |
String | 是 | 付款类型 | CASH_PAID / COMPANY_PAID / SIGNED |
settlementConfirmStatus |
String | 是 | 本次保存的确认状态 | UNCONFIRMED / CONFIRMED |
voucherUrls |
String[] | 否 | 凭证 URL 列表 | 最多 9 项;每项非空、最长 1024 字符;必须是 http/https |
remark |
String | 否 | 备注 | 最长 512 字符 |
响应 Result<SettlementOtherExpenseRespVO>
| 字段 | 类型 | 说明 |
|---|---|---|
code |
Integer | 业务状态码,成功为 200 |
message |
String | 响应消息,成功为“成功” |
data.id |
String | 其他支出 ID |
data.expenseType |
String | 支出类型 |
data.projectName |
String | 项目名称 |
data.expenseDate |
String(date) / null | 发生日期 |
data.actualAmount |
String | 实际金额,按字符串返回 |
data.paymentMethod |
String | 付款类型 |
data.voucherUrls |
String[] | 凭证 URL 列表 |
data.settlementConfirmStatus |
String | 实际保存的确认状态,与请求一致 |
data.remark |
String / null | 备注 |
traceId |
String / null | 链路追踪 ID |
success |
Boolean | code=200 时为 true |
错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 必填缺失、字段长度超限、确认状态/支出类型/付款类型格式非法、金额或凭证格式非法 |
584086 |
无权修改核单资金数据 | 当前账号不是允许写入资金数据的角色 |
584089 |
核单或结算已完成,资金数据不可再修改 | 订单资金数据已锁定 |
584091 |
其他支出不存在或不属于当前订单 | settlementId 不存在或不属于 orderId |
584095 |
其他支出字段超出允许范围 | 支出类型、项目名、金额或备注超出业务允许范围 |
584096 |
付款类型不合法 | paymentMethod 不是允许值 |
584097 |
凭证 URL 格式或数量不合法 | 凭证数量、协议或单项长度不合法 |
584106 |
确认状态非法 | 服务层收到非 UNCONFIRMED / CONFIRMED;公开接口通常先由参数校验返回 400 |
584307 |
当前核单状态不允许写入餐食或其他支出 | 订单核单状态不允许写入 |
业务边界
- PUT 是完整字段保存,不能只传
settlementConfirmStatus。 - 请求传
UNCONFIRMED时,原先已确认的记录也会按本次请求保存为未确认。 actualAmount在响应中是字符串,不是 JSON 数字。- 不存在或不属于当前订单的
settlementId返回584091。
示例
典型成功:修改并确认
请求:
PUT /v3/admin/order/60001/settlement/other-expenses/801
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "PARKING",
"projectName": "停车费",
"expenseDate": "2026-07-29",
"actualAmount": 35.00,
"paymentMethod": "COMPANY_PAID",
"settlementConfirmStatus": "CONFIRMED",
"voucherUrls": ["https://cdn.example.com/vouchers/parking.jpg"],
"remark": "已复核"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "801",
"expenseType": "PARKING",
"projectName": "停车费",
"expenseDate": "2026-07-29",
"actualAmount": "35.00",
"paymentMethod": "COMPANY_PAID",
"voucherUrls": ["https://cdn.example.com/vouchers/parking.jpg"],
"settlementConfirmStatus": "CONFIRMED",
"remark": "已复核"
},
"traceId": "3ae1f2a3-b4c5-6789",
"success": true
}
边界成功:改回未确认
请求:
PUT /v3/admin/order/60001/settlement/other-expenses/801
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "PARKING",
"projectName": "停车费",
"expenseDate": "2026-07-29",
"actualAmount": 35.00,
"paymentMethod": "COMPANY_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": [],
"remark": "凭证待补"
}
响应:
{
"code": 200,
"message": "成功",
"data": {
"id": "801",
"expenseType": "PARKING",
"projectName": "停车费",
"expenseDate": "2026-07-29",
"actualAmount": "35.00",
"paymentMethod": "COMPANY_PAID",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"remark": "凭证待补"
},
"traceId": "4bf2a3b4-c5d6-7890",
"success": true
}
业务失败:其他支出不存在
请求:
PUT /v3/admin/order/60001/settlement/other-expenses/99999
Authorization: Bearer <admin-token>
Content-Type: application/json
{
"expenseType": "OTHER",
"projectName": "不存在记录",
"actualAmount": 10.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "CONFIRMED"
}
响应:
{
"code": 584091,
"message": "其他支出不存在或不属于当前订单",
"data": null,
"traceId": "5ca3b4c5-d6e7-8901",
"success": false
}
6. 枚举 / 数据字典
6.1 settlementConfirmStatus
所属字段:四个接口请求和响应的 settlementConfirmStatus | 类型:String | 必填:是
| 值 | 中文 | 说明 |
|---|---|---|
UNCONFIRMED |
未确认 | 保存成功但仍阻塞核单提交,需要后续 PUT 改为已确认 |
CONFIRMED |
已确认 | 保存成功且该记录确认状态为已确认 |
6.2 settlement_other_income_project_category
所属字段:其他收入请求 projectCategory、响应 projectCategory/projectCategoryName
dictValue |
dictLabel |
说明 |
|---|---|---|
HOTEL |
住宿 | 住宿类其他收入 |
TICKET |
门票/游玩项目 | 门票或游玩项目类其他收入 |
MEAL |
餐食 | 餐食类其他收入 |
VEHICLE |
车辆 | 车辆类其他收入 |
GUIDE |
导游 | 导游类其他收入 |
PHOTOGRAPHER |
摄影 | 摄影类其他收入 |
OTHER |
其他 | 以上类别以外的其他收入 |
加载接口:
GET /admin/dict/data/settlement_other_income_project_category
展示 dictLabel,保存提交 dictValue。
6.3 settlement_ticket_spec
所属字段:其他收入请求/响应 specification | 类型:String | 必填:否
dictValue |
dictLabel |
说明 |
|---|---|---|
成人票 |
成人票 | 当前启用票种/规格 |
加载接口:
GET /admin/dict/data/settlement_ticket_spec
后续增加启用项时,接口会返回新项;保存时提交选中项的 dictValue。
6.4 paymentMethod
所属字段:四个接口请求/响应 paymentMethod
| 值 | 中文 |
|---|---|
CASH_PAID |
现付 |
COMPANY_PAID |
公司付款 |
SIGNED |
签单 |
6.5 expenseType
所属字段:其他支出请求/响应 expenseType
| 值 | 中文 |
|---|---|
FUEL |
油费 |
TOLL |
过路费 |
PARKING |
停车费 |
RENTAL |
租赁费 |
MAINTENANCE |
维修保养费 |
OTHER |
其他 |
6.6 sourceType
所属字段:其他收入响应 sourceType
| 值 | 中文 |
|---|---|
ORDER_SURCHARGE |
订单增费 |
验证证据
- merge commit
7477b03963中的SettlementControllerTest覆盖四个保存接口显式接收UNCONFIRMED/CONFIRMED,以及缺失或非法确认状态返回参数错误。 SettlementDictValueValidatorTest覆盖项目类别、可选票种规格、确认状态、禁用字典项、 空字典响应及 Feign 异常。- hl-user-service 的迁移结构测试与 MySQL 迁移测试覆盖 7 个项目类别值、固定 ID、 重复执行和冲突失败。
- changelog consumer gate、文件名校验、frontmatter 校验及仓库 46 项自动化测试均已通过。
- 本次没有新增或修改 Gateway 路由,
gateway_status=not_required。
9. 业务边界
- 四个接口都必须显式提交
settlementConfirmStatus,且后端按请求值保存。 UNCONFIRMED记录可以保存,但提交核单时仍会被未确认门禁拦截。- #5310 删除的
POST /v3/admin/order/:orderId/settlement/other-incomes/confirm和POST /v3/admin/order/:orderId/settlement/other-expenses/confirm不恢复;确认状态通过对应 PUT 完整保存。 - 其他收入
projectCategory只接受 7 个启用项目类别值。 - 其他收入
specification可空;非空时只接受settlement_ticket_spec启用值。 - 字典不可用时其他收入 POST/PUT 失败,不会绕过校验写入。
10. 修改前后对比
10.1 字段级对比
| 字段 | 改前 | 改后 |
|---|---|---|
settlementConfirmStatus(四个请求) |
无该请求字段;保存后强制 CONFIRMED |
必填,UNCONFIRMED / CONFIRMED,按请求保存 |
projectCategory(其他收入) |
非空自由文本,最长 64 字符 | 必须是 settlement_other_income_project_category 启用 dictValue |
projectCategoryName(其他收入响应) |
通常与 projectCategory 原值相同 |
返回项目类别 dictLabel,缺失时回退原值 |
specification(其他收入) |
可选自由文本,最长 100 字符 | 可空;非空时必须是 settlement_ticket_spec 启用 dictValue |
10.2 行为级对比
| 行为 | 改前 | 改后 |
|---|---|---|
| 新增/修改其他收入 | 保存即确认 | 调用方决定保存为未确认或已确认 |
| 新增/修改其他支出 | 保存即确认 | 调用方决定保存为未确认或已确认 |
| 切换确认状态 | 保存接口总是写 CONFIRMED |
PUT 完整保存时传目标状态 |
| 其他收入项目类别 | 可提交自由文本 | 按启用字典强校验 |
| 其他收入规格 | 可提交自由文本 | 非空时按票种规格启用字典强校验 |
| 独立确认接口 | #5310 已删除 | 仍保持删除,不恢复 |
11. 影响评估
- 是否破坏向后兼容:是。四个请求新增必填字段;其他收入自由文本项目类别及非字典规格不再接受。
- 前端是否必须同步上线:是。调用四个接口时必须提交
settlementConfirmStatus;其他收入必须改用项目类别和票种规格字典值。 - 前端读取兼容:响应结构不新增字段,但
projectCategoryName从原始值改为字典中文标签。
12. 注意事项
- 不能沿用“保存成功就一定已确认”的前端判断;以响应
settlementConfirmStatus为准。 - PUT 是完整保存,不是确认状态局部更新;切换状态时必须提交该接口全部必填字段。
- 其他收入项目类别展示中文
dictLabel,请求提交英文dictValue。 - 票种/规格继续使用
settlement_ticket_spec,不要使用项目类别字典填充。 - 旧记录如果保存了自由文本
projectCategory或非字典specification,再次编辑时必须先转换为当前启用字典值。
13. 关联 / 联系人
13.1 链接
- Issue: #5320
- PR: #5330
- Merge commit: 7477b03963
- 前序变更: #5310 / #5313
13.2 联系人
- 后端负责人: @yst