hl-api-changelog/changelogs-v2/2026-08/06_5599_核单餐食餐厅下拉值独立存储-修改接口-管理后台.md
API Changelog Bot 0e911a98f3 chore(changelog): 回填 #5599/#5567/#5633 部署实测状态(未部署即推送整改)+5 条枚举/结构合规
- #5599/#5567/#5633(yst 8-06~8-07 推送时未部署测试服): 2026-08-10 复核确认代码已随 order-v3 8-10 部署上测试服,补验证证据章节(部署点位+DB 列+网关实测),backend_status 回填 deployed/gateway verified
- #5444、#5730-5732: backend_status released→deployed(枚举合法化,状态语义不变)
- #5784 补验证证据章节、#5788 章节结构规范化、#5797 清理 not_required 残留前端字段

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-10 16:37:20 +08:00

15 KiB

schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, verified_at, target_release, updated_at, base, status_note
schema ticket title consumer author change_type backend_status gateway_status frontend_status frontend_owner frontend_ref verified_at target_release updated_at base status_note
hl-changelog/v2 5599 核单餐食餐厅下拉值独立存储 admin yst 修改接口 deployed verified implemented mmg hl-admin@01bd3875046cd32e9497e2f00a9eb0f782a197dd 2026-08-10 dev-v3 PR #5600 已合并 dev-v3;2026-08-10 复核确认测试服已部署并验证见文末验证证据章节。restaurantId 关联资源服务餐厅下拉接口 PR #5583

核单餐食餐厅下拉值独立存储(不覆盖餐食名称)

  • 变更类型:修改接口
  • 端类型:管理后台
  • 服务hl-order-service-v3
  • 日期2026-08-06

1. 接口背景

核单「餐食」Tab 原来只有手动录入的 mealName(餐食名称)一个文本字段承载餐食/餐厅信息。业务上需要把「从餐厅资源库选择的餐厅」作为独立结构化数据存下来,便于后续按餐厅维度统计与对账,且不能覆盖运营手动填写的餐食名称。

本次变更:餐食费用的「新增 / 修改 / 回显」三个接口同时新增 restaurantId + restaurantName 两个字段,与 mealName 完全独立、互不联动。

2. 变更清单

# 接口 变更 说明
1 POST /v3/admin/order/{orderId}/settlement/meals 入参 +2 字段 新增 restaurantId / restaurantName成对、非必填
2 PUT /v3/admin/order/{orderId}/settlement/meals/{settlementId} 入参 +2 字段 新增 restaurantId / restaurantName成对、非必填
3 GET /v3/admin/order/{orderId}/settlement/meals 出参 +2 字段 回显新增 restaurantId / restaurantName

无删除字段、无改名字段、无枚举值变化。

3. 接口详情

3.1 新增餐食费用

  • 方法/路径:POST /v3/admin/order/{orderId}/settlement/meals
  • 接口名:新增餐食费用
  • 认证:管理后台 JWT网关统一鉴权
  • 幂等性:非幂等(重复提交会产生多条餐食记录)
  • 限流:走网关默认限流,无接口级特殊限流

3.2 修改餐食费用

  • 方法/路径:PUT /v3/admin/order/{orderId}/settlement/meals/{settlementId}
  • 接口名:修改餐食费用
  • 认证:管理后台 JWT网关统一鉴权
  • 幂等性:幂等(相同 body 重复 PUT 结果一致)
  • 限流:走网关默认限流

3.3 查询餐食费用(回显)

  • 方法/路径:GET /v3/admin/order/{orderId}/settlement/meals
  • 接口名:查询餐食费用
  • 认证:管理后台 JWT网关统一鉴权
  • 幂等性:只读接口
  • 限流:走网关默认限流

4. 接口入参

4.1 路径参数

参数 位置 类型 必填 说明
orderId path Long 订单ID三个接口均有
settlementId path Long 餐食费用记录ID仅 PUT 修改接口)

无 Query 参数。

4.2 请求体字段POST / PUT 共用同一请求结构)

