用户通过小程序扫码开门、选购商品、自动结算的完整交互流程。
sequenceDiagram
participant 用户
participant 小程序 as haha-mp (小程序)
participant 后端 as haha-miniapp (后端)
participant SDK as HahaClient (SDK)
participant 哈哈零兽 as 哈哈零兽平台
participant 微信 as 微信支付分
rect rgb(240, 248, 255)
Note over 用户,微信: 阶段一:扫码开门
用户->>小程序: 扫描设备二维码
小程序->>小程序: 检查登录状态
小程序->>后端: GET /api/payscore/check-enable
后端-->>小程序: 支付分开通状态
alt 未开通支付分
小程序->>小程序: 跳转支付分开通页面
小程序->>后端: POST /api/payscore/enable
后端->>微信: 创建支付分服务订单
微信-->>后端: 返回 outOrderNo
后端-->>小程序: 返回开通参数
小程序->>微信: wx.openBusinessView 调起授权
微信-->>小程序: 用户授权结果
小程序->>后端: POST /api/payscore/confirm-enable
后端-->>小程序: 开通确认结果
end
小程序->>小程序: 弹窗选择:查看柜内商品 / 直接开门
alt 直接开门
小程序->>后端: POST /api/device/scan-open {deviceId}
后端->>SDK: openDoor(deviceId, userId, OUT, doorIndex, MINIAPP)
SDK->>哈哈零兽: POST /door/open
哈哈零兽-->>SDK: {activity_id, user_id}
SDK-->>后端: OpenDoorResult
后端->>后端: 保存开门记录(数据库 + Redis)
后端-->>小程序: Result<OpenDoorVO>
小程序->>小程序: 跳转购物页面
end
end
rect rgb(255, 248, 240)
Note over 用户,微信: 阶段二:选购等待
小程序->>小程序: 开始轮询设备状态
loop 每 2 秒轮询
小程序->>后端: GET /api/device/status?deviceId=xxx
后端-->>小程序: 设备门状态
end
哈哈零兽->>后端: 回调 DEVICE_STATUS (status=OPENED)
后端->>后端: 保存状态到 Redis + 更新数据库门状态
Note over 用户: 用户选购商品...
哈哈零兽->>后端: 回调 DEVICE_STATUS (status=CLOSED)
后端->>后端: 更新开门记录关门状态 + 更新数据库门状态
end
rect rgb(240, 255, 240)
Note over 用户,微信: 阶段三:AI识别
哈哈零兽->>后端: 回调 ORC_RESULT (AI识别结果)
后端->>后端: 保存识别结果到 Redis
alt 有消费 (nobuy=0)
后端->>后端: 获取或创建订单
后端->>后端: 更新订单商品列表 + 视频URL(金额待订单回调更新)
后端->>后端: 更新关门状态
else 无消费 (nobuy=1)
后端->>后端: 标记无消费 + 更新关门状态
end
后端-->>哈哈零兽: "success"
end
rect rgb(255, 240, 255)
Note over 用户,微信: 阶段四:订单结算
哈哈零兽->>后端: 回调 ORDER (订单信息 + 金额)
后端->>后端: 更新订单号 + 订单金额 + 订单类型
后端->>后端: 保存订单商品明细
后端->>后端: 优惠券自动扣减(最先到期优先)
后端->>后端: 更新优惠金额 + 实付金额
alt 支付分订单
后端->>微信: completePayScoreOrder (完结扣费)
微信-->>后端: 扣费结果
end
后端->>后端: 保存订单信息到 Redis
后端-->>哈哈零兽: "success"
小程序->>后端: 轮询获取订单信息
后端-->>小程序: 返回订单详情
小程序->>小程序: 显示订单结果
end
哈哈零兽平台通过两类回调地址推送通知,后端统一在 CallbackController 接收处理。
/callback/haha/message)| notify_type | 说明 | 处理逻辑 |
|---|---|---|
| DEVICE_STATUS | 开关门状态通知 | 更新 Redis 缓存 + 数据库门状态 + 开门记录 |
| ONLINE_STATUS | 设备在线状态通知 | 更新 Redis 缓存 + 离线告警/上线恢复/弱信号告警 |
| VOICE_RESULT | 音量调节结果通知 | 更新 Redis 缓存 |
| ORC_RESULT | AI识别结果通知 | 保存识别结果 + 创建/更新订单 + 关门状态处理 |
| CLIENT_NEW_PRODUCT | 新品审核结果回调 | 更新申请记录 + 同步商品到商家库 |
| MERGE_PRODUCT | 商品合并结果通知 | 记录合并日志 |
/callback/haha/order)哈哈零兽生成订单后推送的完整订单信息,包含订单号、金额、商品明细等。
flowchart TD
Callback[收到回调通知] --> VerifySign[验证签名]
VerifySign --> ParseParams[解析回调参数]
ParseParams --> CheckType{判断回调类型}
CheckType --> |消息回调| CheckNotifyType{notify_type?}
CheckType --> |订单回调| HandleOrder[处理订单回调]
CheckNotifyType --> |DEVICE_STATUS| HandleDeviceStatus[处理开关门状态]
CheckNotifyType --> |ONLINE_STATUS| HandleOnlineStatus[处理在线状态]
CheckNotifyType --> |VOICE_RESULT| HandleVoiceResult[处理音量结果]
CheckNotifyType --> |ORC_RESULT| HandleOrcResult[处理AI识别结果]
CheckNotifyType --> |CLIENT_NEW_PRODUCT| HandleNewProduct[处理新品审核]
CheckNotifyType --> |MERGE_PRODUCT| HandleMergeProduct[处理商品合并]
HandleDeviceStatus --> SaveRedis1[保存状态到Redis]
SaveRedis1 --> UpdateDB1[更新数据库门状态]
UpdateDB1 --> UpdateRecord1[更新开门记录]
HandleOnlineStatus --> SaveRedis2[保存在线状态到Redis]
SaveRedis2 --> CheckOnline{在线/离线?}
CheckOnline --> |离线| OfflineAlert[触发离线告警]
CheckOnline --> |在线| BackOnline[发送恢复通知]
BackOnline --> CheckSignal{信号强度?}
CheckSignal --> |弱信号| WeakSignalAlert[触发弱信号告警]
HandleOrcResult --> SaveRedis3[保存识别结果到Redis]
SaveRedis3 --> CheckConsume{是否有消费?}
CheckConsume --> |有消费| CreateOrder[创建/更新订单]
CheckConsume --> |无消费| MarkNoConsume[标记无消费]
HandleOrder --> UpdateOrderAmount[更新订单金额]
UpdateOrderAmount --> SaveOrderGoods[保存订单商品]
SaveOrderGoods --> CouponDiscount[优惠券自动扣减]
CouponDiscount --> PayScorePayment[支付分扣费]
PayScorePayment --> SaveRedis4[保存订单到Redis]
RecordResult[返回 success]
HandleVoiceResult --> SaveRedis5[保存音量到Redis] --> RecordResult
HandleNewProduct --> UpdateApply[更新申请记录] --> SyncProduct[同步商品] --> RecordResult
HandleMergeProduct --> LogMerge[记录合并日志] --> RecordResult
MarkNoConsume --> RecordResult
CreateOrder --> RecordResult
SaveRedis4 --> RecordResult
OfflineAlert --> RecordResult
BackOnline --> RecordResult
WeakSignalAlert --> RecordResult
UpdateDB1 --> RecordResult
sequenceDiagram
participant 小程序
participant 后端 as DeviceServiceImpl
participant SDK as HahaClient
participant 哈哈零兽
participant Redis
participant DB as 数据库
小程序->>后端: scanOpenDoor(deviceId, userId)
后端->>后端: checkUserPayscoreStatus(userId)
后端->>SDK: getOnlineStatus(deviceId)
SDK->>哈哈零兽: 查询设备在线状态
哈哈零兽-->>SDK: DeviceOnlineStatus
alt 设备离线
SDK-->>后端: isOnline=0
后端-->>小程序: BusinessException("设备当前离线")
end
后端->>SDK: isMultiDoorUnique(deviceId)
SDK-->>后端: 多门单开类型
alt 多门单开设备
后端->>后端: doorIndex = "A"
end
后端->>SDK: openDoor(deviceId, userId, OUT, doorIndex, MINIAPP)
SDK->>哈哈零兽: POST /door/open
哈哈零兽-->>SDK: {activity_id, user_id}
SDK-->>后端: OpenDoorResult
后端->>DB: 保存开门记录 (DoorRecord)
后端->>Redis: 保存设备状态
后端-->>小程序: OpenDoorVO
订单回调触发完整的结算链路,包括金额更新、优惠券扣减、支付分扣费。
flowchart TD
OrderCallback[收到订单回调] --> FindOrder[查找本地订单]
FindOrder --> OrderExists{订单是否存在?}
OrderExists --> |是| CheckDuplicate{是否已处理?}
OrderExists --> |否| FetchRecognition[主动查询识别结果创建订单]
CheckDuplicate --> |已处理| SaveRedis[保存到Redis]
CheckDuplicate --> |未处理| UpdateOrder[更新订单信息]
FetchRecognition --> CreateFromRecognition[根据识别结果创建订单]
CreateFromRecognition --> UpdateOrder
UpdateOrder --> SetAmount[设置订单金额 totalAmount]
SetAmount --> InitDiscount[初始化 discountAmount=0, paidAmount=totalAmount]
InitDiscount --> SaveGoods[保存订单商品明细]
SaveGoods --> ProcessCoupon[优惠券自动扣减]
ProcessCoupon --> QueryCoupons[查询用户可用优惠券]
QueryCoupons --> SortByExpiry[按到期时间升序排序]
SortByExpiry --> FindApplicable{找到适用优惠券?}
FindApplicable --> |是| CalcDiscount[计算优惠金额]
FindApplicable --> |否| UseOriginalAmount[使用原金额]
CalcDiscount --> CheckCap{优惠金额 > 订单金额?}
CheckCap --> |是| CapDiscount[优惠金额 = 订单金额]
CheckCap --> |否| KeepDiscount[保持计算值]
CapDiscount --> FinalAmount
KeepDiscount --> FinalAmount[计算实付金额 paidAmount = total - discount]
FinalAmount --> UseCoupon[标记优惠券已使用]
UseCoupon --> UpdateOrderDiscount[更新订单 discountAmount + paidAmount]
UseOriginalAmount --> PayScoreCheck
UpdateOrderDiscount --> PayScoreCheck{支付分订单?}
PayScoreCheck --> |是| DeductPayScore[支付分完结扣费]
PayScoreCheck --> |否| SaveOrderRedis[保存订单到Redis]
DeductPayScore --> SaveOrderRedis
SaveOrderRedis --> Done[结算完成]
| 阶段 | 负责方 | totalAmount | discountAmount | paidAmount |
|---|---|---|---|---|
| 开门记录 | DeviceServiceImpl | - | - | - |
| AI识别回调 | HahaCallbackServiceImpl | 0 | 0 | 0 |
| 订单回调 | HahaCallbackServiceImpl | 设备传来 | 0 | = totalAmount |
| 优惠券扣减 | HahaCallbackServiceImpl | 不变 | 优惠券金额 | = total - discount |
| 支付扣费 | PayScoreService | 不变 | 不变 | 使用此金额扣费 |
applyScope=1:全场通用applyScope=2:指定门店applyScope=3:指定商品type=1 满减券:直接返回 discountValuetype=2 折扣券:orderAmount × (1 - discountValue/10),受 maxDiscount 上限约束type=3 抵扣券:直接返回 discountValuetype=4 兑换券:不参与金额扣减sequenceDiagram
participant 用户
participant 小程序
participant 后端 as PayScoreServiceImpl
participant 微信 as 微信支付分
rect rgb(240, 255, 255)
Note over 用户,微信: 支付分开通过程
用户->>小程序: 点击开通支付分
小程序->>后端: POST /api/payscore/enable
后端->>微信: 创建支付分服务订单
微信-->>后端: outOrderNo + serviceId
后端-->>小程序: 开通参数
小程序->>微信: wx.openBusinessView 调起授权页
微信-->>用户: 展示授权页面
用户->>微信: 确认授权
微信-->>小程序: 授权结果回调
小程序->>后端: POST /api/payscore/confirm-enable
后端->>微信: 查询支付分状态
微信-->>后端: 用户支付分状态
后端->>后端: 更新用户 payscoreEnabled 状态
后端-->>小程序: 开通成功
end
rect rgb(255, 255, 240)
Note over 用户,微信: 开门时创建支付分服务订单
小程序->>后端: POST /api/device/scan-open
后端->>后端: scanOpenDoor()
后端->>后端: 创建本地订单
后端->>后端: 创建支付分服务订单
后端->>微信: createPayScoreOrder(orderId, openId)
微信-->>后端: 支付分服务订单号
后端->>后端: 保存 payScoreOrderId 到订单
end
rect rgb(255, 240, 240)
Note over 用户,微信: 订单完结自动扣费
后端->>后端: processPayScorePayment(order, finalAmount)
后端->>后端: 校验:支付分渠道 + 服务可用 + 订单未完结
后端->>微信: completePayScoreOrder(orderId, finalAmount)
微信->>微信: 自动扣款
微信-->>后端: 扣款结果
后端->>后端: 更新订单支付状态
end
rect rgb(245, 240, 255)
Note over 用户,微信: 支付分回调
微信->>后端: POST /api/payscore/callback
后端->>后端: 解析回调参数
后端->>后端: 更新订单支付分状态
后端-->>微信: 返回成功响应
end
stateDiagram-v2
[*] --> CREATED: 创建服务订单
CREATED --> DOING: 用户授权确认
DOING --> DONE: 完结扣费成功
DOING --> REVOKED: 取消订单
DONE --> [*]
REVOKED --> [*]
小程序购物页面的状态轮询与界面联动机制。
stateDiagram-v2
[*] --> opened: 开门成功
opened --> opened: 轮询设备状态(2s间隔)
opened --> closing: 门已关闭
opened --> error: 设备异常
closing --> closed: 获取到订单信息
closing --> error: 订单异常
closed --> [*]: 倒计时结束返回首页
error --> [*]: 返回首页
| 轮询类型 | 间隔 | 超时 | 说明 |
|---|---|---|---|
| 设备状态 | 2秒 | 120秒 | 等待门从 opened 变为 close |
| 识别结果 | 3秒 | - | 门关后轮询 AI 识别结果 |
| 订单信息 | 3秒 | - | 识别完成后轮询订单详情 |
onActivated):重新启动轮询onDeactivated):暂停轮询、清理定时器onHide):清理购物页面轮询状态运营平台通过管理后台远程控制设备。
sequenceDiagram
participant 运营人员
participant Web管理端 as haha-admin-web
participant Admin后端 as haha-admin
participant SDK as HahaClient
participant 哈哈零兽
rect rgb(245, 245, 245)
Note over 运营人员,哈哈零兽: 远程开门
运营人员->>Web管理端: 点击远程开门
Web管理端->>Admin后端: POST /admin/devices/{id}/open
Admin后端->>Admin后端: 查找设备
Admin后端->>SDK: openDoor(deviceId, ADMIN+timestamp, IN, doorIndex)
SDK->>哈哈零兽: POST /door/open
哈哈零兽-->>SDK: {activity_id}
SDK-->>Admin后端: OpenDoorResult
Admin后端-->>Web管理端: 操作结果
end
rect rgb(245, 245, 245)
Note over 运营人员,哈哈零兽: 设置温度
运营人员->>Web管理端: 输入温度值(-30~30℃)
Web管理端->>Admin后端: POST /admin/devices/{id}/temperature
Admin后端->>SDK: setTemperature(deviceId, temperature)
SDK->>哈哈零兽: 设置温度指令
SDK-->>Admin后端: 设置结果
Admin后端-->>Web管理端: 操作结果
end
rect rgb(245, 245, 245)
Note over 运营人员,哈哈零兽: 音量调节
运营人员->>Web管理端: 输入音量值(0~100)
Web管理端->>Admin后端: POST /admin/devices/{id}/volume
Admin后端->>SDK: setVolume(deviceId, volume)
SDK->>哈哈零兽: 音量调节指令
SDK-->>Admin后端: 设置结果
Admin后端-->>Web管理端: 操作结果
end
注意:管理后台开门默认为上货模式 (
openType=IN),outUserId格式为ADMIN+时间戳;小程序开门为消费模式 (openType=OUT),source=MINIAPP。
设备在线状态回调触发的告警流程。
flowchart TD
OnlineCallback[收到 ONLINE_STATUS 回调] --> ParseOnline[解析在线状态]
ParseOnline --> SaveOnlineRedis[保存在线状态到Redis 10min TTL]
SaveOnlineRedis --> CheckOnlineStatus{is_online?}
CheckOnlineStatus --> |0-离线| TriggerOfflineAlert[触发离线告警]
CheckOnlineStatus --> |1-在线| CheckBackOnline[检查是否需要恢复通知]
TriggerOfflineAlert --> AlertService[DeviceAlertService.processOfflineAlert]
AlertService --> NotifyWeCom[发送企业微信告警通知]
CheckBackOnline --> ProcessBackOnline[DeviceAlertService.processBackOnlineNotify]
ProcessBackOnline --> SendRecoverNotify[发送恢复通知]
SendRecoverNotify --> ExtractSignal[提取信号值 signal_val]
ExtractSignal --> CheckSignal{信号强度 <= 阈值?}
CheckSignal --> |是| WeakSignalAlert[触发弱信号告警]
CheckSignal --> |否| Done[完成]
WeakSignalAlert --> NotifyWeCom2[发送企业微信告警通知]
| 角色 | 说明 | 对应模块 |
|---|---|---|
| 用户 | 最终消费者,通过小程序扫码购物 | haha-mp |
| 运营人员 | 管理后台操作人员,远程控制设备 | haha-admin-web |
| 哈哈零兽 | 设备平台,负责设备控制、AI识别、订单生成 | 外部平台 (SDK对接) |
| haha-miniapp | 小程序后端,处理回调、订单结算 | haha-miniapp |
| haha-admin | 运营平台后端,设备管理、营销管理 | haha-admin |
| 微信支付分 | 免密支付渠道,先享后付 | 外部平台 (API对接) |
/api)| 接口 | 方法 | 说明 |
|---|---|---|
/api/device/scan-open |
POST | 扫码开门 |
/api/payscore/check-enable |
GET | 检查支付分开通状态 |
/api/payscore/enable |
POST | 开通支付分 |
/api/payscore/confirm-enable |
POST | 确认开通支付分 |
/api/payscore/callback |
POST | 支付分回调通知 |
/callback/haha)| 接口 | 方法 | 说明 |
|---|---|---|
/callback/haha/message |
POST | 消息回调统一入口 |
/callback/haha/order |
POST | 订单回调通知 |
/admin)| 接口 | 方法 | 说明 |
|---|---|---|
/admin/devices/{id}/open |
POST | 远程开门 |
/admin/devices/{id}/temperature |
POST | 设置温度 |
/admin/devices/{id}/volume |
POST | 音量调节 |