这是一份面向**CTO、架构师及核心开发人员**的《智能视觉售卖机系统技术开发指导文档》。 该文档不讲业务故事,只讲**代码怎么写、库怎么建、第三方接口怎么接**,以及核心技术难点的解决方案。 --- # 智能视觉售卖机系统 - 技术开发指导书 (Technical Guide) | 项目属性 | 定义 | | :--- | :--- | | **后端技术栈** | Java 21, Spring Boot 3.5.9, Maven, Lombok, MyBatis-Plus, Hutool | | **前端技术栈** | Uni-app (Vue 3 + TypeScript + Vite + Pinia) | | **中间件** | MySQL 8.0, Redis 7.0 (缓存/分布式锁/消息订阅) | | **第三方核心** | 哈哈零兽开放平台 API, 微信支付V3 (支付分) | | **开发模式** | 前后端分离,RESTful API | --- ## 1. 核心架构与交互时序 ### 1.1 系统逻辑拓扑 * **网关层**: Nginx/Spring Cloud Gateway (可选),负责SSL终结和路由。 * **应用层**: Spring Boot 单体/微服务。 * `Module-User`: C端用户、鉴权、支付分。 * `Module-Device`: 设备状态、指令下发、哈哈API代理。 * `Module-Order`: 订单状态机、回调处理、算费逻辑。 * `Module-Admin`: 商家管理、商品库同步。 * **数据层**: * **MySQL**: 核心业务数据。 * **Redis**: * `Key: device_status:{sn}` -> 存储设备实时状态(在线/离线/购物中)。 * `Key: lock:door:{sn}` -> 分布式锁,防止并发开门。 * `Key: order:temp:{sn}` -> 临时存储回调数据。 ### 1.2 关键交互流程 (购物全链路) 1. **开门请求**: 小程序 -> 后端 (校验余额/风控) -> **Redis加锁** -> 调用哈哈API (`apply_open_door`) -> 设备开门。 2. **购物中**: 设备上传视频流至哈哈云端 (黑盒)。 3. **结算回调**: * 哈哈云端 -> 后端 Webhook (`/api/callback/haha/notify`). * 后端 -> 匹配本地 `out_trade_no`. * 后端 -> 算费 -> **微信支付分扣款**. * 后端 -> WebSocket/Redis PubSub -> 前端提示“支付成功”。 --- ## 2. 数据库详细设计 (Schema Design) ### 2.1 核心表结构 (MySQL 8.0) #### A. 商品表 (t_product) 视觉柜的核心在于**商品与AI模型的绑定**。 ```sql CREATE TABLE `t_product` ( `id` bigint NOT NULL AUTO_INCREMENT, `barcode` varchar(32) NOT NULL COMMENT '69码,全局唯一,核心识别键', `name` varchar(128) NOT NULL, `price` decimal(10,2) NOT NULL COMMENT '零售价', `image_url` varchar(512) NOT NULL COMMENT '白底图URL,用于展示及AI同步', `haha_sync_status` tinyint DEFAULT 0 COMMENT '0-未同步 1-已同步 2-同步失败', `haha_model_id` varchar(64) DEFAULT NULL COMMENT '哈哈侧返回的商品ID', `is_deleted` tinyint DEFAULT 0, `create_time` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_barcode` (`barcode`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表'; ``` #### B. 设备表 (t_device) ```sql CREATE TABLE `t_device` ( `id` bigint NOT NULL AUTO_INCREMENT, `device_id` varchar(64) NOT NULL COMMENT '哈哈设备SN序列号', `shop_id` bigint NOT NULL COMMENT '归属点位ID', `name` varchar(64) DEFAULT NULL, `auth_token` varchar(128) DEFAULT NULL COMMENT '设备端通信Token(如需)', `status` tinyint DEFAULT 1 COMMENT '0-离线 1-在线 2-购物中 3-故障', `current_inventory_hash` varchar(64) DEFAULT NULL COMMENT '当前库存快照Hash,用于补货比对', PRIMARY KEY (`id`), UNIQUE KEY `uk_sn` (`device_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备表'; ``` #### C. 订单表 (t_order) ```sql CREATE TABLE `t_order` ( `id` bigint NOT NULL AUTO_INCREMENT, `order_no` varchar(32) NOT NULL COMMENT '系统内部订单号', `out_trade_no` varchar(64) NOT NULL COMMENT '请求哈哈开门时的流水号', `haha_order_no` varchar(64) DEFAULT NULL COMMENT '哈哈侧订单号', `user_id` bigint NOT NULL, `device_id` varchar(64) NOT NULL, `total_amount` decimal(10,2) DEFAULT NULL, `pay_status` varchar(20) DEFAULT 'PENDING' COMMENT 'PENDING-识别中, WAIT_PAY-待支付, PAID-已支付, FAIL-失败, REVIEW-待人工', `video_url` varchar(512) DEFAULT NULL COMMENT '购物视频证据', `confidence` decimal(5,4) DEFAULT NULL COMMENT 'AI识别置信度(0.00-1.00)', `items_json` text COMMENT '识别到的原始商品JSON快照', `create_time` datetime DEFAULT CURRENT_TIMESTAMP, `pay_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uk_out_trade` (`out_trade_no`), INDEX `idx_user` (`user_id`), INDEX `idx_status` (`pay_status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单主表'; ``` --- ## 3. 哈哈零兽 API 对接指南 (核心难点) ### 3.1 鉴权工具类封装 所有请求 Header 必须包含签名。 * **AppId**: 平台申请。 * **Sign算法**: `MD5(app_id + app_secret + timestamp + body_json)`. * 建议封装 `HahaApiClient` 类,统一处理签名和 HTTP POST。 ### 3.2 关键接口映射 | 业务动作 | 接口名 (参考) | 请求参数重点 | 回调处理 | | :--- | :--- | :--- | :--- | | **同步商品** | `goods/add` | `barcode`, `name`, `image`(Url或Base64) | 无 | | **申请开门** | `device/applyOpen` | `device_id`, **`out_order_no`** (关键关联键) | 仅返回开门指令发送结果,非订单结果 | | **订单结果** | (Webhook) | - | 接收 `out_order_no`, `goods_list`, `video_url` | | **设备状态** | (Webhook) | - | 接收 `device_id`, `status` (update Redis) | ### 3.3 回调处理策略 (Spring Event 模式) 为了解耦 HTTP 回调和业务逻辑: 1. Controller 接收哈哈 Webhook JSON。 2. 校验 `Sign` 确保安全。 3. 发布 `Spring Event` (如 `HahaOrderEvent`)。 4. `@EventListener` 异步处理: * 查询 `t_order` (通过 `out_trade_no`)。 * 若订单存在且状态为 `PENDING` -> 计算金额 -> 扣款。 * **幂等性保护**:如果 Redis 中已有该订单处理记录,直接返回 success。 --- ## 4. Phase 1: 用户端小程序开发要点 ### 4.1 支付分/信用分对接流程 这是“先享后付”的基础。 1. **查询授权**: `GET /api/user/credit_score_status`. * 后端调用微信支付 `v3/payscore/permissions` 查询。 2. **申请授权**: 若未授权,前端拉起小程序插件 `plugin-private-wxpay`. 3. **创单开门**: * 后端先调用微信支付分“创建订单”接口 (Status: DOING)。 * **锁定成功后**,再调用哈哈 API 开门。 * *原因:防止用户开门后无法扣款。* ### 4.2 轮询与状态反馈 用户关门后,订单生成有 5-15秒 延迟。 * **方案 A (推荐)**: 简单轮询。 * 前端每 2秒 调用 `GET /api/order/latest_status?device_id=xxx`. * 后端查询 Redis 或 DB 中的最新订单状态。 * **方案 B (进阶)**: WebSocket。 * 后端处理完回调后,通过 `SimpMessagingTemplate` 推送 `/topic/user/{userId}`。 --- ## 5. Phase 2: PC 运营端开发要点 ### 5.1 商品同步队列 商品图片上传和同步可能耗时。 * **实现**: 使用 Spring `ApplicationEventPublisher` 或 RabbitMQ。 * **流程**: 管理员保存商品 -> DB 状态设为 `SYNCING` -> 异步线程上传图片给哈哈 -> 获取哈哈 `model_id` -> 更新 DB 为 `SYNCED`。 ### 5.2 挂起订单处理 (人工审核) 针对置信度低的订单: * **后端逻辑**: 接收回调 -> 判断 `confidence < 0.85` -> 状态设为 `REVIEW` (不扣款)。 * **PC前端**: 展示 `video_url` (引用哈哈CDN) -> 管理员勾选 `t_product` 中的商品 -> 提交 `/api/admin/order/audit_confirm`。 * **后续**: 接口触发算费 -> 调用微信支付分“完结订单”接口扣款。 --- ## 6. Phase 3: 商家端小程序开发要点 ### 6.1 补货逻辑 (基于快照差异) **切记:视觉柜不手动输入数量。** 1. **开门**: 调用 `/api/device/maintain_open` (类型: 补货)。 2. **回调处理**: * 哈哈返回 `replenish_callback`,包含 `current_stock_list` (当前柜子里的所有东西)。 * **后端逻辑**: * `Last_Stock` = 查询 DB 中该设备的上次库存。 * `Current_Stock` = 回调数据。 * `Diff` = `Current` - `Last` (计算出的补货量/取货量)。 * 更新 DB `t_device_stock` 表为 `Current_Stock`。 * 记录 `t_replenish_log` (补货日志)。 --- ## 7. 安全与风控实现 ### 7.1 防逃单 * **问题**: 用户拿货,但微信余额不足,扣款失败。 * **处理**: 1. 微信返回 `PAY_FAIL`。 2. 后端将 `t_order` 状态更为 `DEBT` (欠款)。 3. 后端将 `t_user` 标记为 `LOCKED`。 4. **Redis 锁**: `lock:user:forbidden:{userId}`。 5. 下次开门前,Filter 拦截器检测到锁,直接拒绝,并返回 `{code: 403, msg: "请先结清欠款"}`。 ### 7.2 接口安全 * 小程序与后端通信:使用 JWT (JSON Web Token),Token 中包含 `userId` 和 `role`。 * 后端与哈哈通信:严格校验 `Sign`。 --- ## 8. 部署建议 * **JDK**: Dragonwell 21. * **Docker**: * 应用容器: `-m 512M` (JVM优化). * Redis: 需开启 AOF 持久化,防止宕机丢失设备状态。 * **域名**: 必须配置 HTTPS (微信小程序强制要求)。 * **内网穿透 (开发期)**: 使用 Ngrok 或 Cpolar 将本地 `7070` 端口暴露为公网域名,填入哈哈后台作为 `Notify Url`。 --- 这份指导文档作为开发实施的基准,请开发团队在编码前详细阅读,特别是 **3. 哈哈零兽 API 对接** 和 **6.1 补货逻辑** 部分,这是最容易出错的环节。