hl-api-changelog/changelogs-v2/2026-07/29_5320_核单其他收支确认状态与项目类别-修改接口-管理后台.md
Mimingguang 3851666575
一些检查失败了
changelog-filename-gate / validate (push) Failing after 1s
chore(changelog): 回写核单接口消费结果
修改原因:#5310、#5320、#5342、#5343 已由管理后台完成消费并通过最终验证。

修改内容:将四份 changelog 标记为 implemented,记录 v2.1 业务提交、目标版本、验证时间和最终契约说明。

实际验证:业务仓库 pnpm checkpoint 全部通过;业务提交 1444fc7f0bf34efaec0ee9f775f7d529b609b847 已推送 origin/v2.1。

Changelog:changelogs-v2/2026-07/28_5310_*;changelogs-v2/2026-07/29_5320_*、5342_*、5343_*。
2026-07-29 20:57:05 +08:00

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 implemented pi:019fadb7-dac9-74bf-9581-058835251208 1444fc7f0bf34efaec0ee9f775f7d529b609b847 v2.1 2026-07-29T20:43:00+08:00 管理后台已接入其他收入、其他支出新增编辑,显式提交确认状态,并按项目类别与票种规格启用字典提交 dictValue;pnpm checkpoint 全部通过。 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,调用方传 UNCONFIRMEDCONFIRMED,响应返回实际保存值。

其他收入同时收紧项目类别和票种/规格:

  • 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
  • 接口名:修改其他收入;金额或项目名变化时原子冲销并重建订单增费
  • 使用场景:修改已有其他收入,或把记录的确认状态在 UNCONFIRMEDCONFIRMED 之间明确切换。
  • 认证:需要管理后台登录态;需要资金写入权限。
  • 幂等性:否。
  • 限流:无接口专属限流。

路径参数

字段 类型 必填 说明
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/confirmPOST /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 链接

13.2 联系人

  • 后端负责人: @yst