字段 类型 必填 说明
mealType String 餐型BREAKFAST / LUNCH / DINNER
mealDate Dateyyyy-MM-dd 发生日期
mealName String 餐食名称,手动录入,最长 200 字符;不受餐厅选择影响、不被覆盖
quantity Integer 数量,1-10000
unitPrice Number 单价,≥ 0,最多 8 位整数 + 2 位小数;unitPrice × quantity ≤ 99999999.99
paymentMethod String 付款类型CASH_PAID / COMPANY_PAID / SIGNED
sourceType String 来源类型MEAL_ASSIGNMENT / MANUAL / SYSTEM
settlementConfirmStatus String 确认状态UNCONFIRMED / CONFIRMED
voucherUrls String[] 凭证 URL 列表,最多 9 个,仅支持 http/https,单个最长 1024 字符
restaurantId Long 本次新增。餐厅资源ID,与 restaurantName 成对出现;不选餐厅则两者都不传
restaurantName String 本次新增。餐厅名称快照,与 restaurantId 成对出现,最长 200 字符
remark String 备注,最长 512 字符

成对校验(关键业务规则)restaurantIdrestaurantName 必须同时传或同时不传restaurantName 为空白字符串视为未传)。只传其一 → 400,message 为「restaurantId 与 restaurantName 必须成对出现」或「餐食费用字段超出允许范围」(错误码 584094,MEAL_EXPENSE_REQUEST_INVALID。两字段与手动录入的 mealName 完全独立、不联动——选了餐厅也不会改动/覆盖 mealName。

5. 出参字段

  • POST / PUT 响应:Result<SettlementMealRespVO>(单条记录)
  • GET 响应:Result<List<SettlementMealRespVO>>(列表,元素结构相同)

SettlementMealRespVO 字段表:

字段 类型 说明
id String 餐食费用ID,雪花ID序列化为字符串
mealType String 餐型
mealDate Dateyyyy-MM-dd 发生日期
mealName String 餐食名称
restaurantId String | null 本次新增。餐厅资源ID,雪花ID序列化为字符串防 JS 精度丢失);未选餐厅为 null
restaurantName String | null 本次新增。餐厅名称快照(选中时记录,餐厅改名/删除不影响已存核单记录);未选餐厅为 null
quantity Integer 数量
unitPrice String 单价BigDecimal 序列化为字符串)
actualAmount String 实际金额 = unitPrice × quantityBigDecimal 序列化为字符串)
paymentMethod String 付款类型
sourceType String 来源类型
sourceTypeName String 来源类型名称
voucherUrls String[] 凭证 URL 列表
settlementConfirmStatus String 确认状态
settlementConfirmStatusName String 确认状态名称
remark String 备注

外层为统一响应结构:{ "code": 200, "message": "...", "data": ..., "traceId": ..., "success": ... };业务失败时 code 为错误码、data 为 null。

6. 枚举 / 数据字典

本次无新增、无变更枚举。以下现有枚举取值保持不变:

字段 取值 说明
mealType BREAKFAST / LUNCH / DINNER 早餐 / 午餐 / 晚餐
paymentMethod CASH_PAID / COMPANY_PAID / SIGNED 现金垫付 / 公司支付 / 签单
sourceType MEAL_ASSIGNMENT / MANUAL / SYSTEM 餐食安排 / 手动录入 / 系统生成
settlementConfirmStatus UNCONFIRMED / CONFIRMED 未确认 / 已确认

restaurantId 的取值来源:资源服务「餐厅资源下拉选项」接口 GET /admin/resource-options/restaurantsPR #5583 已上线)返回的 resourceId。餐厅选项是业务数据,不是枚举/字典。

7. 错误码

