# 货柜设备端 — API 文档 > Controller: `NutCabinetController`(毛菜柜/净菜柜通用,通过 `cabinetType` 区分) > 路径前缀: `/neglect/mater-cabinet`(Nacos 白名单,无需 Sa-Token) > 日期: 2026-06-24 --- ## 一、货柜初始化 设备端首次加载调用,后端从 `nut_terminal.device_type` 推导毛菜/净菜类型,懒创建空置格口,返回柜体元信息 + 全量格口。 ``` GET /neglect/mater-cabinet/init?cabinetId=CAB-001 ``` **参数**:`cabinetId` — 必填,智能柜编号(即 `nut_terminal.device_code`) **响应** ```json { "cabinetType": 1, // 1=毛菜柜,2=净菜柜(后端推导) "cabinetId": "CAB-001", "deviceName": "毛菜柜-A区", "canteenId": 100, "canteenName": "第一食堂", "horizontalRows": 4, "verticalCount": 5, "slots": [ { "id": 1, "slotNo": "1", "cabinetId": "CAB-001", "cabinetType": 1, "hasContent": true, "inboundNo": "IN20260623001", "materId": 10001, "materName": "菠菜", "traceCode": "TC20260601", "vegTypeId": 10, "weight": 5.2, "storeTime": "2026-06-23 10:30:00" }, { "id": null, "slotNo": "2", "cabinetId": "CAB-001", "cabinetType": 1, "hasContent": false, "weight": null, "storeTime": null } ] } ``` **说明**: - `cabinetType` 由后端根据 `nut_terminal.device_type` 推导(`raw_cabinet`→1 / `clean_cabinet`→2),前端无需传 - `hasContent=true` 时有存放数据,业务字段(inboundNo/materId 等)有值 - `hasContent=false` 时空置,业务字段均为 null - 格口按 slotNo 数值升序排列 - 每次调用均幂等:格口数不足时自动补建,已足则跳过 --- ## 二、格口入柜 ``` POST /neglect/mater-cabinet/put ``` **请求体** ```json { "cabinetType": 1, // 必填 — 1=毛菜柜,2=净菜柜 "cabinetId": "CAB-001", // 必填 — 智能柜编号 "slotNo": "1", // 必填 — 格口编号 "materId": 10001, // 必填 — 食材id "materName": "菠菜", // 必填 — 食材名称 "traceCode": "TC20260601", // 选填 — 溯源码(净菜柜用于精确匹配) "weight": 5.2 // 必填 — 入柜重量(kg) } ``` **说明**: - 后端按食材+重量匹配对应入库记录(选重量最接近的一条),将其标记为"已入柜" - 传 traceCode 时先按溯源码精确过滤,再按重量匹配 - 格口必须已由 `/init` 预置,否则报错"格口未初始化" - 格口已有物品时拒绝入柜 - 格口行 UPDATE,流水表 INSERT **响应** ```json { "code": 200, "data": "IN20260623001", "msg": "操作成功" } ``` --- ## 三、格口列表(全部格口) ``` POST /neglect/mater-cabinet/slots ``` **请求体** ```json { "cabinetType": 1, // 必填 — 1=毛菜柜,2=净菜柜 "cabinetId": "CAB-001" // 必填 — 智能柜编号 } ``` **响应** — 与 `/init` 返回的 `slots` 字段结构完全相同 ```json [{ "id": 1, "slotNo": "1", "cabinetId": "CAB-001", "cabinetType": 1, "hasContent": true, "inboundNo": "IN20260623001", "materId": 10001, "materName": "菠菜", "traceCode": "TC20260601", "vegTypeId": 10, "weight": 5.2, "storeTime": "2026-06-23 10:30:00" }] ``` --- ## 四、待入柜列表 ``` POST /neglect/mater-cabinet/pending-inbound ``` **请求体** ```json { "cabinetType": 1, // 必填 — 货柜类型 "cabinetId": "CAB-001", // 必填 — 智能柜编号(DTO 校验要求,实际查询用 canteenId) "canteenId": 100 // 必填 — 食堂id } ``` **响应** ```json [{ "id": 1, "materId": 10001, "materName": "菠菜", "traceCode": "TC20260601", "weight": 10.0, "inboundNo": "IN20260623001", "inboundTime": "2026-06-23 09:00:00", "vegTypeId": 10, "vegTypeName": "净菜包", "spec": "500g" }] ``` --- ## 五、格口详情 ``` POST /neglect/mater-cabinet/slot-detail ``` **请求体** ```json { "cabinetType": 1, // 必填 "cabinetId": "CAB-001", // 必填 "slotNo": "1" // 必填 } ``` **响应** — 与 `/init` 返回的 `slots` 元素结构相同 ```json { "id": 1, "slotNo": "1", "cabinetId": "CAB-001", "cabinetType": 1, "hasContent": true, "inboundNo": "IN20260623001", "materId": 10001, "materName": "菠菜", "traceCode": "TC20260601", "vegTypeId": 10, "weight": 5.2, "storeTime": "2026-06-23 10:30:00" } ``` --- ## 六、全量同步格口重量 ``` POST /neglect/mater-cabinet/sync-weight ``` **请求体** ```json { "cabinetType": 1, // 必填 — 货柜类型 "cabinetId": "CAB-001", // 必填 — 智能柜编号 "slotList": [ { "slotNo": "1", "weight": 5.1 }, { "slotNo": "2", "weight": 3.0 }, { "slotNo": "3", "weight": 0 } ] } ``` **说明**: - 终端定时上报整柜各格口当前重量,后端比对变化写流水 - `weight: 0` 表示空格口,空置格口不产生重量变化流水 - 建议上报所有格口(含空格口),不要只报有变化的格口 **响应** ```json { "code": 200, "msg": "操作成功" } ``` --- ## 七、前端注意事项 1. **调用顺序**:设备端启动后先调 `/init` 获取柜体元信息(行列数、食堂名)+ 全量格口。后续定时刷新格口状态走 `/sync-weight` 2. **`cabinetType`**:`/init` **无需传**,后端从 `nut_terminal.device_type` 推导;其余接口仍需传(毛菜柜=1,净菜柜=2) 3. **`deviceType` / `deviceCode`**:由后端自动从请求头 `X-DEVICE-CODE` 填充,前端无需传 4. **sync-weight 的 `slotList`**:上报所有格口(含空格口 weight=0),不要只报有变化的格口 5. **格口编号**:毛菜柜和净菜柜的 slotNo 均为数字字符串("1", "2", …),最大编号 = horizontalRows × verticalCount 6. **格口结构**:一个格口即一条数据库记录,响应为扁平对象,无嵌套