docs(changelog): #7737 餐食管理接口 1.1–1.7 与菜单(新增接口)
changelog-filename-gate / validate (push) Failing after 2s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
这个提交包含在:
lc
2026-09-18 10:16:23 +08:00
共同撰写人 Claude Opus 5
父节点 553aa99dd7
当前提交 4cdf3606e9
@@ -0,0 +1,580 @@
---
schema: "hl-changelog/v2"
ticket: "7737"
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: "2026-09-18"
status_note: "新增 7 个接口与「资源管理 → 餐食管理」菜单(component 为 /resource/dish)。前端需新做餐食管理页面并按本文契约对接。"
updated_at: "2026-09-18"
base: "dev-v3"
---
# resource/user/gateway: 餐食管理接口
**服务**: hl-resource-service(接口)、hl-user-service(菜单)、hl-gateway(路由)
**PR**: #7918
**Issue**: #7737
---
## ⚠️ 关键变化
🔴 **新增 7 个接口,路径前缀 `/admin/dish/items`。** 餐食不关联餐厅;下拉(1.2)只返回上架餐食,不按餐厅过滤。
🔴 **新增菜单「资源管理 → 餐食管理」**:menu_id `422`,path `/dish/list`,component `/resource/dish`,与「餐厅管理」同级同排序;已授权超级管理员、管理员、定制师。前端需按该 component 路径提供页面。
🔴 **新建不传 `settleType` 时存 `cash`(现付);修改是整份覆盖,`imageUrl` / `settleType` / `remark` 传 `null` 即清空。**
---
## 一、背景
新增「餐食管理」:维护餐食(编码、名称、单价、图片、结算方式、上下架),供订单「用餐」选用。系统不预置任何餐食。
---
## 二、变更接口清单
| # | 接口 | 方法 | 路径 | 变更类型 | 说明 |
|---|------|------|------|----------|------|
| 1 | 分页查询餐食 | GET | `/admin/dish/items/page` | 新增 | 餐食管理列表 |
| 2 | 查询餐食列表 | GET | `/admin/dish/items/list` | 新增 | 下拉用,只返回上架 |
| 3 | 查询餐食详情 | GET | `/admin/dish/items/{dishId}/view` | 新增 | 编辑回显 |
| 4 | 新建餐食 | POST | `/admin/dish/items/add` | 新增 | 默认上架、默认现付 |
| 5 | 修改餐食 | PUT | `/admin/dish/items/{dishId}/update` | 新增 | 整份覆盖 |
| 6 | 上架 / 下架餐食 | PUT | `/admin/dish/items/{dishId}/status/update` | 新增 | 不走审批,立即生效 |
| 7 | 删除餐食 | DELETE | `/admin/dish/items/{dishId}/del` | 新增 | 软删除,不检查是否在用 |
---
## 三、接口详情
### 1. 分页查询餐食 `GET /admin/dish/items/page`
**VO**: `DishPageReqVO` → `PageResult<DishListItemRespVO>`
#### 使用场景
餐食管理列表分页查询。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| keyword | Query | String | 否 | 去首尾空格后 ≤64 | 包含式模糊匹配编码、名称;`%`、`_` 按字面量匹配 |
| dishCode | Query | String | 否 | 去首尾空格后 ≤20 | 编码精确匹配 |
| status | Query | Integer | 否 | 0 / 1 | 0=下架,1=上架;不传查全部 |
| settleType | Query | String | 否 | cash / sign / company | 结算方式;不传查全部 |
| createdByName | Query | String | 否 | — | 创建人姓名包含式匹配;去首尾空格后为空按不传 |
| page | Query | Integer | 否 | ≥1,默认 1 | 页码 |
| pageSize | Query | Integer | 否 | 1–100,默认 20 | 每页条数 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| records[].dishId | String | 餐食 ID(雪花,字符串) |
| records[].dishCode | String | 编码 |
| records[].dishName | String | 餐食名称 |
| records[].unitPrice | Number | 单价(每人每餐,元,两位小数) |
| records[].imageUrl | String | 图片 URL,可为 null |
| records[].settleType | String | 结算方式,字典 `resource_settle_type`:cash / sign / company,可为 null |
| records[].status | Integer | 0=下架,1=上架 |
| records[].remark | String | 备注,可为 null |
| records[].createdBy | String | 创建人 ID |
| records[].createdByName | String | 创建人姓名(企微姓名优先,没有取管理员用户名);查不到为 null |
| records[].createdAt | String | 创建时间 `yyyy-MM-dd HH:mm:ss` |
| total / page / pageSize | Integer | 总条数、当前页、每页条数 |
#### 请求示例
```http
GET /admin/dish/items/page?keyword=三百&status=1&page=1&pageSize=20
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"records": [
{
"dishId": "2100767707048636418",
"dishCode": "STANDARD_300",
"dishName": "标准三百快",
"unitPrice": 30.00,
"imageUrl": null,
"settleType": "sign",
"status": 1,
"remark": null,
"createdBy": "1900000000000000001",
"createdByName": "张三",
"createdAt": "2026-09-18 10:02:11"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}
```
#### 空数据 / 降级响应
无数据时 `records` 为 `[]`、`total` 为 0。创建人姓名查询失败时 `createdByName` 为 null,列表照常返回;按创建人姓名筛选而无匹配时返回空页。
#### 错误响应
```json
{ "code": 400, "message": "每页条数最大为100", "success": false, "data": null }
```
#### 业务边界
- 只查未删除餐食,各条件之间是「并且」。
- 按更新时间倒序、餐食 ID 倒序。
---
### 2. 查询餐食列表 `GET /admin/dish/items/list`
**VO**: `DishListReqVO` → `List<DishListItemRespVO>`
#### 使用场景
下拉数据源:订单「用餐」的餐食选择、保存模版时选餐食。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| keyword | Query | String | 否 | 去首尾空格后 ≤64 | 模糊匹配编码、名称 |
| settleType | Query | String | 否 | cash / sign / company | 结算方式 |
| limit | Query | Integer | 否 | 1–200,默认 50 | 最大返回条数 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| [] | Array | 每项字段同接口 1 的 `records[]`,`status` 恒为 1 |
#### 请求示例
```http
GET /admin/dish/items/list?limit=50
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": [
{
"dishId": "2100767707048636418",
"dishCode": "STANDARD_300",
"dishName": "标准三百快",
"unitPrice": 30.00,
"imageUrl": null,
"settleType": "sign",
"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 }
```
#### 业务边界
- 只返回未删除且上架的餐食;下架后立即查不到,重新上架后恢复。
- 不按餐厅过滤;按创建时间升序、餐食 ID 升序。
---
### 3. 查询餐食详情 `GET /admin/dish/items/{dishId}/view`
**VO**: `DishRespVO`
#### 使用场景
编辑回显。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| dishId | Path | String | 是 | 正数 | 餐食 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| (同接口 1 的 records[]) | — | 字段完全一致 |
#### 请求示例
```http
GET /admin/dish/items/2100767707048636418/view
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": {
"dishId": "2100767707048636418",
"dishCode": "STANDARD_300",
"dishName": "标准三百快",
"unitPrice": 30.00,
"imageUrl": null,
"settleType": "cash",
"status": 1,
"remark": null,
"createdBy": "1900000000000000001",
"createdByName": "张三",
"createdAt": "2026-09-18 10:02:11"
}
}
```
#### 空数据 / 降级响应
无空数据分支:餐食不存在或已删除返回 340001。
#### 错误响应
```json
{ "code": 340001, "message": "餐食不存在", "success": false, "data": null }
```
#### 业务边界
- `dishId` 非正数或非数字返回 400。
---
### 4. 新建餐食 `POST /admin/dish/items/add`
**VO**: `DishCreateReqVO` → `DishWriteRespVO`
#### 使用场景
新建餐食;新建默认上架,不走审批。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| dishCode | Body | String | 是 | 去首尾空格后 1–20 | 编码,未删除餐食内不能重复 |
| dishName | Body | String | 是 | 去首尾空格后 1–500 | 餐食名称,未删除餐食内不能重复 |
| unitPrice | Body | Number | 是 | 0–99999999.99,最多两位小数 | 单价 |
| imageUrl | Body | String | 否 | ≤65535 | 图片 URL |
| settleType | Body | String | 否 | cash / sign / company | 结算方式;不传存 cash |
| status | Body | Integer | 否 | 0 / 1 | 不传为 1(上架) |
| remark | Body | String | 否 | ≤500 | 备注 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| dishId | String | 新餐食 ID |
| dishName | String | 餐食名称 |
#### 请求示例
```json
{
"dishCode": "STANDARD_300",
"dishName": "标准三百快",
"unitPrice": 30.00,
"settleType": "sign"
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": { "dishId": "2100767707048636418", "dishName": "标准三百快" }
}
```
#### 空数据 / 降级响应
无降级:要么 200 写入,要么返回错误码且零写入。
#### 错误响应
```json
{ "code": 340002, "message": "编码已存在:STANDARD_300", "success": false, "data": null }
```
```json
{ "code": 340003, "message": "餐食名称已存在:标准三百快", "success": false, "data": null }
```
#### 业务边界
- 字符串入参先去首尾空格,再校验长度、查重。
- 编码、名称只在未删除餐食内唯一;删除后同编码、同名称可以重新新建。
---
### 5. 修改餐食 `PUT /admin/dish/items/{dishId}/update`
**VO**: `DishUpdateReqVO` → `DishWriteRespVO`
#### 使用场景
修改餐食全部字段(整份覆盖)。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| dishId | Path | String | 是 | 正数 | 餐食 ID |
| dishCode | Body | String | 是 | 去首尾空格后 1–20 | 未删除餐食内不能重复(排除自己) |
| dishName | Body | String | 是 | 去首尾空格后 1–500 | 未删除餐食内不能重复(排除自己) |
| unitPrice | Body | Number | 是 | 0–99999999.99,最多两位小数 | 单价 |
| imageUrl | Body | String | 否 | ≤65535 | 传 null 清空 |
| settleType | Body | String | 否 | cash / sign / company | 传 null 清空 |
| status | Body | Integer | 是 | 0 / 1 | 状态 |
| remark | Body | String | 否 | ≤500 | 传 null 清空 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| dishId | String | 餐食 ID |
| dishName | String | 修改后的名称 |
#### 请求示例
```json
{
"dishCode": "STANDARD_300",
"dishName": "标准三百快",
"unitPrice": 32.00,
"imageUrl": null,
"settleType": "company",
"status": 1,
"remark": null
}
```
#### 响应示例
```json
{
"code": 200,
"message": "成功",
"success": true,
"data": { "dishId": "2100767707048636418", "dishName": "标准三百快" }
}
```
#### 空数据 / 降级响应
无降级:要么 200 覆盖写入,要么返回错误码且不改动数据。
#### 错误响应
```json
{ "code": 340001, "message": "餐食不存在", "success": false, "data": null }
```
#### 业务边界
- 整份覆盖:未传或传 null 的可选字段会被清空,编辑前先调接口 3 读回全量。
- 编码重复 340002、名称重复 340003;已生成的订单用餐信息不受修改影响。
---
### 6. 上架 / 下架餐食 `PUT /admin/dish/items/{dishId}/status/update`
**VO**: `DishStatusUpdateReqVO`
#### 使用场景
切换上下架,不走审批,立即生效。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| dishId | Path | String | 是 | 正数 | 餐食 ID |
| status | Body | Integer | 是 | 0 / 1 | 0=下架,1=上架 |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| data | null | 无返回数据 |
#### 请求示例
```json
{ "status": 0 }
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "success": true, "data": null }
```
#### 空数据 / 降级响应
状态没变化直接返回成功。
#### 错误响应
```json
{ "code": 340001, "message": "餐食不存在", "success": false, "data": null }
```
#### 业务边界
- 下架后接口 2 立即查不到;重新上架后恢复。
---
### 7. 删除餐食 `DELETE /admin/dish/items/{dishId}/del`
**VO**: `Result<Void>`(无请求体)
#### 使用场景
软删除餐食。
#### 入参字段表
| 字段 | 位置 | 类型 | 必填 | 约束 | 说明 |
|------|------|------|------|------|------|
| dishId | Path | String | 是 | 正数 | 餐食 ID |
#### 出参字段表
| 字段 | 类型 | 说明 |
|------|------|------|
| data | null | 无返回数据 |
#### 请求示例
```http
DELETE /admin/dish/items/2100767707048636418/del
Authorization: Bearer <admin token>
```
#### 响应示例
```json
{ "code": 200, "message": "成功", "success": true, "data": null }
```
#### 空数据 / 降级响应
无降级:已删除的餐食再删返回 340001。
#### 错误响应
```json
{ "code": 340001, "message": "餐食不存在", "success": false, "data": null }
```
#### 业务边界
- 不检查是否被模版或订单用过;删除后同编码、同名称可以重新新建。
---
## 四、契约约束与正确调用方式
- ID 均为字符串,原样透传,不要转成数字。
- 编辑流程:接口 3 读全量 → 修改 → 接口 5 整份提交;可选字段不传即清空。
- 结算方式取值见字典 `resource_settle_type`(cash 现付 / sign 签单 / company 公司付款)。
- 参数校验失败统一返回 `code=400` 与中文提示;业务错误码见各接口。
---
## 五、数据库行为
- 新建、修改、上下架、删除都只写餐食数据;删除为软删除,删除后同编码、同名称可重建。
- 失败请求(参数校验、重复、不存在)零写入。
- 写接口记操作日志,模块「餐食管理」。
---
## 六、边界行为
| 场景 | 行为 |
|---|---|
| 新建不传 `settleType` / `status` | 存 `cash`、上架 |
| 编码或名称前后带空格 | 去掉后保存、查重 |
| 编码与未删除餐食重复 | 340002,零写入 |
| 名称与未删除餐食重复 | 340003,零写入 |
| 餐食不存在或已删除(详情/修改/上下架/删除) | 340001 |
| 上下架目标状态与当前相同 | 200,不改数据 |
| 下架后调接口 2 | 查不到;重新上架后恢复 |
| 删除后用同编码、同名称新建 | 200,得到新 ID |
---
## 七、不影响范围
- 既有接口、既有错误码不变;餐厅选项接口 `/admin/resource-options/restaurants` 不变。
- 产品行程天仍用字典 `meal_option`,不接餐食。
---
## 八、测试环境已验证
TEST 已部署 dev-v3 `00e882be4`(hl-resource-service、hl-user-service、hl-gateway),经网关用管理员身份逐项实测:7 个接口的入参边界与出参字段、新建默认现付与上架、340001/340002/340003、下架后下拉不返回与重新上架恢复、删除后同编码同名称重建、「资源管理」下出现「餐食管理」菜单,全部通过。
---
## 十、相关文档
- Issue:https://git.1814.love:8443/wx/HL/issues/7737
- PR:https://git.1814.love:8443/wx/HL/pulls/7918
---
## 关联 / 联系人
- 后端:@lc