HTTP 错误码 message 触发条件
400 584094 餐食费用字段超出允许范围 restaurantId 与 restaurantName 只传其一不成对,service 层抛 MEAL_EXPENSE_REQUEST_INVALID
400 (参数校验失败) restaurantId 与 restaurantName 必须成对出现 同上场景,bean validation 层先行拦截时的提示文案
400 (参数校验失败) mealType 必须是 BREAKFAST / LUNCH / DINNER 之一 等 既有字段校验失败(本次不变)
401 - 未登录 / token 失效 未携带或携带无效的管理后台 JWT

8. 示例

8.1 典型成功:选择餐厅保存 + 回显

请求:

POST /v3/admin/order/1951234567890123456/settlement/meals
Authorization: Bearer {adminToken}
Content-Type: application/json
{
  "mealType": "LUNCH",
  "mealDate": "2026-08-10",
  "mealName": "团队桌餐10人标",
  "quantity": 10,
  "unitPrice": 68.00,
  "paymentMethod": "COMPANY_PAID",
  "sourceType": "MANUAL",
  "settlementConfirmStatus": "UNCONFIRMED",
  "voucherUrls": ["https://oss.example.com/voucher/a1.jpg"],
  "restaurantId": 1889900112233445566,
  "restaurantName": "海拉尔XX手把肉餐厅",
  "remark": "导游现场确认"
}

响应200

{
  "code": 200,
  "message": "成功",
  "data": {
    "id": "1952345678901234567",
    "mealType": "LUNCH",
    "mealDate": "2026-08-10",
    "mealName": "团队桌餐10人标",
    "restaurantId": "1889900112233445566",
    "restaurantName": "海拉尔XX手把肉餐厅",
    "quantity": 10,
    "unitPrice": "68.00",
    "actualAmount": "680.00",
    "paymentMethod": "COMPANY_PAID",
    "sourceType": "MANUAL",
    "sourceTypeName": "手动录入",
    "voucherUrls": ["https://oss.example.com/voucher/a1.jpg"],
    "settlementConfirmStatus": "UNCONFIRMED",
    "settlementConfirmStatusName": "未确认",
    "remark": "导游现场确认"
  }
}

