diff --git a/changelogs-v2/2026-09/19_7947_餐食删除编码关联餐厅增加桌人-修改接口-管理后台.md b/changelogs-v2/2026-09/19_7947_餐食删除编码关联餐厅增加桌人-修改接口-管理后台.md new file mode 100644 index 00000000..59f5b7df --- /dev/null +++ b/changelogs-v2/2026-09/19_7947_餐食删除编码关联餐厅增加桌人-修改接口-管理后台.md @@ -0,0 +1,522 @@ +--- +schema: "hl-changelog/v2" +ticket: "7947" +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: "餐食接口去掉编码 dishCode;新增餐厅 restaurantId(不传为全部)与桌/人 priceUnit(person/table,不传按人);出参新增 restaurantId、restaurantName(全部返回「全部」)、priceUnit。编码、图片 URL、餐厅 ID 不需要展示。前端需按本文对接。" +updated_at: "2026-09-19" +base: "dev-v3" +--- + +# resource: 餐食删除编码,关联餐厅(含全部),增加桌/人 + +> **服务**: hl-resource-service(dish 表随服务部署由 Flyway 迁移) +> **PR**: #7958 +> **Issue**: #7947 +> **日期**: 2026-09-19 +> **影响范围**: 管理后台「餐食管理」5 个接口(分页、列表、详情、新建、修改) + +--- + +## ⚠️ 关键变化 + +- 🗑️ **编码 `dishCode` 删除**:新建、修改不再有编码,分页不再按编码筛选,分页、列表、详情不再返回编码。340002「编码已存在」停用。 +- 🆕 **餐食关联餐厅**:入参新增 `restaurantId`(餐厅资源里的餐厅 ID),**不传为「全部」**;出参新增 `restaurantId`(全部为 `null`)与 `restaurantName`(全部返回「全部」)。 +- 🆕 **桌/人 `priceUnit`**:`person` 按人 / `table` 按桌,表示单价是每人还是每桌;**不传按人**。 +- 🔁 **名称唯一范围变了**:同一餐厅(「全部」算一组)内名称不能重复,报 340003;不同餐厅可以同名、单价各自维护。 +- 👁️ **不需要展示**:编码、图片 URL、餐厅 ID。 + +--- + +## 一、背景 + +餐食不再需要编码;餐食按餐厅维护,同一道餐食在不同餐厅价格不同,另有一种餐厅是「全部」(没有餐厅 ID);新增「桌/人」。已有餐食上线后都归「全部」、桌/人为「人」。 + +--- + +## 二、变更接口清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 分页查询餐食 | GET | `/admin/dish/items/page` | 修改 | 去掉查询参数 `dishCode`,`keyword` 只匹配名称;出参去掉 `dishCode`,加 `restaurantId`/`restaurantName`/`priceUnit` | +| 2 | 查询餐食列表 | GET | `/admin/dish/items/list` | 修改 | `keyword` 只匹配名称;出参同接口 1 | +| 3 | 查询餐食详情 | GET | `/admin/dish/items/{dishId}/view` | 修改 | 出参同接口 1 | +| 4 | 新建餐食 | POST | `/admin/dish/items/add` | 修改 | 去掉入参 `dishCode`;加 `restaurantId`、`priceUnit`;重名按餐厅分组 | +| 5 | 修改餐食 | PUT | `/admin/dish/items/{dishId}/update` | 修改 | 同接口 4;整份覆盖,`restaurantId` 不传即改为全部,`priceUnit` 不传即按人 | + +--- + +## 三、接口详情 + +### 1. 分页查询餐食 `GET /admin/dish/items/page` + +**VO**: `DishPageReqVO` → `PageResult` + +#### 使用场景 + +餐食管理列表分页查询。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | 否 | 去首尾空格后 ≤64 | **只模糊匹配名称**(原来同时匹配编码) | +| ~~dishCode~~ | Query | — | — | — | **已删除**;继续传会被忽略 | +| status / settleType / createdByName / page / pageSize | Query | — | 否 | 不变 | 不变 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| records[].~~dishCode~~ | — | **已删除** | +| records[].restaurantId | String \| null | **新增**。餐厅 ID;「全部」为 `null` | +| records[].restaurantName | String \| null | **新增**。餐厅名称;「全部」返回 `"全部"`;餐厅已删除为 `null` | +| records[].priceUnit | String | **新增**。`person` 按人 / `table` 按桌 | +| records[] 其余字段、total / page / pageSize | — | 不变 | + +#### 请求示例 + +```http +GET /admin/dish/items/page?page=1&pageSize=20&keyword=团队 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "records": [ + { + "dishId": "2100767707048636418", + "restaurantId": null, + "restaurantName": "全部", + "dishName": "团队标准餐(八菜一汤)", + "unitPrice": 40.00, + "priceUnit": "person", + "imageUrl": null, + "settleType": "cash", + "status": 1, + "remark": null, + "createdBy": "1900000000000000001", + "createdByName": "张三", + "createdAt": "2026-09-18 10:02:11" + }, + { + "dishId": "2100767707048636501", + "restaurantId": "2023382100664676353", + "restaurantName": "菌香园火锅", + "dishName": "团队升级餐(十菜一汤)", + "unitPrice": 600.00, + "priceUnit": "table", + "imageUrl": null, + "settleType": "cash", + "status": 1, + "remark": "含一道特色菜", + "createdBy": "1900000000000000001", + "createdByName": "张三", + "createdAt": "2026-09-18 10:05:40" + } + ], + "total": 2, + "page": 1, + "pageSize": 20 + } +} +``` + +#### 空数据 / 降级响应 + +不变:无数据时 `records` 为 `[]`、`total` 为 0。 + +#### 错误响应 + +```json +{ "code": 400, "message": "每页条数最大为100", "success": false, "data": null } +``` + +#### 业务边界 + +- 旧前端继续传 `dishCode` 查询参数不报错,但不再筛选。 + +--- + +### 2. 查询餐食列表 `GET /admin/dish/items/list` + +**VO**: `DishListReqVO` → `List` + +#### 使用场景 + +餐食下拉数据源(只返回上架餐食,不按餐厅过滤)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| keyword | Query | String | 否 | 去首尾空格后 ≤64 | **只模糊匹配名称** | +| settleType / limit | Query | — | 否 | 不变 | 不变 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| [] | — | 字段同接口 1 的 `records[]`:去掉 `dishCode`,新增 `restaurantId`、`restaurantName`、`priceUnit` | + +#### 请求示例 + +```http +GET /admin/dish/items/list?limit=50 +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": [ + { + "dishId": "2100767707048636418", + "restaurantId": null, + "restaurantName": "全部", + "dishName": "团队标准餐(八菜一汤)", + "unitPrice": 40.00, + "priceUnit": "person", + "imageUrl": null, + "settleType": "cash", + "status": 1, + "remark": null, + "createdBy": "1900000000000000001", + "createdByName": "张三", + "createdAt": "2026-09-18 10:02:11" + } + ] +} +``` + +#### 空数据 / 降级响应 + +不变:没有上架餐食时 `data` 为 `[]`。 + +#### 错误响应 + +```json +{ "code": 400, "message": "limit最大为200", "success": false, "data": null } +``` + +#### 业务边界 + +- 同名餐食在「全部」和各餐厅下各有一条时会同时出现,用 `restaurantName` 区分。 + +--- + +### 3. 查询餐食详情 `GET /admin/dish/items/{dishId}/view` + +**VO**: `DishRespVO` + +#### 使用场景 + +编辑回显。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| dishId | Path | String | 是 | 正数 | 餐食 ID(不变) | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| 全部字段 | — | 同接口 1 的 `records[]`:去掉 `dishCode`,新增 `restaurantId`、`restaurantName`、`priceUnit` | + +#### 请求示例 + +```http +GET /admin/dish/items/2100767707048636501/view +Authorization: Bearer +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { + "dishId": "2100767707048636501", + "restaurantId": "2023382100664676353", + "restaurantName": "菌香园火锅", + "dishName": "团队升级餐(十菜一汤)", + "unitPrice": 600.00, + "priceUnit": "table", + "imageUrl": null, + "settleType": "cash", + "status": 1, + "remark": "含一道特色菜", + "createdBy": "1900000000000000001", + "createdByName": "张三", + "createdAt": "2026-09-18 10:05:40" + } +} +``` + +#### 空数据 / 降级响应 + +不变:餐食不存在或已删除返回 340001。 + +#### 错误响应 + +```json +{ "code": 340001, "message": "餐食不存在", "success": false, "data": null } +``` + +#### 业务边界 + +- 回显时 `restaurantId` 为 `null` 即「全部」。 + +--- + +### 4. 新建餐食 `POST /admin/dish/items/add` + +**VO**: `DishCreateReqVO` → `DishWriteRespVO` + +#### 使用场景 + +新建餐食。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| ~~dishCode~~ | Body | — | — | — | **已删除**;继续传会被忽略,不再校验 | +| restaurantId | Body | String | 否 | 餐厅资源里的餐厅 ID | **新增**。不传为「全部」 | +| priceUnit | Body | String | 否 | `person` / `table` | **新增**。不传按 `person` 存 | +| dishName | Body | String | 是 | 去首尾空格后 1–500 | 同一餐厅(全部算一组)内不能重复 | +| unitPrice / imageUrl / settleType / status / remark | Body | — | — | 不变 | 不变 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| dishId | String | 新餐食 ID(不变) | +| dishName | String | 餐食名称(不变) | + +#### 请求示例 + +```json +{ + "restaurantId": "2023382100664676353", + "dishName": "团队升级餐(十菜一汤)", + "unitPrice": 600.00, + "priceUnit": "table", + "settleType": "cash", + "remark": "含一道特色菜" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "dishId": "2100767707048636501", "dishName": "团队升级餐(十菜一汤)" } +} +``` + +#### 空数据 / 降级响应 + +不变:要么 200 写入,要么返回错误码且零写入。 + +#### 错误响应 + +```json +{ "code": 340003, "message": "餐食名称已存在:团队升级餐(十菜一汤)", "success": false, "data": null } +``` + +```json +{ "code": 400, "message": "桌/人必须是 person 或 table", "success": false, "data": null } +``` + +#### 业务边界 + +- 同名餐食可以在「全部」和不同餐厅下各建一条;同一餐厅(或都不传餐厅)下重名报 340003。 + +--- + +### 5. 修改餐食 `PUT /admin/dish/items/{dishId}/update` + +**VO**: `DishUpdateReqVO` → `DishWriteRespVO` + +#### 使用场景 + +修改餐食(整份覆盖)。 + +#### 入参字段表 + +| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | +|------|------|------|------|------|------| +| dishId | Path | String | 是 | 正数 | 餐食 ID(不变) | +| ~~dishCode~~ | Body | — | — | — | **已删除**;继续传会被忽略 | +| restaurantId | Body | String | 否 | 餐厅资源里的餐厅 ID | **新增**。整份覆盖:不传即改为「全部」,编辑时须带回原值 | +| priceUnit | Body | String | 否 | `person` / `table` | **新增**。整份覆盖:不传即按 `person` 存,编辑时须带回原值 | +| 其余入参 | Body | — | — | 不变 | dishName、unitPrice、imageUrl、settleType、status、remark 不变 | + +#### 出参字段表 + +| 字段 | 类型 | 说明 | +|------|------|------| +| dishId | String | 餐食 ID(不变) | +| dishName | String | 修改后的名称(不变) | + +#### 请求示例 + +```json +{ + "restaurantId": "2023382100664676353", + "dishName": "团队升级餐(十菜一汤)", + "unitPrice": 620.00, + "priceUnit": "table", + "imageUrl": null, + "settleType": "cash", + "status": 1, + "remark": "含一道特色菜" +} +``` + +#### 响应示例 + +```json +{ + "code": 200, + "message": "成功", + "success": true, + "data": { "dishId": "2100767707048636501", "dishName": "团队升级餐(十菜一汤)" } +} +``` + +#### 空数据 / 降级响应 + +不变:要么 200 覆盖写入,要么返回错误码且不改动数据。 + +#### 错误响应 + +```json +{ "code": 340003, "message": "餐食名称已存在:团队升级餐(十菜一汤)", "success": false, "data": null } +``` + +#### 业务边界 + +- 查重在修改后的餐厅内进行,排除自己。 + +--- + +## 四、契约约束与正确调用方式 + +| 场景 | payload | +|------|---------| +| ✅ 「全部」下按人的餐食 | `{ "dishName": "素斋套餐", "unitPrice": 50.00 }` | +| ✅ 某餐厅下按桌的餐食 | `{ "restaurantId": "2023382100664676353", "dishName": "团队升级餐(十菜一汤)", "unitPrice": 600.00, "priceUnit": "table" }` | +| ✅ 同名餐食在另一家餐厅 | 换一个 `restaurantId` 再建同名,单价可不同 | +| ❌ 同一餐厅(或「全部」)下重名 | 340003「餐食名称已存在」 | +| ❌ `priceUnit` 传 `seat` | 400「桌/人必须是 person 或 table」 | +| ⚠️ 修改时漏传 `restaurantId` / `priceUnit` | 整份覆盖:会改成「全部」/按人 | + +--- + +## 五、数据库行为 + +`dish` 表删除 `dish_code`,新增 `restaurant_id`(空即全部)、`price_unit`(默认 person);名称唯一改为同一餐厅(全部算一组)内唯一。已有餐食迁移后都是「全部」、按人,其余字段不变。 + +--- + +## 六、边界行为 + +- 未登录 → 401(网关拦截),不变。 +- 餐厅被删除后,其下餐食的 `restaurantName` 为 `null`,`restaurantId` 仍返回。 + +--- + +## 六.6、修改前后对比 + +### 字段级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| `dishCode`(入参、出参、分页查询参数) | 必填,≤20,唯一 | 删除 | +| `restaurantId`(入参、出参) | 无 | 新增,空即全部 | +| `restaurantName`(出参) | 无 | 新增,全部返回「全部」 | +| `priceUnit`(入参、出参) | 无 | 新增,person / table,不传按人 | +| `keyword` | 匹配编码或名称 | 只匹配名称 | + +### 行为级对比 + +| 行为 | 改前 | 改后 | +|------|------|------| +| 名称唯一 | 全部未删除餐食内唯一 | 同一餐厅(全部算一组)内唯一 | +| 340002 编码已存在 | 会返回 | 停用 | + +## 六.7、影响评估 + +- **是否破坏向后兼容**: 是(出参去掉 `dishCode`;旧前端多传的 `dishCode` 会被忽略,不报错) +- **前端是否必须同步上线**: 是,按本文对接餐厅与桌/人;编码、图片 URL、餐厅 ID 不需要展示 +- **前端 workaround 清理点**: 去掉编码相关的传参与展示 + +## 七、不影响范围 + +- **仅影响**: 餐食接口 1–5 +- **零影响**: + - 餐食上下架 `PUT /admin/dish/items/{dishId}/status/update`、删除 `DELETE /admin/dish/items/{dishId}/del` + - 订单用餐信息、用餐模版接口(其中的「餐食编码快照」以后不会再有值) + +--- + +## 八、测试环境已验证 + +部署提交 `4cbccc26b`,经 Gateway 用真实身份实测 34 项全部通过: + +``` +已有 9 行餐食(含已删除) → 编码列已删,其余列与部署前一致;全部归「全部」、按人 ✓ +分页、列表、7 个详情 → 不再有 dishCode,其余字段与部署前逐字段一致 ✓ +不传编码新建、修改;传空串 / 25 字符 / 重复编码 → 均成功,编码被忽略 ✓ +按 22 行数据表新建(全部 10 道 + 3 家餐厅各 4 道) → 全部成功,详情逐字段一致;同名在全部与 3 家餐厅各一条、单价不同 ✓ +不传 priceUnit 新建、修改 → 存为 person ✓ +全部下 / 同一餐厅下重名,修改成已有名称 → 340003,零写入 ✓ +Swagger 部署前后 → 餐食模型只去 dishCode、加 restaurantId/restaurantName/priceUnit,其余一致 ✓ +``` + +--- + +## 十、相关文档 + +- 关联 Issue: [wx/HL#7947](https://git.1814.love:8443/wx/HL/issues/7947) +- 关联 PR: [wx/HL#7958](https://git.1814.love:8443/wx/HL/pulls/7958) + +## 关联 / 联系人 + +### 链接 + +- **Issue**: [#7947](https://git.1814.love:8443/wx/HL/issues/7947) +- **PR**: [#7958](https://git.1814.love:8443/wx/HL/pulls/7958) +- **Merge commit**: [4cbccc26b](https://git.1814.love:8443/wx/HL/commit/4cbccc26b3d8856c61e61902098bb79afba83147) + +### 联系人 + +- **后端负责人**: @lc