docs(order-v3): #8125 用餐第几天按订单出发日算,套用模版改为在现有行后面追加
changelog-filename-gate / validate (push) Failing after 1s
changelog-filename-gate / validate (push) Failing after 1s
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
@@ -0,0 +1,465 @@
|
|||||||
|
---
|
||||||
|
schema: "hl-changelog/v2"
|
||||||
|
ticket: "8125"
|
||||||
|
title: "用餐第几天按订单出发日算,套用模版改为在现有行后面追加"
|
||||||
|
consumer: "admin"
|
||||||
|
author: "lc(GIT)"
|
||||||
|
change_type: "修改接口"
|
||||||
|
backend_status: "deployed"
|
||||||
|
gateway_status: "verified"
|
||||||
|
frontend_status: "pending"
|
||||||
|
frontend_owner: ""
|
||||||
|
frontend_ref: ""
|
||||||
|
target_release: ""
|
||||||
|
verified_at: ""
|
||||||
|
status_note: "订单用餐行的第几天改为后端按「用餐日期 − 订单出发日期 + 1」重算:整单保存时新增的行和改了用餐日期的行都会跟着变,前端不要再自己算或沿用旧值。保存为模版的第几日同口径(不再按选中那批行里最早的日期算第 1 日)。套用模版的返回由「只有模版行」改为「订单现有行 + 模版行(模版行在后)」,前端仍把返回的行原样整单提交,原有行按行ID保留、模版行按新增落库,页面上表现为在现有数据后面追加。"
|
||||||
|
updated_at: "2026-09-21"
|
||||||
|
base: "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(不变) |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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 日晚餐:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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 午餐):模版那一行**照样追加**,原有行不被覆盖也不被去掉。
|
||||||
|
|
||||||
|
#### 空数据 / 降级响应
|
||||||
|
|
||||||
|
- 模版一行都没有:只返回订单现有行,`code` 200。
|
||||||
|
- 订单一行都没有:只返回模版套出来的行。
|
||||||
|
- 订单没有出发日期:无法对天,**只返回订单现有行**(#8093 时返回空),`code` 200,`message` 带 589602 文案。
|
||||||
|
- 现有行 + 模版行超过 200 条时按 200 截断。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "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` |
|
||||||
|
| 其余字段 | — | 与改动前完全一致 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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`,`code` 200。
|
||||||
|
- 订单没有出发日期:照常保存,行上 `dayNumber` 为 `null`。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "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` 与行上其余字段均不变 |
|
||||||
|
|
||||||
|
#### 请求示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"templateName": "HL8125-TEST-D几模版",
|
||||||
|
"mealInfoIds": ["2102036...010", "2102036...011"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 响应示例
|
||||||
|
|
||||||
|
源订单出发日 `2026-10-01`,两行用餐日期分别是 `2026-10-02` 和 `2026-10-08`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"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。
|
||||||
|
|
||||||
|
#### 错误响应
|
||||||
|
|
||||||
|
```json
|
||||||
|
{ "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](https://git.1814.love:8443/wx/HL/issues/8125)
|
||||||
|
- 关联 PR: [wx/HL#8126](https://git.1814.love:8443/wx/HL/pulls/8126)
|
||||||
|
- 前置变更: [#8093 套用用餐模版改为只读返回组合结果](21_8093_套用用餐模版改只读返回组合结果与模版桌人创建人搜索删除-修改接口-管理后台.md)
|
||||||
|
|
||||||
|
## 关联 / 联系人
|
||||||
|
|
||||||
|
### 链接
|
||||||
|
|
||||||
|
- **Issue**: [#8125](https://git.1814.love:8443/wx/HL/issues/8125)
|
||||||
|
- **PR**: [wx/HL#8126](https://git.1814.love:8443/wx/HL/pulls/8126)
|
||||||
|
- **Merge commit**: [d30cd9561](https://git.1814.love:8443/wx/HL/commit/d30cd9561e868b7b684589f65ad344045c818ce4)
|
||||||
|
|
||||||
|
### 联系人
|
||||||
|
|
||||||
|
- 后端: @lc
|
||||||
在新工单中引用
屏蔽一个用户