# 配比秤(新设备)— 采样模式 API 文档 > Controller: `NutRatioScaleController` > 路径前缀: `/neglect/ratio-scale`(Nacos 白名单 `/nutrition/neglect/**`,无需 Sa-Token) > 设备上下文通过请求头 `X-DEVICE-CODE` 解析(`TerminalContextHelper`,据此拿到食堂 id) > 日期: 2026-09-10 > > **序列化规则**:Long 类型字段响应中均为字符串(Jackson `ToStringSerializer`),`BigDecimal` 为普通数字 > **单位约定**: > - 提交采样的熟重 `foodWeight` 单位为 **克 (g)** > - 食材称量用量 `useWeight` 单位为 **克 (g)** --- ## 业务背景 配比秤「采样」复用 **味养创新(nut_innovation)** 的人工创新实验流程,不再直接落 `nut_food` 主表。 ### 完整流程(3 步) ``` ① 提交采样(设备端) → 落库味养创新实验,状态 = 1 实验中 ② 提交评测(味养创新后台)→ 计算营养 + 完善检验信息,状态 = 2 已评测 ③ 转化餐品(味养创新后台)→ 走 nut_food 新建餐品流程关联,状态 = 9 已转化 ``` ### 设备端职责边界 配比秤设备端只负责 **① 提交采样**(称量食材 + 熟重 → 写味养创新实验)。后续的 **② 提交评测**、**③ 转化餐品** 走味养创新已有的后台接口(`/nut/innovation`),不在设备端范围内。 ### 采样交互流程 ``` 搜菜品(food-search) │ ├─ 有目标餐品 → 查菜品构成(food/{foodId}/composition)回显 → 可增删构成 │ └─ 无目标餐品 → 食材模糊搜索(mater-search)自己添加食材/调料 │ ▼ 提交采样(constitute/save)→ 落库味养创新 ``` ### 食材分类枚举 | 分类 | 名称 | 说明 | |:----:|------|------| | 1 | 主材 | 菜品主要食材 | | 2 | 辅材 | 菜品辅助食材 | | 3 | 调料 | 油盐酱醋等 | --- ## 接口清单 | 序号 | 接口 | 路径 | 用途 | |:----:|------|------|------| | 一 | 搜菜品 | `POST /neglect/ratio-scale/food-search` | 按名称模糊查上架菜品 | | 二 | 查菜品构成 | `GET /neglect/ratio-scale/food/{foodId}/composition` | 按菜品id查主辅料+调料,回显构成 | | 三 | 食材模糊搜索 | `POST /neglect/ratio-scale/mater-search` | 采样称量前检索食材 | | 四 | 提交采样 | `POST /neglect/ratio-scale/constitute/save` | **落库味养创新实验** | | 五 | 已提交采样列表 | `POST /neglect/ratio-scale/sample-list` | 按日期查已提交采样,默认当天 | --- ## 一、搜菜品 ``` POST /neglect/ratio-scale/food-search Content-Type: application/json X-DEVICE-CODE: <设备编码> ``` 根据餐厅查餐品名称,供硬件端选择目标菜品(有该餐品时回显其构成)。 ### 请求体 ```json { "keyword": "西兰花", // 选填 — 菜品名称或编号模糊搜索 "pageNum": 1, // 必填 — 页码 "pageSize": 20 // 必填 — 每页条数 } ``` ### 响应 ```json { "code": "00000", "msg": "操作成功", "data": [ { "foodId": "2091839370283118593", // 菜品id "foodCode": "FOOD00012", // 菜品编号 "foodName": "清炒西兰花" // 菜品名称 } ], "total": 1 } ``` ### 逻辑说明 - 数据源:`nut_food`(`status=1` 上架) - 过滤:`canteen_id` = 当前食堂 - 关键词模糊匹配 `food_name` 和 `food_code` - 排序:`id DESC` --- ## 二、查菜品构成 ``` GET /neglect/ratio-scale/food/{foodId}/composition X-DEVICE-CODE: <设备编码> ``` 根据菜品 id 查询已有菜品的主辅料+调料构成及熟重,供硬件端回显构成数据。 ### 响应 ```json { "code": "00000", "msg": "操作成功", "data": { "foodId": "2091839370283118593", // 菜品id "foodName": "清炒西兰花", // 菜品名称 "foodWeight": 450.0, // 熟重(g) "ingredients": [ { "materId": "1979825467306471454", // 食材id "materName": "樱桃萝卜", // 食材名称 "useWeight": 300.0, // 用量(g) "isMain": 1, // 1=主材 / 2=辅材 / 3=调料 "isMainName": "主材" } ] } } ``` ### 逻辑说明 - 数据源:`nut_food_composition`(按 `food_id` 查全部构成) - 食材名称从 `nut_mater_base` **批量反查**(无 N+1) - `foodWeight` 从 `nut_food_cook.output_weight` 反查 --- ## 三、食材模糊搜索 ``` POST /neglect/ratio-scale/mater-search Content-Type: application/json X-DEVICE-CODE: <设备编码> ``` 称量食材前,从食材库检索食材(设备对应食堂食材)。 ### 请求体 ```json { "keyword": "萝卜", // 选填 — 模糊匹配食材名称和别名 "pageNum": 1, // 必填 — 页码 "pageSize": 20 // 必填 — 每页条数 } ``` ### 响应 ```json { "code": "00000", "msg": "操作成功", "data": [ { "id": "1979825467306471454", // 食材id "materName": "樱桃萝卜", // 食材名称 "materCode": "04041012", // 食材编码 "alias": null, // 别名 "materFirstClass": "04", // 食材一级分类 "materSecondClass": "043", // 食材二级分类 "materUrl": null, // 食材图片url "canteenId": "2" // 归属食堂id } ], "total": 1 } ``` ### 逻辑说明 - 数据源:`nut_mater_base` - 过滤:`canteen_id` = 当前食堂(由 `X-DEVICE-CODE` 解析) - 关键词模糊匹配 `mater_name` 和 `alias` - 排序:`mater_name ASC` --- ## 四、提交采样(落库味养创新) ``` POST /neglect/ratio-scale/constitute/save Content-Type: application/json X-DEVICE-CODE: <设备编码> ``` > ⚠️ **接口变更标注**:原提交接口含 `cookMode`(区分制作/采样),现制作模式已废弃,本接口统一为「提交采样」,入参去掉 `cookMode`、`foodId` 字段。 后厨称量食材 + 熟重后提交,后端落库到 **味养创新实验**(`nut_innovation`,人工创新,状态 = 实验中)。 ### 请求体 ```json { "foodName": "清炒西兰花", // 必填 — 菜品名称 "foodWeight": 500, // 必填 — 熟重(g),必须 > 0 "items": [ // 必填 — 食材构成列表,至少1条 { "materId": 20001, // 必填 — 食材id(nut_mater_base.id) "useWeight": 300, // 必填 — 食材称量用量(g),必须 > 0 "isMain": 1 // 必填 — 食材分类: 1=主材 / 2=辅材 / 3=调料 }, { "materId": 30001, "useWeight": 50, "isMain": 3 } ] } ``` ### 响应 ```json { "code": "00000", "msg": "操作成功", "data": null } ``` ### 处理流程 ``` 1. 校验食材构成非空 + materId 非空且唯一 2. 反查食材名称(nut_mater_base) + 一级分类名称(nut_mater_class) 3. 构建味养创新实验 DTO: - foodName → food_name - foodWeight → food_weight(熟重) - items → 食材构成(mater_id + mater_name + mater_cat + amount_g + is_main) - canteenId → 从 X-DEVICE-CODE 解析 - startDate → 回填当天(实验开始日期) - source / deviceCode → 来源标识(2) + 设备编码 - innovGoal → 默认值「配比秤采样」 4. 调用味养创新 manualAdd,落库 nut_innovation(status=1 实验中)+ nut_innovation_ing(食材构成) 5. 自动初始化营养:按食材构成 + 熟重计算每100g熟重营养,写入草稿评测事件 nut_innovation_eval(status=0)+ 营养维度 nut_innovation_eval_nutri(7 项:热量/蛋白/碳水/脂肪/维C/膳食纤维/钠,mineral 留空) ``` > 注:设备端不传 `foodCategory`(餐品分类),落库为空,后期在味养创新后台维护;`innovGoal`(创新目标)后端填默认值「配比秤采样」,后续可改。 ### 异常场景 | 场景 | 错误信息 | |------|---------| | 食材构成列表为空 | `请完善菜品构成信息` | | 食材id为空 | `菜品食材不能为空` | | 食材id重复 | `菜品食材不能重复,重复食材id:xxx` | | 熟重/用量 ≤ 0 | 参数校验:`熟重必须大于0` / `食材用量必须大于0` | ### 涉及数据表 | 表名 | 操作 | 说明 | |------|:--:|------| | `nut_innovation` | 新增 | 味养创新实验(status=1 实验中,含熟重 food_weight) | | `nut_innovation_ing` | 新增 | 食材构成(mater_id + mater_name + mater_cat + amount_g + is_main) | | `nut_innovation_eval` | 新增 | 草稿评测事件(status=0,采样时初始化) | | `nut_innovation_eval_nutri` | 新增 | 营养维度(7 项自动计算值,mineral 留空) | | `nut_mater_base` | 查询 | 反查食材名称 + 营养数据(计算营养) | | `nut_mater_class` | 查询 | 反查一级分类名称 | --- ## 五、已提交采样列表 ``` POST /neglect/ratio-scale/sample-list Content-Type: application/json X-DEVICE-CODE: <设备编码> ``` 查询本食堂已提交的采样记录(味养创新实验 `source=2`),分页返回、按提交时间倒序,供硬件端回看。 ### 请求体 ```json { "pageNum": 1, // 必填 — 页码 "pageSize": 20 // 必填 — 每页条数 } ``` ### 响应 ```json { "code": "00000", "msg": "操作成功", "data": [ { "innovId": "2097486806812893186", // 味养创新实验id "innovNo": "INN-H-2026-001", // 实验编号 "foodName": "清炒西兰花", // 餐品名称 "foodWeight": 500.0, // 熟重(g) "status": 1, // 状态: 1实验中/2已评测/9已转化 "statusName": "实验中", // 状态中文 "createTime": "2026-09-11 10:30:00" // 提交时间 } ], "total": 1 } ``` ### 逻辑说明 - 数据源:`nut_innovation` - 过滤:`canteen_id` = 当前食堂 + `source` = 2(配比秤采样,后台手动为 1) - 排序:`create_time DESC`(查所有,不分日期) --- ## 数据链路图(提交采样 → 味养创新) ``` nut_mater_base (食材库) │ │ 称量 mater_id + use_weight(g) + 熟重 food_weight(g) ▼ POST /constitute/save │ ├── 生成 nut_innovation (味养创新实验,status=1 实验中,含 food_weight) ├── 生成 nut_innovation_ing (食材构成:mater_id + mater_name + mater_cat + amount_g + is_main) └── 自动算营养 → nut_innovation_eval(草稿) + nut_innovation_eval_nutri(7项) 后续(味养创新后台,非设备端): ├── 提交评测 → 复核营养 + 补味道/口感/色泽,status=2 已评测 └── 转化餐品 → nut_food(新建餐品流程),convert_food_id 关联,status=9 已转化 ```