# 补货系统文档 ## 系统概览 补货系统负责管理智能售货机的商品补货流程,包含两套子体系: - **补货员体系**:独立的补货员账号管理、微信登录认证、设备绑定,以及小程序端执行补货操作 - **补货单体系**:运营后台创建补货计划单,管理补货单的完整生命周期 系统已从 V1(`t_stocker` 上货员)迁移到 V2(`t_replenisher` 补货员),V1 表已废弃。 --- ## 一、补货员管理 ### 1.1 数据模型 #### t_replenisher(补货员表) | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键,雪花算法生成 | | name | VARCHAR | 姓名 | | phone | VARCHAR | 手机号 | | employee_id | VARCHAR | 工号 | | wechat_openid | VARCHAR | 微信 OpenID(绑定后写入) | | avatar | VARCHAR | 头像 URL | | status | INT | 状态:1=启用,0=禁用 | | total_tasks | INT | 累计任务数 | | last_task_time | DATETIME | 最后任务时间 | | create_time | DATETIME | 创建时间 | | update_time | DATETIME | 更新时间 | #### t_replenisher_device(设备绑定表) | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键 | | replenisher_id | BIGINT | 补货员 ID | | device_id | VARCHAR | 设备 SN 号 | | source | VARCHAR | 绑定来源:MANUAL(手动)/ QR(扫码)/ SHOP_INHERIT(门店继承) | | create_time | DATETIME | 绑定时间 | ### 1.2 API 接口 所有接口路由前缀为 `/replenishers`,需要管理员权限。 #### 补货员 CRUD | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/replenishers/list` | `replenisher:read` | 分页查询补货员列表,支持 keyword/status 筛选 | | GET | `/replenishers/{id}` | `replenisher:read` | 获取补货员详情(含绑定设备数) | | GET | `/replenishers/search` | `replenisher:read` | 按姓名/手机/工号模糊搜索 | | POST | `/replenishers` | `replenisher:create` | 创建补货员(name 必填) | | PUT | `/replenishers/{id}` | `replenisher:update` | 更新补货员信息 | | PUT | `/replenishers/{id}/status` | `replenisher:update` | 启用/禁用补货员 | | DELETE | `/replenishers/{id}` | `replenisher:delete` | 删除补货员(级联解绑所有设备) | #### 设备绑定 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/replenishers/{id}/devices` | `replenisher:read` | 获取补货员绑定的设备 ID 列表 | | POST | `/replenishers/{id}/devices` | `replenisher:update` | 绑定设备到补货员(INSERT IGNORE,幂等) | | DELETE | `/replenishers/{id}/devices/{deviceId}` | `replenisher:update` | 解绑单个设备 | | POST | `/replenishers/batch-bind` | `replenisher:update` | 批量绑定多个补货员到多个设备 | #### 微信绑定 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | POST | `/replenishers/{id}/binding-code` | `replenisher:update` | 生成 24 位绑定码(Redis 存储,24小时有效) | ### 1.3 认证流程 补货员的绑定和登录全部在**运营端小程序(haha-admin-mp)**中完成。 #### 创建与绑定(首次使用) ``` 管理员在 Web 后台创建补货员 → 生成 24 位绑定码(Redis 存储,24h 有效) → 将绑定码告知补货员 → 补货员打开"哈哈运营平台"小程序 → 登录页点击"已有绑定码?点击绑定微信" → 输入绑定码 → 微信授权 → POST /replenisher/login/bind → 后端校验绑定码、获取 openid、写入 wechat_openid、激活账号 → 自动登录 → 进入补货首页 ``` #### 静默登录(已绑定后) ``` 补货员打开运营端小程序 → 点击"补货员微信一键登录" → wx.login() 获取 code → POST /replenisher/login/wechat → 后端通过 code 换 openid → 查找已绑定的补货员 → 生成 SaToken → 返回 token + 用户信息 → 进入补货首页 ``` ### 1.4 关联管理入口 补货员还可以通过以下入口绑定到门店/设备: | 来源 | 接口 | 说明 | |------|------|------| | 门店管理 | `POST /shops/{id}/replenishers` | 将补货员绑定到门店下所有设备,source=SHOP_INHERIT | | 门店管理 | `GET /shops/{id}/replenishers` | 查询门店关联的补货员列表 | | 设备管理 | `POST /devices/{deviceId}/replenishers` | 直接绑定补货员到设备,source=MANUAL | | 设备管理 | `GET /devices/{deviceId}/replenishers` | 查询设备绑定的补货员列表 | | 设备管理 | `DELETE /devices/{deviceId}/replenishers/{replenisherId}` | 解绑设备的补货员 | --- ## 二、补货操作 ### 2.1 权限控制 补货员只能操作自己绑定的设备。操作前系统会: 1. 从当前登录的 SaToken session 中获取补货员 ID 2. 查询补货员绑定的设备 ID 列表 3. 校验目标设备是否在绑定列表中,不在则拒绝访问 ### 2.2 API 接口 所有接口路由前缀为 `/replenisher`,需要补货员角色(`REPLENISHER`)。 | 方法 | 路径 | 说明 | |------|------|------| | GET | `/replenisher/my-info` | 获取当前补货员个人信息 | | GET | `/replenisher/device/list` | 获取绑定设备列表(含各设备库存概览、在线状态) | | GET | `/replenisher/device/inventory/{deviceId}` | 获取设备库存详情(含商品信息、层模板分组) | | POST | `/replenisher/stock/replenish` | 执行补货操作,逐商品增加库存 | ### 2.3 补货操作流程 ``` 补货员选择设备 → GET /replenisher/device/inventory/{deviceId} → 查看当前库存 → 输入各商品补货数量 → POST /replenisher/stock/replenish → 后端逐商品调用 deviceInventoryService.increaseStock() → 库存增加成功 → 返回执行结果(成功/失败明细) ``` 补货请求体(`ReplenishDTO`): ```json { "deviceId": "SN20250101001", "items": [ { "productId": 1001, "productCode": "P001", "productName": "可口可乐", "shelfNum": 1, "position": "left", "quantity": 10 } ] } ``` ### 2.4 库存记录 补货操作后系统自动记录上货日志: - 日志类型:`InventoryLog.TYPE_RESTOCK`(补货上货) - 数据库表:`t_inventory_log` 记录 beforeStock → afterStock 变动 - 可通过 `POST /inventory/records/v2` 创建上货记录时关联补货员信息(`replenisherId`、`replenisherName`、`replenisherPhone`) --- ## 三、补货单管理 补货单用于运营人员制定补货计划,管理补货任务从创建到完成的全流程。 ### 3.1 数据模型 #### t_replenishment_order(补货单表) | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键 | | order_no | VARCHAR | 补货单号,规则:`RO` + yyyyMMdd + 4位序号 | | device_id | VARCHAR | 目标设备 SN | | shop_id | BIGINT | 目标门店 ID | | status | INT | 状态:0 草稿 / 1 已提交 / 2 已同步ERP / 3 已完成 / 4 已取消 | | erp_order_no | VARCHAR | ERP 采购单号(同步后回填) | | erp_sync_time | DATETIME | ERP 同步时间 | | supplier_name | VARCHAR | 供应商名称 | | warehouse_name | VARCHAR | 发货仓库 | | expected_arrival_time | DATETIME | 预计到货时间 | | total_quantity | INT | 补货总数量 | | total_amount | DECIMAL | 补货总金额 | | remark | VARCHAR | 备注 | | creator_id | BIGINT | 创建人 ID | | creator_name | VARCHAR | 创建人姓名 | #### t_replenishment_order_item(补货单明细表) | 字段 | 类型 | 说明 | |------|------|------| | id | BIGINT | 主键 | | order_id | BIGINT | 关联补货单 ID | | device_id | VARCHAR | 设备 SN(冗余) | | product_id | BIGINT | 商品 ID | | product_code | VARCHAR | 商品编码 | | product_name | VARCHAR | 商品名称 | | planned_quantity | INT | 计划补货数量(≥1) | | actual_quantity | INT | 实际补货数量 | | before_stock | INT | 补货前库存 | | after_stock | INT | 补货后库存 | | unit_price | DECIMAL | 单价 | | total_price | DECIMAL | 小计金额 | | shelf_num | INT | 货架层号 | | position | VARCHAR | 货道位置(left/right) | ### 3.2 状态流转 ``` 草稿(0) ──提交──→ 已提交(1) ──同步ERP──→ 已同步ERP(2) ──完成──→ 已完成(3) │ │ │ └──取消──→ 已取消(4) ←──取消──┘ │ ↑ │ └───────────────────────────────────────────────────┘ (已完成也可以取消) ``` 状态说明: | 状态码 | 状态名 | 标签色 | 说明 | |--------|--------|--------|------| | 0 | 草稿 | info | 可编辑、可删除、可提交、可取消 | | 1 | 已提交 | warning | 可同步ERP、可取消 | | 2 | 已同步ERP | primary | 已推送至采购系统,可完成、可取消 | | 3 | 已完成 | success | 终态(也可取消) | | 4 | 已取消 | danger | 终态 | ### 3.3 API 接口 路由前缀为 `/replenishment-orders`,需要相应权限。 | 方法 | 路径 | 权限 | 说明 | |------|------|------|------| | GET | `/replenishment-orders/list` | `replenishment:order:read` | 分页查询,支持 orderNo/deviceId/shopId/status/时间范围/创建人 筛选 | | GET | `/replenishment-orders/{id}` | `replenishment:order:read` | 获取补货单详情 | | GET | `/replenishment-orders/{id}/items` | `replenishment:order:read` | 获取补货单商品明细列表 | | POST | `/replenishment-orders` | `replenishment:order:create` | 创建补货单(含商品明细) | | PUT | `/replenishment-orders` | `replenishment:order:update` | 更新补货单(仅草稿状态可更新) | | DELETE | `/replenishment-orders/{id}` | `replenishment:order:delete` | 删除补货单(仅草稿状态可删除) | | POST | `/replenishment-orders/{id}/submit` | `replenishment:order:submit` | 提交补货单(草稿 → 已提交) | | POST | `/replenishment-orders/{id}/sync-erp` | `replenishment:order:sync` | 同步到 ERP 系统(已提交 → 已同步ERP) | | POST | `/replenishment-orders/{id}/complete` | `replenishment:order:complete` | 完成补货单(已同步ERP → 已完成) | | POST | `/replenishment-orders/{id}/cancel` | `replenishment:order:update` | 取消补货单 | ### 3.4 操作约束 - **创建**:需传入 deviceId(必填)、商品明细列表(plannedQuantity ≥ 1),系统自动生成单号 - **更新**:仅草稿状态可更新,更新时重新计算 totalQuantity 和 totalAmount - **删除**:仅草稿状态可删除,删除时级联删除明细 - **提交**:草稿 → 已提交,此后不可再编辑 - **同步 ERP**:目前为桩代码,预留企得宝 SDK 集成接口 - **完成**:已同步ERP → 已完成 - **取消**:草稿、已提交、已同步ERP、已完成 均可取消(取消是终态) --- ## 四、前端页面 ### 4.1 Web 管理后台(haha-admin-web) | 页面 | 路由 | 说明 | |------|------|------| | 补货员列表 | `/operation/replenisher` | 补货员 CRUD、设备绑定/解绑、绑定码生成 | | 补货单列表 | 侧边栏「库存管理 → 补货单列表」 | 补货单查询、新建、状态操作 | | 补货单详情 | 侧边栏「库存管理 → 补货单详情」 | 补货单完整信息 + 商品明细 | | 设备管理 | 设备列表页内嵌弹窗 | 为指定设备绑定/解绑补货员 | | 门店管理 | 门店列表页内嵌弹窗 | 为门店批量绑定补货员到其下所有设备 | | 库存记录 | 库存管理 → 上货记录 | 上货记录中可关联补货员信息(V2 补货员体系) | ### 4.2 运营端小程序(haha-admin-mp) | 页面 | 路径 | 说明 | |------|------|------| | 补货首页 | `pages/replenish/index` | 补货员信息卡片、绑定设备列表、待补货统计、**扫码开柜**按钮 | | 补货操作 | `pages/replenish/operation` | 设备商品清单、补货数量输入、提交补货 | | 绑定微信 | `pages/replenish/bind` | 通过小程序码/绑定码参数进入,完成微信授权绑定 | | 补货员管理 | `pages/replenisher/list` | 管理员查看补货员列表 | | 编辑补货员 | `pages/replenisher/form` | 管理员新增/编辑补货员 | | 设备绑定 | `pages/replenisher/bind-device` | 管理员为补货员绑定设备(门店设备树) | | 配置补货员 | `pages/device/replenisher-config` | 从设备端配置其绑定的补货员 | ### 4.3 扫码开柜 补货首页提供"扫码开柜"按钮,补货员可扫描设备机身二维码直接进入补货操作: 1. 点击「扫码开柜」→ 调用 `uni.scanCode` 扫描设备二维码 2. 机身二维码格式:`https://dev-haha.kuaiyuman.cn/{deviceId}` 3. 解析提取 deviceId → 校验是否在绑定设备列表中 4. 跳转 `/pages/replenish/operation?deviceId=xxx` 执行补货 --- ## 五、数据库变更记录 | 文件 | 说明 | |------|------| | `docs/database/migrate_replenisher.sql` | V1 `t_stocker` → V2 `t_replenisher` 数据迁移 | | `docs/database/migrate_replenishment_to_inventory.sql` | 补货单菜单注册到库存管理模块下 | | `haha-admin/src/main/resources/sql/replenishment_order.sql` | `t_replenishment_order` 和 `t_replenishment_order_item` 建表 DDL | --- ## 六、技术要点 ### 6.1 库存操作 补货操作调用 `DeviceInventoryService.increaseStock()` 增加库存,该方法: - 若库存记录不存在则创建(stock = quantity) - 若已存在则累加(stock = beforeStock + quantity) - 无上限限制 - 写入 `t_inventory_log` 记录变动 - 使用乐观锁(version 字段)防止并发冲突 ### 6.2 绑定码机制 - 管理员在 Web 后台补货员列表点击「绑定码」按钮调用 `POST /replenishers/{id}/binding-code` - 后端生成 24 位随机字符串(UUID 截取)存入 Redis,Key 格式为 `replenisher:bind:code:{code}`,TTL 24 小时 - 将绑定码告知补货员,补货员在运营端小程序登录页输入绑定码完成微信绑定 - 绑定码一次性使用,验证后立即从 Redis 删除 ### 6.3 补货单号规则 - 格式:`RO` + `yyyyMMdd` + 4位自增序号(如 `RO202507060001`) - 通过查询当天最大单号 + 1 生成 - 数据库 `order_no` 字段有 UNIQUE 索引保证唯一性 - 日订单量超过 9999 会报错 ### 6.4 ERP 集成状态 `syncToErp` 接口目前为桩代码,日志记录"模拟同步ERP成功"。企得宝 SDK 已在 `haha-sdk` 模块中预留,后续接入时需实现真实推送逻辑。 --- ## 七、相关文件索引 ### 后端核心文件 ``` haha-entity/src/main/java/com/haha/entity/ ├── Replenisher.java # 补货员实体 ├── ReplenisherDevice.java # 设备绑定实体 ├── ReplenishmentOrder.java # 补货单实体 ├── ReplenishmentOrderItem.java # 补货单明细实体 └── dto/ ├── ReplenishDTO.java # 补货操作请求 DTO ├── ReplenisherCreateDTO.java # 创建补货员 DTO ├── ReplenisherUpdateDTO.java # 更新补货员 DTO ├── ReplenisherQueryDTO.java # 补货员查询 DTO ├── ReplenisherBindDTO.java # 微信绑定 DTO ├── BindReplenisherDTO.java # 门店绑定补货员 DTO ├── ReplenishmentOrderCreateDTO.java # 创建补货单 DTO ├── ReplenishmentOrderUpdateDTO.java # 更新补货单 DTO └── ReplenishmentOrderQueryDTO.java # 补货单查询 DTO haha-admin/src/main/java/com/haha/admin/controller/ ├── ReplenisherController.java # 补货员管理接口 ├── ReplenisherLoginController.java # 补货员认证接口 ├── ReplenisherOperationController.java # 补货员操作接口 └── ReplenishmentOrderController.java # 补货单管理接口 haha-service/src/main/java/com/haha/service/ ├── ReplenisherService.java # 补货员服务接口 ├── ReplenishmentOrderService.java # 补货单服务接口 └── impl/ ├── ReplenisherServiceImpl.java # 补货员服务实现 └── ReplenishmentOrderServiceImpl.java # 补货单服务实现 haha-mapper/src/main/java/com/haha/mapper/ ├── ReplenisherMapper.java ├── ReplenisherDeviceMapper.java ├── ReplenishmentOrderMapper.java └── ReplenishmentOrderItemMapper.java haha-common/src/main/java/com/haha/common/enums/ └── ReplenishmentOrderStatusEnum.java # 补货单状态枚举 ``` ### 前端核心文件 ``` haha-admin-web/src/ ├── views/replenisher/ # 补货员管理页面 │ ├── index.vue │ └── utils/ │ ├── hook.tsx │ └── types.ts ├── views/replenishment-order/ # 补货单管理页面 │ ├── index.vue │ ├── detail.vue │ └── components/ │ └── CreateDialog.vue └── api/ ├── replenisher.ts └── replenishmentOrder.ts haha-admin-mp/src/ ├── pages/replenish/ # 补货员小程序页面 │ ├── index.vue │ ├── operation.vue │ └── bind.vue ├── pages/replenisher/ # 补货员管理页面 │ ├── list.vue │ ├── form.vue │ └── bind-device.vue ├── pages/device/ │ └── replenisher-config.vue └── api/ ├── replenish.ts └── replenisher.ts haha-mp/src/ └── pages/replenish/ └── bind.vue # 用户端绑定页面 ```