回显:GET /v3/admin/order/1951234567890123456/settlement/meals 返回 data 为列表,元素结构同上(含 restaurantId / restaurantName

8.2 边界:不选餐厅,纯手动录入(两字段都不传)

请求:

POST /v3/admin/order/1951234567890123456/settlement/meals
Authorization: Bearer {adminToken}
Content-Type: application/json
{
  "mealType": "BREAKFAST",
  "mealDate": "2026-08-11",
  "mealName": "酒店自助早餐",
  "quantity": 10,
  "unitPrice": 0,
  "paymentMethod": "SIGNED",
  "sourceType": "MANUAL",
  "settlementConfirmStatus": "CONFIRMED"
}

响应200datarestaurantId: nullrestaurantName: null,其余字段正常填充。存量历史餐食记录回显同样两字段为 null。

8.3 业务失败:只传 restaurantId、缺 restaurantName不成对

请求:

{
  "mealType": "DINNER",
  "mealName": "涮羊肉",
  "quantity": 10,
  "unitPrice": 88.00,
  "paymentMethod": "CASH_PAID",
  "sourceType": "MANUAL",
  "settlementConfirmStatus": "UNCONFIRMED",
  "restaurantId": 1889900112233445566
}

响应400

{
  "code": 584094,
  "message": "餐食费用字段超出允许范围",
  "data": null
}

(若 bean validation 层先行拦截,message 为「restaurantId 与 restaurantName 必须成对出现」。反向场景——只传 restaurantName 不传 restaurantId——同样 400。

9. 业务边界

适用

  • 核单「餐食」Tab 新增 / 编辑费用时,从餐厅资源下拉中选择餐厅,结构化保存 restaurantId + restaurantName
  • 运营纯手动录入餐食(不关联餐厅):两字段都不传即可,行为与本次变更前完全一致

不适用

  • restaurantId 只接受「餐厅资源下拉选项」接口返回的 resourceId,不接受其他类型资源ID
  • 已提交核单(终态)订单不可再改餐食费用(核单提交守卫为既有逻辑,本次不变)

特殊边界

  • 快照语义restaurantName 是选中那一刻的名称快照。之后餐厅在资源库改名 / 删除,不影响已存核单记录的 restaurantName
  • 与 mealName 完全独立:选了餐厅也不会改动 / 覆盖 mealName;mealName 仍是必填手动录入字段
  • 存量数据:历史餐食费用记录两字段均为 null,回显正常、无需迁移

10. 修改前后对比

字段级对比

接口 维度 修改前 修改后
POST / PUT 保存 入参 无餐厅字段 +restaurantIdLong,可选,成对+restaurantNameString,最长 200,可选,成对
GET 回显 出参 无餐厅字段 +restaurantIdString,可 null+restaurantNameString,可 null
mealName 语义 餐食/餐厅信息只能挤在这一个文本字段里 仍为必填手动录入;餐厅信息改由独立字段承载,不再占用 mealName

行为级对比

场景 修改前 修改后
保存餐食费用 想记餐厅只能写进 mealName 文本 可结构化传 restaurantId + restaurantName,mealName 不被覆盖
回显 无法区分「餐食名称」和「餐厅」 两类信息独立字段回显,前端可分开展示
入参校验 无成对概念 restaurantId / restaurantName 不成对 → 400

11. 影响评估 / 回滚

  • 破坏性兼容:无。两个字段均为可选新增;存量请求不传两字段时行为完全不变;存量数据两列为 NULL
  • 前端同步上线:不强制同步。前端未接新字段前,保存 / 回显行为与之前一致(回显多两个 null 字段,不读即可);前端接新字段需等后端部署后联调
  • 后端部署状态PR #5600 已合并 dev-v3,测试服已部署2026-08-10 复核确认,见文末验证证据章节)
  • 回滚方案:后端回滚 = 下线两字段即可。回滚时前端需同步停止传这两字段;存量数据两列本就为 NULL 或历史快照值,无数据迁移、无回滚 SQL

12. 注意事项

  1. restaurantId 出参是 String(雪花 ID 序列化),前端不要用 Number 解析,防 JS 精度丢失
  2. unitPrice / actualAmount 出参同样是 StringBigDecimal 序列化),金额展示直接渲染字符串即可
  3. 编辑时「清掉已选餐厅」= 两字段都不传(或都传 null,保持成对空态
  4. restaurantName 是快照不是实时关联,餐厅后续改名 / 删除不影响历史核单记录显示,属预期
  5. 餐厅下拉数据源接口 GET /admin/resource-options/restaurants 属资源服务PR #5583,不在本变更范围内
  6. 本变更只涉及接口契约;后端代码已合并 dev-v3,测试服部署时间以后端通知为准

13. 关联 / 联系人

  • Issuewx/HL#5599
  • PRwx/HL#5600
  • Commit9884585a29
  • 关联依赖:餐厅资源下拉接口 PR #5583资源服务,已上线
  • 后端负责人yst腰苏图

验证证据2026-08-10 复核回填,wx

本条 changelog 2026-08-06 推送时后端尚未部署测试服(原 status_note 自述),违反「测试服部署+实测后才通知前端」流程。2026-08-10 复核补齐部署与验证证据如下:

  • 测试服 hl-order-service-v3 运行版本为 2026-08-10 15:10 构建dev-v3,晚于 PR #5600 合并点2026-08-06 14:56,本变更代码已在运行实例中。
  • 测试服 DB hl_order_service_v3.order_settlement_meal 已存在 restaurant_id / restaurant_name 两列Flyway 已执行)。
  • SettlementMealRespVOrestaurantId / restaurantName 字段dev-v3 源码核对)。
  • GET /v3/admin/order/{orderId}/settlement/meals 经网关 9443 + 真 admin token 实测返回 code=200空列表订单;测试库现有餐食记录的订单因核单数据权限581008 无权查看此订单)未能以 SUPER_ADMIN 直接观测到带值行,字段存在性以 DB 列 + VO 源码 + 部署点位三重证据确认。