--- schema: "hl-changelog/v2" ticket: "7737" title: "餐食管理接口(分页、下拉、详情、新建、修改、上下架、删除)" consumer: "admin" author: "lc(GIT)" change_type: "新增接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "7d18b442071bb50db9d260cb80224f9e413f3058" target_release: "" verified_at: "2026-09-18" status_note: "新增 7 个接口与「资源管理 → 餐食管理」菜单(component 为 /resource/dish)。前端需新做餐食管理页面并按本文契约对接。[mmg 2026-09-18 已实现并验证] 新建 src/api/dish.js(7 函数+DISH_STATUS 0/1 原码字典+statusMeta 兜底;cleanQuery 空值剥离 status=0 保留;ID String 透传;四写 cancelDuplicate:false)与 src/views/resource/dish/ 页面(import.meta.glob 自动映射不改路由;useListPage 筛选 keyword 模糊/settleType 字典/status/createdByName;列含单价千分位/结算方式字典空显-/状态 tag 原码;行操作[编辑,上下架|删除] renderActionsWithMore;EditModal 必填三项 NForm rules,新建选填空值不带键,编辑 view 回填后整份提交可选传 null)。定向 20/20 + checkpoint 全绿。" 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` #### 使用场景 餐食管理列表分页查询。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | 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 ``` #### 响应示例 ```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` #### 使用场景 下拉数据源:订单「用餐」的餐食选择、保存模版时选餐食。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | 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 ``` #### 响应示例 ```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 ``` #### 响应示例 ```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`(无请求体) #### 使用场景 软删除餐食。 #### 入参字段表 | 字段 | 位置 | 类型 | 必填 | 约束 | 说明 | |------|------|------|------|------|------| | dishId | Path | String | 是 | 正数 | 餐食 ID | #### 出参字段表 | 字段 | 类型 | 说明 | |------|------|------| | data | null | 无返回数据 | #### 请求示例 ```http DELETE /admin/dish/items/2100767707048636418/del Authorization: Bearer ``` #### 响应示例 ```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