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 | 8230 | 团期订单支持用餐:生成、保存、重置、查询、套用模版可只传团期ID | admin | lc(GIT) | 修改接口 | deployed | verified | verified | mmg | 04e08d1a9ddb8ee26f5313fb7a08e3c020225475 | v2.1 | 2026-09-24 | 已部署 TEST 并经 Gateway 实测通过。用餐的查询、生成、保存、重置、套用模版 5 个接口加入团期维度:orderId 与 groupBatchId 必须且只能传一个,都传或都不传返回 400。只传 groupBatchId 时读写的是团期自己的用餐行(行上团期ID有值、订单ID为空);按订单读写的行此后团期ID为空,按团期查不到子订单的行。团期的出发/返回日期取团期出团日期 depart_date / 结束日期 end_date。前端结论(2026-09-23):现有子订单用餐页(MealTab)5 个调用点全只传 orderId,查询恒有 orderId 空值守卫,零 batchNo/groupBatchId 读写、零 589601 分支——「挂团订单保存勿再带 groupBatchId」「只传 batchNo 400」等破坏点前端零命中,基本行为零改动。团期维度用餐属新增能力,前端无团期用餐页面,建页为新产品需求待拍板,非本次同步范围。;前端纠正(2026-09-24):09-23 误把团期维度建页判出范围,用户指出后立项——团期详情新增「用餐」Tab(行程后,show:lazy),复用 MealTab 新增 groupBatchId prop 二选一 scope;mealInfo API 4 函数改 scope 对象+normalizeMealScope 前端断言;破坏点维持零命中;spec MealTab 30+mealInfo 5+batch 357+detail 389 全绿,checkpoint 13 项过 | 2026-09-23 | dev-v3 |
order-v3: 团期订单支持用餐,5 个用餐接口可只传团期ID
服务: hl-order-service-v3 PR: #8251 Issue: #8230 日期: 2026-09-23 影响范围: 管理后台用餐信息的查询、生成、保存、重置与套用模版(订单、团期两种维度)
⚠️ 关键变化
- 🔁 二选一(破坏性):查询、生成、保存、重置、套用模版这 5 个接口,
orderId与groupBatchId必须且只能传一个。都传返回400「订单ID和团期ID只能传一个」;都不传返回400「订单ID和团期ID必须传一个」。查询接口的batchNo不算数,只传batchNo视为两个都没传,同样 400。 - 🔁 订单行不再带团期:按订单生成、保存、重置、套用出来的行,行上
groupBatchId、batchNo一律为空。以前挂团订单的行会顺带写团期ID / 团期编号,现在不写。 - 🔁 保存不再用
orderId+groupBatchId一起传:以前保存时可以同时传两者并校验订单是否属于该团期(589601),现在同时传直接 400,589601 不再出现。 - 🆕 团期维度:只传
groupBatchId时读写的是团期自己的用餐行,行上groupBatchId有值、orderId为空;按团期查询只返回团期自己的行,不含团里子订单的行。 - ℹ️ 3.1 用
orderId+batchNo组合此后查不到数据:订单行上已不存团期编号,这个组合固定返回空列表。
二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|---|---|---|---|---|
| 1 | 查询用餐信息列表(3.1) | GET | /v3/admin/order/meal-info/list |
修改 | orderId / groupBatchId 二选一;只传团期ID只返回团期自己的行,顶层汇总按团期填 |
| 2 | 生成用餐信息(3.2) | POST | /v3/admin/order/meal-info/generate |
修改 | 新增 groupBatchId,二选一;团期按团期产品当前行程生成 |
| 3 | 保存用餐信息(整单,3.3) | POST | /v3/admin/order/meal-info/save |
修改 | orderId 不再必填,二选一;只传团期ID保存团期行 |
| 4 | 重置用餐信息(3.4) | POST | /v3/admin/order/meal-info/reset |
修改 | 新增 groupBatchId,二选一;团期删掉自己的行后重新生成 |
| 5 | 套用用餐模版(4.3) | POST | /v3/admin/order/meal-template/apply |
修改 | 新增 groupBatchId,二选一;同一个模版可套到团期 |
出参字段一个都没增减。模版查询、存为模版、删除模版不变(模版不分订单、团期)。没有新增错误码。
三、接口详情
1. 查询用餐信息列表 GET /v3/admin/order/meal-info/list
VO: OrderMealInfoListReqVO → OrderMealInfoListRespVO
使用场景
按订单或按团期取用餐行及顶层汇总。两种维度用同一个接口,由传 orderId 还是 groupBatchId 区分。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Query | String | 二选一 | 正数 | 按订单查。与 groupBatchId 必须且只能传一个 |
| groupBatchId | Query | String | 二选一 | 正数 | 按团期查,只返回团期自己的行 |
| batchNo | Query | String | 否 | — | 不参与二选一;只传它按都没传处理(400);与 orderId 组合固定查不到数据 |
| 其余筛选 | Query | — | 否 | — | mealType、mealDateStart/End、dayNumber、settleType、keyword、limit 不变 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| orderId | String | 按订单查为订单ID;按团期查为 null |
| groupBatchId / batchNo | String | 按订单查:仍回显订单所属团期(不挂团为 null);按团期查:该团期 |
| departDate / returnDate | String | 按订单查:订单出发/返回日期;按团期查:团期出团日期 depart_date / 结束日期 end_date |
| personCount | Integer | 按订单查:订单人数;按团期查:团期报名人数 |
| orderEditable | Boolean | 按团期查恒为 true |
| generated | Boolean | 按团期查:该团期有没有自己的行 |
| items[].orderId / items[].groupBatchId | String | 订单行:orderId 有值、groupBatchId 为 null;团期行:groupBatchId 有值、orderId 为 null |
| items[].batchNo | String | 新写入的行一律为 null |
请求示例
GET /v3/admin/order/meal-info/list?groupBatchId=2101996448340238338
响应示例
{
"code": 200,
"success": true,
"data": {
"orderId": null,
"groupBatchId": "2101996448340238338",
"batchNo": "Q202611042099750379129368577",
"departDate": "2026-11-04",
"returnDate": "2026-11-08",
"personCount": 4,
"orderEditable": true,
"generated": true,
"totalAmount": 0.00,
"items": [
{ "mealInfoId": "…", "orderId": null, "groupBatchId": "2101996448340238338", "batchNo": null,
"mealType": "LUNCH", "mealDate": "2026-11-04", "dayNumber": 1, "amount": 0.00 }
]
}
}
按订单查(订单挂在该团期下)时顶层 groupBatchId 仍是 "2101996448340238338",但 items[].groupBatchId 为 null。
空数据 / 降级响应
- 团期还没有自己的行:
items: []、generated: false,顶层汇总照常填。 - 团期里子订单有用餐行、团期自己没有:按团期查仍是
items: []。 orderId+batchNo:items: []。
错误响应
{ "code": 400, "message": "订单ID和团期ID只能传一个", "success": false, "data": null }
都不传(含只传 batchNo):400「订单ID和团期ID必须传一个」。按团期查没有团期查看权限:589507。订单维度的错误码不变。
业务边界
- 二选一在入参校验阶段判断,先于任何权限、存在性判断。
- 按团期查只按团期ID取行,不含团里子订单的行;按订单查只返回该订单的行。
- 顶层
groupBatchId是订单汇总回显,不代表行上存了团期ID。
2. 生成用餐信息 POST /v3/admin/order/meal-info/generate
VO: OrderMealInfoGenerateReqVO → OrderMealInfoListRespVO
使用场景
订单或团期还没有用餐行时,按产品行程生成空行。团期传 groupBatchId。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 二选一 | 正数 | 按订单产品快照生成(不变) |
| groupBatchId | Body | String | 二选一 | 正数 | 新增。按团期产品当前行程生成团期行 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| 全部字段 | — | 与查询接口同维度的返回一致(团期维度见接口 1) |
| items[].dayNumber | Integer | 团期:第 d 天用餐日期 = depart_date + d − 1,天数到 end_date |
请求示例
{ "groupBatchId": "2101996448340238338" }
响应示例
{
"code": 200,
"success": true,
"data": {
"orderId": null, "groupBatchId": "2101996448340238338",
"departDate": "2026-11-04", "returnDate": "2026-11-08", "personCount": 4, "generated": true,
"items": [
{ "orderId": null, "groupBatchId": "2101996448340238338", "mealType": "LUNCH", "mealDate": "2026-11-04", "dayNumber": 1, "amount": 0.00 },
{ "orderId": null, "groupBatchId": "2101996448340238338", "mealType": "DINNER", "mealDate": "2026-11-04", "dayNumber": 1, "amount": 0.00 }
]
}
}
空数据 / 降级响应
- 团期已有自己的行:不再生成,直接返回现有行(幂等)。
- 团期
depart_date或end_date为空:不生成,code200,message带 589602 文案。 - 行程里含(酒店)/ 自理的餐、行程没有的天不生成;团期产品取不到行程时生成 0 行。
错误响应
{ "code": 400, "message": "订单ID和团期ID必须传一个", "success": false, "data": null }
都传:400「订单ID和团期ID只能传一个」。团期:无团期管理权限 589507、团期不存在 589500、end_date 早于 depart_date 589612。
业务边界
- 团期生成的每天每餐取团期产品当前行程,不是某张子订单下单时的快照。
- 团期不按团期状态拦截。
- 按订单生成的行
groupBatchId为空。
3. 保存用餐信息(整单) POST /v3/admin/order/meal-info/save
VO: OrderMealInfoBatchSaveReqVO → OrderMealInfoListRespVO
使用场景
一次提交某张订单或某个团期保存后应有的全部行:带行ID覆盖、不带新增、原来有而这次没传的软删。团期只传 groupBatchId,不要同时传 orderId。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 二选一 | 正数 | 不再必填。按订单保存 |
| groupBatchId | Body | String | 二选一 | 正数 | 含义变化:以前是「订单所属团期一致校验」,现在只传它表示按团期保存 |
| items[] | Body | Array | 是 | — | 保存后应有的全部行,字段不变;空数组表示一行都不留 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| 全部字段 | — | 与查询接口同维度的返回一致 |
| items[].dayNumber | Integer | 团期行 = 用餐日期 − depart_date + 1;depart_date 为空时为 null |
请求示例
{
"groupBatchId": "2101996448340238338",
"items": [
{ "mealType": "LUNCH", "mealDate": "2026-11-05", "restaurantName": "…", "dishName": "…",
"unitPrice": 45.00, "tableCount": 0, "personCount": 4, "freePersonCount": 0,
"freeAmount": 0, "otherCost": 0, "amount": 180.00, "settleType": "sign", "settleTypeName": "签单" }
]
}
响应示例
{
"code": 200,
"success": true,
"data": {
"orderId": null, "groupBatchId": "2101996448340238338", "departDate": "2026-11-04",
"totalAmount": 180.00,
"items": [
{ "mealInfoId": "…", "orderId": null, "groupBatchId": "2101996448340238338", "batchNo": null,
"mealType": "LUNCH", "mealDate": "2026-11-05", "dayNumber": 2, "amount": 180.00 }
]
}
}
空数据 / 降级响应
- 团期传
items: []:该团期自己的行全部软删,返回items: [],不影响子订单的行。
错误响应
{ "code": 400, "message": "订单ID和团期ID只能传一个", "success": false, "data": null }
都不传:400「订单ID和团期ID必须传一个」。团期:无团期管理权限 589507、团期不存在 589500、带的行ID不是本团期自己的行 589611、行查不到或已删 589606、金额对不上 589613。589601 不再返回。
业务边界
- 团期保存只动团期自己的行,软删范围也只在团期自己的行里。
- 按订单保存新增的行
groupBatchId为空。 - 任一行校验失败整批不写。
4. 重置用餐信息 POST /v3/admin/order/meal-info/reset
VO: OrderMealInfoResetReqVO → OrderMealInfoListRespVO
使用场景
删掉某张订单或某个团期自己的全部行,再按生成规则重新生成空行。团期传 groupBatchId。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 二选一 | 正数 | 按订单重置(不变) |
| groupBatchId | Body | String | 二选一 | 正数 | 新增。按团期重置 |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| 全部字段 | — | 与接口 2 生成的返回一致 |
请求示例
{ "groupBatchId": "2101996448340238338" }
响应示例
{
"code": 200,
"success": true,
"data": {
"orderId": null, "groupBatchId": "2101996448340238338", "generated": true,
"items": [
{ "mealInfoId": "…(新行ID)", "orderId": null, "groupBatchId": "2101996448340238338", "mealType": "LUNCH", "mealDate": "2026-11-04", "dayNumber": 1, "amount": 0.00 }
]
}
}
空数据 / 降级响应
- 团期
depart_date或end_date为空:原有团期行照样删掉、不生成,code200,message带 589602 文案。
错误响应
{ "code": 589612, "message": "返回日期早于出发日期,无法生成用餐信息", "success": false, "data": null }
end_date 早于 depart_date 时原有行不删。二选一 400、589507、589500 同接口 2。
业务边界
- 团期重置只删团期自己的行,子订单的行不动;删和生成一起成功或一起失败。
- 重置后行ID全部换新。
5. 套用用餐模版 POST /v3/admin/order/meal-template/apply
VO: MealTemplateApplyReqVO → OrderMealInfoListRespVO
使用场景
把一个模版按出发日铺开,接在现有行后面返回(只读,不写库),确认后再用接口 3 保存。模版不分订单、团期,同一个模版两边都能套。套到团期传 groupBatchId。
入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|---|---|---|---|---|---|
| orderId | Body | String | 二选一 | 正数 | 不再必填。套到订单 |
| groupBatchId | Body | String | 二选一 | 正数 | 新增。套到团期 |
| templateId | Body | String | 是 | — | 模版ID(不变) |
出参字段表
| 字段 | 类型 | 说明 |
|---|---|---|
| items[] | Array | 前段是现有行(团期:团期自己的行),后段是模版行(mealInfoId 为 null) |
| items[].mealDate | String | 团期:模版第 d 日 = depart_date + d − 1 |
| items[].personCount | Integer | 团期按人的模版行 = 团期报名人数;按桌为 0 |
| items[].orderId / groupBatchId | String | 套到团期:模版行 groupBatchId 有值、orderId 为 null;套到订单:模版行 groupBatchId 为 null |
| 其余字段 | — | 与 #8125 相同 |
请求示例
{ "groupBatchId": "2101996448340238338", "templateId": "2102035945748684802" }
响应示例
{
"code": 200,
"success": true,
"data": {
"orderId": null, "groupBatchId": "2101996448340238338", "departDate": "2026-11-04", "personCount": 4,
"items": [
{ "mealInfoId": "…", "orderId": null, "groupBatchId": "2101996448340238338", "mealType": "LUNCH", "mealDate": "2026-11-05", "dayNumber": 2 },
{ "mealInfoId": null, "orderId": null, "groupBatchId": "2101996448340238338", "mealType": "DINNER", "mealDate": "2026-11-06", "dayNumber": 3, "personCount": 4, "priceUnit": "person" }
]
}
}
空数据 / 降级响应
- 团期
depart_date为空:只返回团期现有行,code200,message带 589602 文案。 - 模版没有行:只返回现有行。
错误响应
{ "code": 400, "message": "订单ID和团期ID只能传一个", "success": false, "data": null }
都不传:400「订单ID和团期ID必须传一个」。团期:无团期管理权限 589507、团期不存在 589500。
业务边界
- 套用不写库;保存时把返回的
items原样用接口 3 提交,套到团期的就只传groupBatchId提交。 - 团期套用的现有行只取团期自己的行,不含子订单的行。
四、契约约束与正确调用方式
| 场景 | payload |
|---|---|
| ✅ 团期生成 / 重置 | { "groupBatchId": "…" } |
| ✅ 团期保存 | { "groupBatchId": "…", "items": [...] } |
| ✅ 团期套用 | { "groupBatchId": "…", "templateId": "…" } |
| ✅ 团期查询 | ?groupBatchId=… |
| ✅ 订单维度 | 只传 orderId,其余同改动前 |
| ❌ 两个都传 | { "orderId": "…", "groupBatchId": "…" } → 400「订单ID和团期ID只能传一个」 |
| ❌ 两个都不传 | {} / 只传 batchNo → 400「订单ID和团期ID必须传一个」 |
- 挂团订单保存时不要再带
groupBatchId(以前可带作一致校验,现在会 400)。 - 团期的出发 / 返回日期、第几天都以后端返回为准(取团期
depart_date/end_date)。
五、数据库行为
- 只传
groupBatchId写入的行:团期ID有值,订单ID、团期编号为空。 - 只传
orderId写入的行:订单ID有值,团期ID、团期编号为空。 - 团期保存、重置的软删范围只在团期自己的行;子订单的行不受影响。
- 套用模版不写库。存量数据无需迁移。
六、边界行为
- 二选一 400 先于权限、存在性判断,库里零写入。
- 团期写(生成 / 保存 / 重置 / 套用)要团期管理权限,查询要团期查看权限,不足
589507;团期不存在589500;不按团期状态拦截。 - 团期
depart_date/end_date为空:生成、重置、套用返回code200,message为「订单出发或返回日期为空,无法生成用餐信息」(沿用 589602 文案),查询照常。 orderId+batchNo查询:固定空列表。
六.6、修改前后对比
字段级对比
| 字段 | 改动前 | 改动后 |
|---|---|---|
3.2 / 3.4 / 4.3 入参 groupBatchId |
无 | 新增,与 orderId 二选一 |
3.3 / 4.3 入参 orderId |
必填 | 与 groupBatchId 二选一 |
3.3 入参 groupBatchId |
与订单所属团期一致校验(589601) | 只传它表示按团期保存;与 orderId 同传 400 |
订单行 items[].groupBatchId / batchNo |
挂团订单有值 | null |
行为级对比
| 行为 | 改动前 | 改动后 |
|---|---|---|
| 3.1 只传团期ID | 连同团里子订单的行一起返回,汇总为 null | 只返回团期自己的行,汇总按团期填 |
| 3.1 都不传 | 查没挂订单也没挂团期的行 | 400 |
3.1 orderId + batchNo |
按订单 + 团期编号筛 | 空列表 |
| 3.3 都传 | 按订单保存 | 400 |
六.7、影响评估
- 是否破坏向后兼容: 是。都传、都不传、只传
batchNo由成功变为 400;订单行不再带团期ID - 前端是否必须同步上线: 是。挂团订单保存不能再带
groupBatchId;团期用餐走只传groupBatchId - 前端 workaround 清理点: 挂团订单保存时附带
groupBatchId的处理;依赖订单行groupBatchId/batchNo判断归属的处理
七、不影响范围
- 仅影响: 上述 5 个用餐接口
- 零影响: 模版查询、存为模版、删除模版;金额公式;订单维度的权限与错误码(589601 除外)
八、测试环境已验证
部署提交 b86e635c7(含合并提交 6ae15b3ed),Deploy Panel 任务 23cffa4a;经 Gateway(https://api.test.1814.love)用真实 TEST 身份(SUPER_ADMIN)由固定脚本实测 73 项全部通过。团期 2101996448340238338(团期编号 Q202611042099750379129368577,出团 2026-11-04、结束 2026-11-08、报名 4 人),子订单 2101996448319266818:
只传 groupBatchId 调 3.2 生成 → 12 行,与团期产品当前行程逐天逐餐一致;第 1 天(自理/含营地/含特色餐)只有午、晚 ✓
生成的行 → groupBatchId=团期、orderId=null、batchNo=null;mealDate=2026-11-04+d−1 ✓
再调 3.2 → 行数、行ID不变(幂等)✓
改一行后调 3.4 重置 → 原行全部软删,重新生成与首次逐行一致 ✓
同一个模版只传 groupBatchId / 只传 orderId 调 4.3 → 两边都能套;团期模版行第 d 日落 2026-11-04+d−1、按人行人数=4、orderId=null ✓
只传 groupBatchId 调 3.3 保存 2 行 → groupBatchId 有值、orderId=null;dayNumber:11-05→2、11-06→3 ✓
再保存改一行、少传一行 → 改的变了(105.00),少传的软删 ✓
子订单按订单保存 1 行后 3.1 按订单查回 → 行上 groupBatchId=null;顶层仍回显所属团期 ✓
只传 groupBatchId 调 3.1 → 查不到子订单那行,只有团期保存的行 ✓
3.1/3.2/3.3/3.4/4.3 都传 → 400「订单ID和团期ID只能传一个」(5/5)✓
3.1/3.2/3.3/3.4/4.3 都不传、3.1 只传 batchNo → 400「订单ID和团期ID必须传一个」(6/6),零写入 ✓
只传 groupBatchId 调 3.1 → departDate=2026-11-04、returnDate=2026-11-08,与团期详情一致 ✓
团期不存在 / 带子订单行ID / 带不存在行ID / 金额不符 → 589500 / 589611 / 589606 / 589613,零写入 ✓
十、相关文档
- 关联 Issue: wx/HL#8230
- 关联 PR: wx/HL#8251
- 前置变更: #8125 套用模版改为在现有行后追加
关联 / 联系人
链接
- Issue: #8230
- PR: wx/HL#8251
- Merge commit: 6ae15b3ed
联系人
- 后端: @lc