# 系统交互流程

## 1. 核心购物流程

用户通过小程序扫码开门、选购商品、自动结算的完整交互流程。

```mermaid
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
```

## 2. 回调通知体系

哈哈零兽平台通过两类回调地址推送通知，后端统一在 [CallbackController](haha-miniapp/src/main/java/com/haha/miniapp/controller/CallbackController.java) 接收处理。

### 2.1 消息回调 (`/callback/haha/message`)

| notify_type | 说明 | 处理逻辑 |
|-------------|------|----------|
| DEVICE_STATUS | 开关门状态通知 | 更新 Redis 缓存 + 数据库门状态 + 开门记录 |
| ONLINE_STATUS | 设备在线状态通知 | 更新 Redis 缓存 + 离线告警/上线恢复/弱信号告警 |
| VOICE_RESULT | 音量调节结果通知 | 更新 Redis 缓存 |
| ORC_RESULT | AI识别结果通知 | 保存识别结果 + 创建/更新订单 + 关门状态处理 |
| CLIENT_NEW_PRODUCT | 新品审核结果回调 | 更新申请记录 + 同步商品到商家库 |
| MERGE_PRODUCT | 商品合并结果通知 | 记录合并日志 |

### 2.2 订单回调 (`/callback/haha/order`)

哈哈零兽生成订单后推送的完整订单信息，包含订单号、金额、商品明细等。

### 2.3 回调处理流程

```mermaid
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
```

## 3. 扫码开门详细流程

```mermaid
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
```

## 4. 订单结算流程

订单回调触发完整的结算链路，包括金额更新、优惠券扣减、支付分扣费。

```mermaid
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[结算完成]
```

### 4.1 金额流转说明

| 阶段 | 负责方 | totalAmount | discountAmount | paidAmount |
|------|--------|-------------|----------------|------------|
| 开门记录 | DeviceServiceImpl | - | - | - |
| AI识别回调 | HahaCallbackServiceImpl | 0 | 0 | 0 |
| 订单回调 | HahaCallbackServiceImpl | 设备传来 | 0 | = totalAmount |
| 优惠券扣减 | HahaCallbackServiceImpl | 不变 | 优惠券金额 | = total - discount |
| 支付扣费 | PayScoreService | 不变 | 不变 | 使用此金额扣费 |

### 4.2 优惠券扣减规则

- **排序策略**：按到期时间升序，优先使用最先到期的券
- **使用限制**：一次订单只能使用一张优惠券
- **适用范围**：
  - `applyScope=1`：全场通用
  - `applyScope=2`：指定门店
  - `applyScope=3`：指定商品
- **优惠金额上限**：不超过订单金额
- **优惠券类型**：
  - `type=1` 满减券：直接返回 discountValue
  - `type=2` 折扣券：orderAmount × (1 - discountValue/10)，受 maxDiscount 上限约束
  - `type=3` 抵扣券：直接返回 discountValue
  - `type=4` 兑换券：不参与金额扣减

## 5. 微信支付分集成流程

```mermaid
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
```

### 5.1 支付分状态流转

```mermaid
stateDiagram-v2
    [*] --> CREATED: 创建服务订单
    CREATED --> DOING: 用户授权确认
    DOING --> DONE: 完结扣费成功
    DOING --> REVOKED: 取消订单
    DONE --> [*]
    REVOKED --> [*]
```

## 6. 购物页面状态流转

小程序购物页面的状态轮询与界面联动机制。

```mermaid
stateDiagram-v2
    [*] --> opened: 开门成功
    opened --> opened: 轮询设备状态(2s间隔)
    opened --> closing: 门已关闭
    opened --> error: 设备异常

    closing --> closed: 获取到订单信息
    closing --> error: 订单异常

    closed --> [*]: 倒计时结束返回首页
    error --> [*]: 返回首页
```

### 6.1 轮询机制

| 轮询类型 | 间隔 | 超时 | 说明 |
|----------|------|------|------|
| 设备状态 | 2秒 | 120秒 | 等待门从 opened 变为 close |
| 识别结果 | 3秒 | - | 门关后轮询 AI 识别结果 |
| 订单信息 | 3秒 | - | 识别完成后轮询订单详情 |

### 6.2 购物页生命周期

- **组件激活** (`onActivated`)：重新启动轮询
- **组件失活** (`onDeactivated`)：暂停轮询、清理定时器
- **应用隐藏** (`onHide`)：清理购物页面轮询状态
- **组件卸载**：清理所有定时器和轮询

## 7. 管理后台远程控制

运营平台通过管理后台远程控制设备。

```mermaid
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`。

## 8. 设备告警体系

设备在线状态回调触发的告警流程。

```mermaid
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[发送企业微信告警通知]
```

## 9. 系统角色说明

| 角色 | 说明 | 对应模块 |
|------|------|----------|
| 用户 | 最终消费者，通过小程序扫码购物 | haha-mp |
| 运营人员 | 管理后台操作人员，远程控制设备 | haha-admin-web |
| 哈哈零兽 | 设备平台，负责设备控制、AI识别、订单生成 | 外部平台 (SDK对接) |
| haha-miniapp | 小程序后端，处理回调、订单结算 | haha-miniapp |
| haha-admin | 运营平台后端，设备管理、营销管理 | haha-admin |
| 微信支付分 | 免密支付渠道，先享后付 | 外部平台 (API对接) |

## 10. 关键接口索引

### 10.1 小程序端 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 | 支付分回调通知 |

### 10.2 回调接口 (`/callback/haha`)

| 接口 | 方法 | 说明 |
|------|------|------|
| `/callback/haha/message` | POST | 消息回调统一入口 |
| `/callback/haha/order` | POST | 订单回调通知 |

### 10.3 管理端 API (`/admin`)

| 接口 | 方法 | 说明 |
|------|------|------|
| `/admin/devices/{id}/open` | POST | 远程开门 |
| `/admin/devices/{id}/temperature` | POST | 设置温度 |
| `/admin/devices/{id}/volume` | POST | 音量调节 |
