修改原因:其他收支日期控件与列宽在联调反馈后继续收敛,需要让 source 指向最新完整交付。 修改内容:将 #5477 frontend_ref 更新为 hl-admin@9f46fa040a77cd899bbc9c21b9b8304a518499e2。 实际验证:日期控件提交通过定向 Vitest,列宽调整通过 pnpm checkpoint 和生产构建,业务提交已推送 origin/v2.1。
21 KiB
schema, ticket, title, consumer, change_type, author, 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 | author | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 5477 | 核单其他收入移除项目类别与票种规格字段 | admin | 修改接口 | yaosutu(GIT) | deployed | verified | implemented | pi-main-session | hl-admin@9f46fa040a77cd899bbc9c21b9b8304a518499e2 | 2026-08-04 | PR #5488 已合并 dev-v3;测试服真实网关已验证 POST 无旧字段成功、PUT 多传旧字段被忽略、GET/POST/PUT 响应均不含 projectCategory/projectCategoryName/specification。前端需删除项目类别与票种规格控件及相关字段读写。 | 2026-08-04 | dev-v3 |
【⚠️ 修改接口·管理后台】核单其他收入移除项目类别与票种规格字段(#5477)
PR: #5488 | 服务: hl-order-service-v3 | 更新时间: 2026-08-04 16:30
1. 接口背景
“其他收入”页签本身已表达项目类型,继续要求填写“项目类别”属于重复信息;“票种/规格”也不适用于其他收入。此次统一收口新增、修改和查询契约,前端应删除这两个控件及相关字段读写。
2. 变更清单
| # | 接口 | 方法 | 路径 | 变更类型 | 前端动作 |
|---|---|---|---|---|---|
| 1 | 查询其他收入核单明细及增减费汇总 | GET | /v3/admin/order/{orderId}/settlement/other-incomes |
删除出参字段 | 停止读取 projectCategory、projectCategoryName、specification |
| 2 | 新增其他收入并原子创建订单增费 | POST | /v3/admin/order/{orderId}/settlement/other-incomes |
删除入参、出参字段 | 删除项目类别与票种规格控件;停止传两个旧入参 |
| 3 | 修改其他收入 | PUT | /v3/admin/order/{orderId}/settlement/other-incomes/{incomeId} |
删除入参、出参字段 | 删除项目类别与票种规格控件;停止传两个旧入参 |
DELETE 接口签名与返回结构未变化,不属于本次契约变更。
3. 接口详情
3.1 查询其他收入核单明细及增减费汇总
- 方法/路径:
GET /v3/admin/order/{orderId}/settlement/other-incomes - 使用场景:进入其他收入页签,以及新增、修改、删除后刷新明细和汇总。
- 认证:需要管理后台登录态;配房角色不可访问。
- 幂等性:只读接口,可安全重复调用。
- 限流:未声明接口专属限流。
路径参数:
| 字段 | 类型 | 必填 | 说明 | 校验 |
|---|---|---|---|---|
orderId |
String(Long) | 是 | 订单 ID | 必须大于 0 |
请求体:无。
成功响应 data:
| 字段 | 类型 | 可空 | 说明 |
|---|---|---|---|
items |
OtherIncomeItem[] |
否 | 其他收入明细;无数据返回 [] |
deductions |
Deduction[] |
否 | 只读减费明细;无数据返回 [] |
summary |
Summary |
否 | 当前有效增减费汇总 |
OtherIncomeItem 完整字段:
| 字段 | 类型 | 可空 | 说明 |
|---|---|---|---|
id |
String(Long) | 否 | 其他收入 ID |
requestId |
String | 是 | 手工新增幂等请求 ID;自动投影时为空 |
incomeDate |
LocalDate | 否 | 收入日期,YYYY-MM-DD |
projectName |
String | 否 | 项目名称 |
quantity |
Decimal | 否 | 数量 |
unitPrice |
Decimal | 否 | 核算单价 |
settlementAmount |
Decimal | 否 | 核算金额 |
paymentMethod |
String | 否 | 付款类型,见 §6.1 |
paymentMethodName |
String | 否 | 付款类型中文名 |
voucherUrls |
String[] | 是 | 凭证 URL 列表 |
settlementConfirmStatus |
String | 否 | 确认状态,见 §6.2 |
settlementConfirmStatusName |
String | 否 | 确认状态中文名 |
remark |
String | 是 | 备注 |
sourceType |
String | 否 | 来源类型,见 §6.3 |
sourceTypeName |
String | 否 | 来源类型中文名 |
sourceId |
String(Long) | 是 | 来源附加费 ID |
Deduction 完整字段:
| 字段 | 类型 | 可空 | 说明 |
|---|---|---|---|
id |
String(Long) | 否 | 减费 ID |
discountName |
String | 否 | 减费名称 |
discountAmount |
Decimal | 否 | 减费金额 |
sourceType |
String | 是 | 来源类型 |
sourceId |
String(Long) | 是 | 来源业务 ID |
createdAt |
LocalDateTime | 是 | 创建时间 |
Summary 完整字段:
| 字段 | 类型 | 说明 |
|---|---|---|
surchargeAmount |
Decimal | 有效增费合计 |
discountAmount |
Decimal | 有效减费合计 |
netAdjustmentAmount |
Decimal | 净调整额,即增费减去减费 |
本接口错误码与业务边界:见 §7、§9;查询不允许再依赖三个已删除字段。
典型成功示例:
GET /v3/admin/order/2084000000000002978/settlement/other-incomes
Authorization: Bearer <token>
{
"code": 200,
"message": "success",
"data": {
"items": [{
"id": "2084000000000004978",
"requestId": "oi-5477-0001",
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差",
"quantity": 2,
"unitPrice": 12.34,
"settlementAmount": 24.68,
"paymentMethod": "COMPANY_PAID",
"paymentMethodName": "公司付款",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": null,
"sourceType": "MANUAL",
"sourceTypeName": "手工",
"sourceId": "2084000000000004979"
}],
"deductions": [],
"summary": {
"surchargeAmount": 24.68,
"discountAmount": 0,
"netAdjustmentAmount": 24.68
}
},
"success": true
}
边界示例(空列表):
GET /v3/admin/order/2084000000000002978/settlement/other-incomes
Authorization: Bearer <token>
{
"code": 200,
"message": "success",
"data": {
"items": [],
"deductions": [],
"summary": {"surchargeAmount": 0, "discountAmount": 0, "netAdjustmentAmount": 0}
},
"success": true
}
异常示例(非法订单 ID):
GET /v3/admin/order/0/settlement/other-incomes
Authorization: Bearer <token>
{"code": 400, "message": "订单 ID 必须大于 0", "data": null, "success": false}
3.2 新增其他收入并原子创建订单增费
- 方法/路径:
POST /v3/admin/order/{orderId}/settlement/other-incomes - 使用场景:核单人员新增一条手工其他收入。
- 认证:需要管理后台登录态;仅超级管理员、管理员或财务可写。
- 幂等性:
requestId是同一订单内永久唯一的稳定幂等键;同一requestId与相同载荷重试返回同一结果,不同载荷冲突返回584087。 - 限流:未声明接口专属限流。
路径参数:orderId,String(Long),必填且必须大于 0。
请求体完整字段:
| 字段 | 类型 | 必填 | 校验与说明 |
|---|---|---|---|
requestId |
String | 是 | 最长 64;同订单内永久唯一 |
incomeDate |
LocalDate | 是 | YYYY-MM-DD |
projectName |
String | 是 | 非空,最长 100 |
quantity |
Decimal | 是 | ≥0,最多 8 位整数、4 位小数 |
unitPrice |
Decimal | 是 | ≥0,最多 8 位整数、2 位小数 |
settlementAmount |
Decimal | 是 | ≥0.01,最多 8 位整数、2 位小数;必须等于 quantity × unitPrice 四舍五入到 2 位 |
paymentMethod |
String | 是 | 见 §6.1 |
settlementConfirmStatus |
String | 是 | 新增只能为 UNCONFIRMED |
voucherUrls |
String[] | 否 | 最多 9 项;每项最长 1024,必须为 http/https URL |
remark |
String | 否 | 最长 500 |
成功响应:data 为 §3.1 的完整 OtherIncomeItem;不含 projectCategory、projectCategoryName、specification。
典型成功请求与响应:
POST /v3/admin/order/2084000000000002978/settlement/other-incomes
Authorization: Bearer <token>
Content-Type: application/json
{
"requestId": "oi-5477-0001",
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差",
"quantity": 2,
"unitPrice": 12.34,
"settlementAmount": 24.68,
"paymentMethod": "COMPANY_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": [],
"remark": null
}
{
"code": 200,
"message": "success",
"data": {
"id": "2084000000000004978",
"requestId": "oi-5477-0001",
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差",
"quantity": 2,
"unitPrice": 12.34,
"settlementAmount": 24.68,
"paymentMethod": "COMPANY_PAID",
"paymentMethodName": "公司付款",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": null,
"sourceType": "MANUAL",
"sourceTypeName": "手工",
"sourceId": "2084000000000004979"
},
"success": true
}
边界请求与响应(旧字段仍被旧客户端多传):
{
"requestId": "oi-5477-legacy-0001",
"incomeDate": "2026-08-04",
"projectName": "历史客户端补差",
"projectCategory": "HOTEL",
"specification": "VIP",
"quantity": 1,
"unitPrice": 0.01,
"settlementAmount": 0.01,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": []
}
{
"code": 200,
"message": "success",
"data": {
"id": "2084000000000004980",
"requestId": "oi-5477-legacy-0001",
"incomeDate": "2026-08-04",
"projectName": "历史客户端补差",
"quantity": 1,
"unitPrice": 0.01,
"settlementAmount": 0.01,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现付",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": null,
"sourceType": "MANUAL",
"sourceTypeName": "手工",
"sourceId": "2084000000000004981"
},
"success": true
}
旧字段会被忽略,响应不会回显。该兼容仅用于过渡;前端仍必须停止发送。
异常请求与响应(缺少确认状态):
{
"requestId": "oi-5477-invalid-0001",
"incomeDate": "2026-08-04",
"projectName": "无确认状态",
"quantity": 1,
"unitPrice": 10,
"settlementAmount": 10,
"paymentMethod": "CASH_PAID"
}
{"code": 400, "message": "确认状态不能为空", "data": null, "success": false}
3.3 修改其他收入
- 方法/路径:
PUT /v3/admin/order/{orderId}/settlement/other-incomes/{incomeId} - 使用场景:修改已有其他收入的公开业务字段。
- 认证:需要管理后台登录态;仅超级管理员、管理员或财务可写。
- 幂等性:无单独幂等键;重复提交相同最终载荷不会改变公开结果。调用方不得依赖并发请求顺序。
- 限流:未声明接口专属限流。
路径参数:
| 字段 | 类型 | 必填 | 说明 | 校验 |
|---|---|---|---|---|
orderId |
String(Long) | 是 | 订单 ID | 必须大于 0 |
incomeId |
String(Long) | 是 | 其他收入 ID | 必须大于 0,且必须属于该订单 |
请求体完整字段:与 POST 相同,但没有 requestId;settlementConfirmStatus 可为 UNCONFIRMED 或 CONFIRMED。字段长度、金额一致性、凭证约束均与 §3.2 相同。
成功响应:data 为 §3.1 的完整 OtherIncomeItem;不含 projectCategory、projectCategoryName、specification。
典型成功请求与响应:
PUT /v3/admin/order/2084000000000002978/settlement/other-incomes/2084000000000004978
Authorization: Bearer <token>
Content-Type: application/json
{
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差-已更新",
"quantity": 3,
"unitPrice": 15.00,
"settlementAmount": 45.00,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": [],
"remark": "金额已核对"
}
{
"code": 200,
"message": "success",
"data": {
"id": "2084000000000004978",
"requestId": "oi-5477-0001",
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差-已更新",
"quantity": 3,
"unitPrice": 15,
"settlementAmount": 45,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现付",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": "金额已核对",
"sourceType": "MANUAL",
"sourceTypeName": "手工",
"sourceId": "2084000000000004982"
},
"success": true
}
边界请求与响应(夹带旧字段):请求可额外包含 projectCategory、specification,服务端会忽略,仍按上述公开字段更新,响应不会出现三个旧字段。
{
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差-兼容请求",
"projectCategory": "OLD_JSON_CATEGORY_SHOULD_BE_IGNORED",
"specification": "OLD_JSON_SPEC_SHOULD_BE_IGNORED",
"quantity": 3,
"unitPrice": 15,
"settlementAmount": 45,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED",
"voucherUrls": []
}
{
"code": 200,
"message": "success",
"data": {
"id": "2084000000000004978",
"requestId": "oi-5477-0001",
"incomeDate": "2026-08-04",
"projectName": "酒店升级补差-兼容请求",
"quantity": 3,
"unitPrice": 15,
"settlementAmount": 45,
"paymentMethod": "CASH_PAID",
"paymentMethodName": "现付",
"voucherUrls": [],
"settlementConfirmStatus": "UNCONFIRMED",
"settlementConfirmStatusName": "未确认",
"remark": null,
"sourceType": "MANUAL",
"sourceTypeName": "手工",
"sourceId": "2084000000000004982"
},
"success": true
}
异常请求与响应(金额不一致):
{
"incomeDate": "2026-08-04",
"projectName": "金额错误",
"quantity": 3,
"unitPrice": 15,
"settlementAmount": 44,
"paymentMethod": "CASH_PAID",
"settlementConfirmStatus": "UNCONFIRMED"
}
{"code": 584076, "message": "其他收入核算金额必须等于数量乘以核算单价", "data": null, "success": false}
4. 接口入参汇总
| 接口 | 路径参数 | 请求体差异 | 已删除字段 |
|---|---|---|---|
| GET | orderId |
无 | 无入参;响应删除三个字段 |
| POST | orderId |
比 PUT 多必填 requestId;新增必须从 UNCONFIRMED 开始 |
projectCategory、specification |
| PUT | orderId、incomeId |
无 requestId;确认状态可为两种合法值 |
projectCategory、specification |
完整字段、类型和校验均已在 §3 对应接口内列出。
5. 出参字段汇总
POST、PUT 返回单个 OtherIncomeItem;GET 返回 items[] + deductions[] + summary。OtherIncomeItem 的完整当前字段见 §3.1,三种接口均不再返回:
| 已删除字段 | 原类型 | 当前替代 |
|---|---|---|
projectCategory |
String | 无;前端删除对应状态与控件 |
projectCategoryName |
String | 无;前端删除对应展示读取 |
specification |
String | 无;前端删除对应状态与控件 |
6. 枚举 / 数据字典
6.1 paymentMethod(付款类型)
所属字段:POST/PUT 入参、OtherIncomeItem.paymentMethod | 类型:String | 必填:是
| 值 | 中文 | 说明 |
|---|---|---|
CASH_PAID |
现付 | 已现场支付 |
COMPANY_PAID |
公司付款 | 由公司付款 |
SIGNED |
签单 | 签单结算 |
6.2 settlementConfirmStatus(确认状态)
所属字段:POST/PUT 入参、OtherIncomeItem.settlementConfirmStatus | 类型:String | 必填:是
| 值 | 中文 | 说明 |
|---|---|---|
UNCONFIRMED |
未确认 | POST 新增时唯一允许的初始值 |
CONFIRMED |
已确认 | 仅对已有明细通过 PUT 更新使用 |
6.3 sourceType(公开来源类型)
所属字段:OtherIncomeItem.sourceType | 类型:String | 必填:响应必有
| 值 | 中文 | 说明 |
|---|---|---|
MANUAL |
手工 | 管理后台手工新增 |
SYSTEM |
系统 | 系统投影来源 |
projectCategory 对应的数据字典不再属于其他收入接口契约;前端应删除该字典请求和映射逻辑。
7. 错误码
| code | 含义 | 触发场景 |
|---|---|---|
400 |
参数校验失败 | 缺必填字段、长度/格式超限、非法枚举、订单 ID 非正数等 |
401 |
未登录或登录态失效 | 缺少有效管理后台凭证 |
584073 |
其他收入不存在或不属于当前订单 | PUT 的 incomeId 不存在或订单归属不符 |
584074 |
当前核单状态不允许修改其他收入 | 订单核单状态不是待核单或核单中 |
584075 |
其他收入关联的附加费来源无效 | 关联来源无法建立或已失效 |
584076 |
核算金额不等于数量乘以单价 | quantity × unitPrice 四舍五入到 2 位后与金额不一致 |
584086 |
无权修改核单资金数据 | 写接口调用角色不是超级管理员、管理员或财务 |
584087 |
requestId 已用于另一笔其他收入 |
POST 重用幂等键但载荷不同 |
584088 |
核单凭证数据损坏 | 历史凭证数据无法读取 |
584089 |
核单或结算已完成,资金数据不可修改 | 对完成后的资金事实调用 POST/PUT |
584106 |
确认状态非法 | 非 UNCONFIRMED/CONFIRMED |
584107 |
手工新增必须先保存为未确认 | POST 直接传 CONFIRMED |
旧错误码 584103(项目类别非法)、584104(规格非法)、584105(相关字典不可用)不再由这三个接口触发。
8. 示例索引
三类示例均已与接口放在一起,避免跨节拼接:
| 接口 | 典型成功 | 边界 | 业务失败 |
|---|---|---|---|
| GET | §3.1 有明细 | §3.1 空列表 | §3.1 非法 orderId |
| POST | §3.2 不传旧字段创建 | §3.2 旧请求多传字段被忽略 | §3.2 缺确认状态 |
| PUT | §3.3 正常更新 | §3.3 夹带旧字段被忽略 | §3.3 金额不一致 |
9. 业务边界
- ✅ GET 用于读取当前其他收入、减费与汇总;空数据稳定返回空数组。
- ✅ POST/PUT 仅适用于核单资金仍可修改的订单,且调用角色必须具备资金写权限。
- ✅ POST 必须携带稳定
requestId,并以UNCONFIRMED创建;后续可通过 PUT 改为CONFIRMED。 - ✅
settlementAmount必须等于quantity × unitPrice四舍五入到 2 位。 - ⚠️ 旧客户端继续多传
projectCategory/specification时,新接口会忽略;这不是继续保留控件的理由。 - ❌ 核单或结算完成后禁止 POST/PUT;不存在或跨订单的
incomeId禁止更新。 - ❌ 前端不得从其他字段猜测、拼装或恢复已删除的项目类别与票种规格。
10. 修改前后对比
10.1 字段级对比
| 接口字段 | 原来 | 现在 |
|---|---|---|
POST/PUT projectCategory |
必填 String,受项目类别字典校验 | 已从契约删除;旧 JSON 多传会被忽略 |
POST/PUT specification |
可选 String,受规格字典校验 | 已从契约删除;旧 JSON 多传会被忽略 |
GET/POST/PUT projectCategory |
响应返回 | 不再返回 |
GET/POST/PUT projectCategoryName |
响应返回中文名 | 不再返回 |
GET/POST/PUT specification |
响应返回 | 不再返回 |
10.2 行为级对比
| 行为 | 原来 | 现在 |
|---|---|---|
| 新增/修改表单 | 必须维护项目类别,可选维护票种规格 | 两个控件都删除,只提交当前公开字段 |
| 旧客户端多传旧字段 | 参与校验和保存 | 被忽略,且响应不回显 |
| 查询展示 | 可读取类别、类别名称和规格 | 三个字段不存在,禁止继续读取或设置默认值 |
| 相关字典异常 | 可能阻断写入 | 不再属于其他收入接口错误面 |
11. 影响评估 / 回滚
11.1 影响评估
- 是否破坏向后兼容:响应字段删除属于破坏性变化;但旧前端继续多传两个旧请求字段时,新接口会忽略,因此后端先上线兼容旧请求。
- 前端是否必须同步上线:必须。删除项目类别与票种规格控件、请求字段、响应读取和相关字典依赖。
- 上线顺序边界:允许“新后端 → 旧前端”短暂过渡;不允许“新前端 → 旧后端”,因为旧后端仍要求
projectCategory。
11.2 回滚方案
- 若接口契约回滚到旧版本,必须同步恢复前端
projectCategory必填提交,否则旧接口会拒绝新增/修改。 - 仅回滚前端到旧版本不会阻断新接口写入,但旧页面读取不到三个已删除响应字段,类别/规格区域会为空,因此不建议长期维持。
12. 注意事项
- 删除项目类别控件、票种规格控件及其表单校验。
- 停止在 POST/PUT 请求中传
projectCategory、specification。 - 停止读取 GET/POST/PUT 响应中的
projectCategory、projectCategoryName、specification。 - 删除其他收入页面对项目类别字典、票种规格字典的加载与映射。
- Long ID 继续按 String 消费;空明细继续按
[]处理。
13. 关联 / 联系人
13.1 链接
- Issue: #5477
- PR: #5488
- Merge commit: da1ee4ebc106
13.2 联系人
- 后端负责人: @yaosutu
- QA 验证: Issue #5477 接口验收已完成(管理后台真实网关,TARGETED_FALLBACK)