23 KiB
schema, ticket, title, consumer, author, 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 | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | 8125 | 用餐第几天按订单出发日算,套用模版改为在现有行后面追加 | admin | lc(GIT) | 修改接口 | deployed | verified | verified | mmg | 74eacd7a992d276e438b2e9f55b38e3924d5fe0c | v2.1 | 2026-09-21 | 订单用餐行的第几天改为后端按「用餐日期 − 订单出发日期 + 1」重算:整单保存时新增的行和改了用餐日期的行都会跟着变,前端不要再自己算或沿用旧值。保存为模版的第几日同口径(不再按选中那批行里最早的日期算第 1 日)。套用模版的返回由「只有模版行」改为「订单现有行 + 模版行(模版行在后)」,前端仍把返回的行原样整单提交,原有行按行ID保留、模版行按新增落库,页面上表现为在现有数据后面追加。 前端 2026-09-21 已交付(hl-admin v2.1 74eacd7a9):MealTab 套用改只读预览——只把回包 mealInfoId=null 的模版行追加进本地列表(不清空/不只提交模版行),保存走整单提交入库;MealTemplateApplyModal 加双模糊搜索/创建人/桌人列/删除模版;checkpoint 全量绿。(与 #8093 合并一个交付单元;dayNumber 前端不自算,沿后端返回直显) | 2026-09-21 | dev-v3 |
order-v3: 用餐第几天按订单出发日算,套用模版改为在现有行后面追加
服务: hl-order-service-v3 PR: #8126 Issue: #8125 日期: 2026-09-21 影响范围: 管理后台订单详情「用餐」页签的整单保存、保存为模版、套用模版
⚠️ 关键变化
- 🔁 套用模版的返回内容变了(破坏性):
POST /v3/admin/order/meal-template/apply现在返回订单现有的用餐行 + 模版套出来的行,模版行排在现有行后面。#8093 的行为是只返回模版行,前端原样提交保存会把原有行全部软删;现在原有行带着行ID一起返回,原样提交后它们按行ID保留,页面上就是「在现有数据后面套用模版」。前端不需要改提交方式,仍是把返回的items原样提交整单保存,但如果前端自己做过「套用前先清空列表」或「只提交模版行」的处理,必须去掉。 - 🔁 套用不去重、不覆盖:模版里那一日那一餐订单已经有行时,两行并存,原有行不动。
- 🔁 第几天
dayNumber由后端按订单出发日重算:第几天 = 用餐日期 − 订单出发日期 + 1,出发日当天是第 1 天。整单保存时,新增的行和改了用餐日期的行都会重算后落库,前端提交的dayNumber不参与(入参本来也没有这个字段)。页面上的「D2」就是dayNumber: 2加个 D 前缀,后端不返回带 D 的字符串。 - ℹ️
dayNumber可能是 0 或负数:用餐日期填在订单出发日之前时按公式照算,不拦截。订单没有出发日期时为null。 - 🔁 保存为模版的第几日同口径:
POST /v3/admin/order/meal-template/save存下的dayNumber改为按「该行用餐日期 − 所属订单出发日期 + 1」算,不再按选中那批行里最早的用餐日期算第 1 日。套用时仍落到目标订单出发日 + d − 1。 - ℹ️ 存量数据不订正:历史用餐行里与新口径对不上的
dayNumber不做数据迁移,页面对那张订单再保存一次即自动算对。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 套用用餐模版 | POST | /v3/admin/order/meal-template/apply |
修改 | 返回由「只有模版行」改为「订单现有行 + 模版行」,模版行在后;totalAmount 为全部行之和 |
| 2 | 保存用餐信息(整单保存) | POST | /v3/admin/order/meal-info/save |
修改 | 入参出参字段不变;保存后 dayNumber 按订单出发日重算 |
| 3 | 保存为用餐模版 | POST | /v3/admin/order/meal-template/save |
修改 | 入参出参字段不变;模版行 dayNumber 改为按订单出发日算 |
查询用餐信息列表 GET /v3/admin/order/meal-info/list 的入参、出参字段一个没变,只是返回的 dayNumber 取值随保存口径变化。3.2 生成、3.4 重置、模版查询与删除的入参出参和行为完全不变。错误码没有新增或删除。
三、接口详情
1. 套用用餐模版 POST /v3/admin/order/meal-template/apply
VO: MealTemplateApplyReqVO → OrderMealInfoListRespVO
使用场景
订单详情「用餐」页签点「套用模版」:把某个模版的内容按订单出发日铺开,接在页面现有数据后面展示给用户预览;用户确认后再点保存。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 是 | 雪花 ID | 订单 ID(不变) |
| templateId | Body | String | 是 | 雪花 ID | 模版 ID(不变) |
请求示例
{
"orderId": "2102035335653556226",
"templateId": "2102035945748684802"
}
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| items[] | Array | 内容变化:前段是该订单现有的用餐行(mealInfoId 非空,逐字段与列表接口一致,金额不重算),后段是模版套出来的行(mealInfoId 为 null)。两段各自有序,不混排 |
| items[].dayNumber | Integer | 模版行为模版里的第几日;现有行为库里存的第几天 |
| items[].mealDate | String | 模版行 = 订单出发日 + 第几日 − 1;现有行为它自己的用餐日期 |
| items[].priceUnit | String | person / table,由桌数是否为 0 派生,不存库。现有行也会带这个字段 |
| totalAmount | BigDecimal | 口径变化:返回全部行(现有行 + 模版行)金额之和 |
| 其余字段 | — | 与 #8093 相同 |
模版行的人数、桌数、价钱、桌/人、金额仍由后端算好(免人数 / 免金额 / 其他成本为 0),前端直接展示。
响应示例
订单出发日 2026-10-11,页面上已有 3 行,模版有第 2 日午餐、第 8 日晚餐:
{
"code": 200,
"success": true,
"data": {
"orderId": "2102035335653556226",
"departDate": "2026-10-11",
"returnDate": "2026-10-13",
"personCount": 4,
"orderEditable": true,
"generated": true,
"totalAmount": 1940.00,
"items": [
{
"mealInfoId": "2102036...001",
"mealType": "LUNCH", "dayNumber": 2, "mealDate": "2026-10-12",
"restaurantName": "HL8125-TEST-B单午餐厅", "dishName": "HL8125-TEST-B单午餐",
"unitPrice": 45.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 180.00,
"settleType": "sign", "settleTypeName": "签单"
},
{
"mealInfoId": "2102036...002",
"mealType": "DINNER", "dayNumber": 2, "mealDate": "2026-10-12",
"restaurantName": "HL8125-TEST-B单晚餐厅", "dishName": "HL8125-TEST-B单晚宴",
"unitPrice": 588.00, "tableCount": 2, "personCount": 0, "priceUnit": "table",
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 1176.00,
"settleType": "cash", "settleTypeName": "现付"
},
{
"mealInfoId": "2102036...003",
"mealType": "BREAKFAST", "dayNumber": 3, "mealDate": "2026-10-13",
"restaurantName": "HL8125-TEST-B单早餐厅", "dishName": "HL8125-TEST-B单早餐",
"unitPrice": 25.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 100.00,
"settleType": "company", "settleTypeName": "公司付款"
},
{
"mealInfoId": null,
"mealType": "LUNCH", "dayNumber": 2, "mealDate": "2026-10-12",
"restaurantName": "HL8125-TEST-七间房全羊馆", "dishName": "HL8125-TEST-午市套餐",
"unitPrice": 60.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 240.00,
"settleType": "sign", "settleTypeName": "签单"
},
{
"mealInfoId": null,
"mealType": "DINNER", "dayNumber": 8, "mealDate": "2026-10-18",
"restaurantName": "HL8125-TEST-卢布里西餐厅", "dishName": "HL8125-TEST-晚宴套餐",
"unitPrice": 61.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 244.00,
"settleType": "cash", "settleTypeName": "现付"
}
]
}
}
第 1 行和第 4 行是同一天同一餐(2026-10-12 午餐):模版那一行照样追加,原有行不被覆盖也不被去掉。
空数据 / 降级响应
- 模版一行都没有:只返回订单现有行,
code200。 - 订单一行都没有:只返回模版套出来的行。
- 订单没有出发日期:无法对天,只返回订单现有行(#8093 时返回空),
code200,message带 589602 文案。 - 现有行 + 模版行超过 200 条时按 200 截断。
错误响应
{ "code": 589607, "message": "订单已完成或已取消,不能修改用餐信息", "success": false, "data": null }
与 #8093 一致:订单已完成或已取消 589607;订单不可写 581045 / 581008;订单不存在按订单模块现有错误码。本单没有新增或删除错误码。
业务边界
- 调用套用仍不改库:调用前后用列表接口按该订单查询,返回逐字段完全一致。
- 前端保存时把返回的
items原样提交POST /v3/admin/order/meal-info/save:带行ID的原有行被保留,mealInfoId为null的模版行按新增落库。priceUnit是只读字段,提交上来后端会忽略。
2. 保存用餐信息(整单保存) POST /v3/admin/order/meal-info/save
VO: OrderMealInfoBatchSaveReqVO → OrderMealInfoListRespVO
使用场景
订单详情「用餐」页签点「保存」:把页面上这张订单应有的全部行一次提交。带行ID的整行覆盖、不带行ID的新增、原来有而这次没传上来的软删。套用模版后的保存走的也是这个接口。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 是 | 雪花 ID | 订单 ID(不变) |
| groupBatchId | Body | String | 否 | 雪花 ID | 团期 ID(不变) |
| items[] | Body | Array | 是 | — | 这张订单保存后应有的全部行(不变) |
| items[].mealInfoId | Body | String | 否 | 雪花 ID | 传了更新这一行,不传新增(不变) |
| items[].mealDate | Body | String | 是 | yyyy-MM-dd |
用餐日期。第几天由它和订单出发日期算出来 |
| items[] 其余字段 | Body | — | — | — | mealType、restaurantId/Name、dishId/Code/Name、unitPrice、tableCount、personCount、freePersonCount、freeAmount、otherCost、amount、settleType、settleTypeName 全部不变 |
入参没有 dayNumber,改动前后都没有;第几天一律由后端算。
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| items[].dayNumber | Integer | 取值口径变化:用餐日期 − 订单出发日期 + 1,出发日当天为 1;用餐日期早于出发日时为 0 或负数;订单没有出发日期时为 null |
| 其余字段 | — | 与改动前完全一致 |
请求示例
{
"orderId": "2102035302317228034",
"items": [
{
"mealType": "LUNCH", "mealDate": "2026-10-02",
"restaurantName": "HL8125-TEST-七间房全羊馆", "dishName": "HL8125-TEST-午市套餐",
"unitPrice": 60.00, "tableCount": 0, "personCount": 4,
"freePersonCount": 0, "freeAmount": 0, "otherCost": 0, "amount": 240.00,
"settleType": "sign", "settleTypeName": "签单"
}
]
}
响应示例
订单出发日 2026-10-01,上面这行用餐日期是 2026-10-02:
{
"code": 200,
"success": true,
"data": {
"orderId": "2102035302317228034",
"departDate": "2026-10-01",
"personCount": 4,
"totalAmount": 240.00,
"items": [
{
"mealInfoId": "2102036...010",
"mealType": "LUNCH", "dayNumber": 2, "mealDate": "2026-10-02",
"restaurantName": "HL8125-TEST-七间房全羊馆", "dishName": "HL8125-TEST-午市套餐",
"unitPrice": 60.00, "tableCount": 0, "personCount": 4,
"freePersonCount": 0, "freeAmount": 0.00, "otherCost": 0.00, "amount": 240.00,
"settleType": "sign", "settleTypeName": "签单"
}
]
}
}
把这一行的 mealDate 改成 2026-10-05 再提交,同一行的 dayNumber 变成 5;改成 2026-10-08(超出返回日期)变成 8,接口不报错。
空数据 / 降级响应
items传空数组:这张订单的行全部软删,返回items: []、totalAmount: 0.00,code200。- 订单没有出发日期:照常保存,行上
dayNumber为null。
错误响应
{ "code": 589613, "message": "金额与明细不一致", "success": false, "data": null }
589606(行查不到或已删除)、589611(行挂的不是这张订单)、589601(团期不一致)、589607(订单已完成或已取消)与改动前一致。
业务边界
- 第几天只在保存时算一次并落库,查询按库里存的值返回。
- 前端提交的行里即使带了
dayNumber也不会被采纳(入参没有这个字段)。 - 用餐日期早于订单出发日期不拦截,得到 0 或负数。
- 历史行里与新口径对不上的
dayNumber不会被批量订正,对那张订单再保存一次即自动算对。
3. 保存为用餐模版 POST /v3/admin/order/meal-template/save
VO: MealTemplateSaveReqVO → MealTemplateRespVO
使用场景
「用餐」页签上排好几天的用餐后点「保存为模版」,把选中的行存成一个可复用的模版。模版只存第几日,不存用餐日期。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| templateName | Body | String | 是 | trim 后 1–500 字符 | 模版名,允许重名(不变) |
| mealInfoIds | Body | Array | 是 | 非空,服务端去重 | 要存进模版的用餐信息行ID(不变) |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| items[].dayNumber | Integer | 算法变化:该行用餐日期 − 所属订单出发日期 + 1,不再按选中那批行里最早的用餐日期算第 1 日 |
| 其余字段 | — | templateId、templateName、creatorName 与行上其余字段均不变 |
请求示例
{
"templateName": "HL8125-TEST-D几模版",
"mealInfoIds": ["2102036...010", "2102036...011"]
}
响应示例
源订单出发日 2026-10-01,两行用餐日期分别是 2026-10-02 和 2026-10-08:
{
"code": 200,
"success": true,
"data": {
"templateId": "2102035945748684802",
"templateName": "HL8125-TEST-D几模版",
"creatorName": "刘畅",
"items": [
{
"dayNumber": 2, "mealType": "LUNCH",
"restaurantName": "HL8125-TEST-七间房全羊馆", "dishName": "HL8125-TEST-午市套餐",
"unitPrice": 60.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"settleType": "sign", "settleTypeName": "签单"
},
{
"dayNumber": 8, "mealType": "DINNER",
"restaurantName": "HL8125-TEST-卢布里西餐厅", "dishName": "HL8125-TEST-晚宴套餐",
"unitPrice": 61.00, "tableCount": 0, "personCount": 4, "priceUnit": "person",
"settleType": "cash", "settleTypeName": "现付"
}
]
}
}
改动前这两行会存成第 1 日和第 7 日(按选中行里最早的 10-02 当第 1 日)。
空数据 / 降级响应
- 源行所属订单查不到或没有出发日期:该行回落到旧口径(这批行里最早的用餐日期算第 1 日),接口照常成功。
- 一行都没取到:返回 589606。
错误响应
{ "code": 400, "message": "第2日午餐重复", "success": false, "data": null }
同一日同一餐出现多条时返回参数错误并指出是哪一日哪一餐,整批不保存;这一条与改动前一致,只是「第几日」的算法变了。
业务边界
- 每次保存都生成新的模版ID,不覆盖已有模版。
- 行上原本存着的第几天不再参与,一律按订单出发日重新算。
- 算出来的第几日可能是 0 或负数(源行用餐日期早于订单出发日),套用时同样落到目标订单出发日 + d − 1。
四、契约约束与正确调用方式
- 套用流程不变,但不要再清空列表:点「套用模版」→ 调 apply 拿到「现有行 + 模版行」→ 在页面上整体展示(可让用户继续改)→ 用户点「保存」→ 把这些行原样提交整单保存。返回里已经包含原有行,前端不需要(也不应该)自己拼接或先清空。
- 提交整单保存时,带
mealInfoId的行必须原样带上,否则那些原有行会被当成「没传上来」而软删。 priceUnit是只读派生字段,提交时带上会被后端忽略。- 第几天不要在前端算:直接用后端返回的
dayNumber。页面上的「D2」是dayNumber: 2加 D 前缀。 - 用户在页面上改了某行的用餐日期后,第几天要等这次保存的返回(或保存后的列表)才刷新,前端如需即时显示可按同一公式本地预览,但以后端返回为准。
五、数据库行为
只写前端可观察到的行为,不涉及表结构细节。
- 套用模版仍然不产生任何写入:调用前后按该订单查询,返回逐字段完全一致;不新增、不覆盖、不软删任何行。
- 整单保存时第几天被重算并落库:新增的行和改了用餐日期的行都会写入新的第几天;这是本次唯一新增的写入行为,不涉及新表或新列。
- 保存为模版仍是新增一个模版,不改动源用餐行;只是存下的第几日算法变了。
- 存量数据不做迁移:历史行里对不上的第几天保持原样,直到那张订单下次整单保存。
六、边界行为
- 用餐日期早于订单出发日期:第几天为 0 或负数,不拦截。
- 用餐日期超出订单返回日期:照算(如第 8 天),不拦截。
- 订单没有出发日期:保存的行第几天为
null;套用时只返回现有行并带提示文案。 - 模版第 d 日超出目标订单行程天数:照落在出发日 + d − 1。
- 套用返回的现有行 + 模版行超过 200 条:按 200 截断。
- 模版里那一日那一餐订单已经有行:两行并存,不去重、不覆盖。
六.6、修改前后对比
字段级对比
| 字段 | 改动前 | 改动后 |
|---|---|---|
meal-info/save 出参 items[].dayNumber |
新增行为 null;改日期不变 |
按「用餐日期 − 订单出发日期 + 1」重算 |
meal-template/save 出参 items[].dayNumber |
按选中行里最早的用餐日期算第 1 日 | 按该行所属订单的出发日期算 |
meal-template/apply 出参 items[] |
只有模版行(mealInfoId 全为 null) |
订单现有行(带行ID)+ 模版行,模版行在后 |
meal-template/apply 出参 totalAmount |
模版行金额之和 | 返回全部行金额之和 |
行为级对比
| 项 | 改动前 | 改动后 |
|---|---|---|
| 页面上「第几天」列 | 手动加的行是空的,改日期不跟着变 | 一直等于用餐日期相对订单出发日的天数 |
| 套用模版的页面效果 | 保存后原有数据被清掉,只剩模版内容 | 在现有数据后面追加模版内容 |
| 同一日同一餐重合 | 原有行被整单保存软删 | 两行并存 |
| 订单没有出发日期时套用 | 返回空列表 | 返回订单现有行 |
六.7、影响评估
- 是否破坏向后兼容: 是。apply 的返回内容变化,沿用「返回即全部模版行」的假设会重复展示或误删
- 前端是否必须同步上线: 是。必须去掉「套用前清空列表」「只提交模版行」「前端自己算第几天」这三类处理
- 前端 workaround 清理点: 前端本地计算
dayNumber的代码;套用后手工拼接原有行的代码
七、不影响范围
- 仅影响: 订单用餐信息的整单保存、保存为用餐模版、套用用餐模版
- 零影响:
- 查询用餐信息列表、生成、重置的入参出参与行为(返回的第几天取值随保存口径变化)
- 查询用餐模版、删除用餐模版
- 金额公式、订单门禁、错误码
- 订单、团期、结算、房务、车务等其他模块
八、测试环境已验证
部署提交 d30cd9561,Deploy Panel 任务 41c56ef7,经 Gateway 用真实 TEST 身份(SUPER_ADMIN)由固定脚本实测 19 项全部通过:
A 单出发 2026-10-01,新增一行用餐日期 2026-10-02 → dayNumber=2 ✓
同一行日期改成 2026-10-05 → dayNumber=5 ✓
日期改回 2026-10-02 → dayNumber=2 ✓
A 单两行 10-02 / 10-08 存为模版 → 模版第几日 2 和 8(不是 1 和 7)✓
模版行的餐厅 / 餐食 / 价钱 / 付款方式 → 与源用餐行一致 ✓
模版套到 B 单(出发 2026-10-11) → 落在 2026-10-12(D2) 与 2026-10-18(D8) ✓
套出来的两行按人算好 → 人数=订单人数 4、金额 240.00 / 244.00、priceUnit=person ✓
套用返回结构 → 前 3 行带行ID(B 单现有行)、后 2 行行ID为空 ✓
前段 3 行与套用前按订单查询 → 逐字段一致 ✓
totalAmount → 1940.00 = 180+1176+100+240+244 ✓
套用零写入 → 调用前后整份列表 JSON 逐字段一致 ✓
同一日同一餐重合(2026-10-12 午餐) → 两行并存,原有行未被覆盖 ✓
把套用返回的 5 行原样提交整单保存 → 共 5 行,原 3 行行ID与内容不变 ✓
模版套出来的 2 行 → 成为新行,10-12/D2 与 10-18/D8 ✓
保存后合计 → 仍为 1940.00 ✓
回归:用户样本订单 2101846199160188929(10 行) → 与部署前读数逐行逐字段一致 ✓
回归:部署前已有的 7 个模版 → 第几日仍是旧口径原值,未被批量改写 ✓
十、相关文档
- 关联 Issue: wx/HL#8125
- 关联 PR: wx/HL#8126
- 前置变更: #8093 套用用餐模版改为只读返回组合结果
关联 / 联系人
链接
- Issue: #8125
- PR: wx/HL#8126
- Merge commit: d30cd9561
联系人
- 后端: @lc