skyline пре 3 месеци
родитељ
комит
68289f6667
5 измењених фајлова са 150 додато и 833 уклоњено
  1. 0 153
      UX设计规范书.md
  2. 42 44
      haha-admin-mp/src/components/CustomTabBar.vue
  3. 108 182
      haha-admin-mp/src/pages/device/list.vue
  4. 0 218
      开发指导.md
  5. 0 236
      需求文档.md

+ 0 - 153
UX设计规范书.md

@@ -1,153 +0,0 @@
-这是一份专为 **UI/UX 设计师** 撰写的《智能视觉售卖机系统 UI 设计参考规范》。
-
-这份文档侧重于**视觉风格、交互体验、关键页面布局及特殊状态的处理**,旨在确保设计方案既美观又符合“视觉识别+即拿即走”的特殊业务场景。
-
----
-
-# 智能视觉售卖机系统 - UI/UX 设计规范书
-
-| 项目属性 | 定义 |
-| :--- | :--- |
-| **项目名称** | 智能视觉售卖机系统 |
-| **文档版本** | V1.0 (设计指导版) |
-| **设计核心** | **科技感 (Tech)**、**极简 (Minimalist)**、**信任感 (Trust)** |
-| **关键体验** | 减少焦虑感(处理识别延迟),强化证据链(视频展示) |
-| **适应终端** | 微信小程序 (C端/B端)、PC Web (B端管理后台) |
-
----
-
-## 1. 视觉体系定义 (Visual Identity)
-
-### 1.1 设计关键词
-*   **无感 (Seamless)**: 流程流畅,没有多余的点击。
-*   **通透 (Transparent)**: 价格透明、扣款透明、证据透明(建立对AI的信任)。
-*   **响应 (Responsive)**: 每一个操作(开门、关门、结算)都有明确的视觉反馈。
-
-### 1.2 建议配色方案
-由于涉及“支付”和“AI”,建议使用**蓝色系**作为主色,代表科技与安全;**绿色**代表通过/开门;**橙红色**用于警示/异常。
-
-*   **主色调 (Brand Color)**: `Technology Blue` (例如 `#2979FF` 或 `#0052D9`) - 用于核心按钮、选中状态。
-*   **辅助色 (Success)**: `Signal Green` (例如 `#00C853`) - 用于“门已开”、“支付成功”。
-*   **辅助色 (Warning)**: `Alert Orange` (例如 `#FF9100`) - 用于“补货差异”、“识别存疑”。
-*   **中性色**:
-    *   `Black` (#333333): 主标题。
-    *   `Grey` (#909399): 次要信息、辅助说明。
-    *   `Light Grey` (#F5F7FA): 背景色。
-
-### 1.3 字体与图标
-*   **数字字体**: 涉及金额、倒计时的数字,建议使用 **DIN** 或 **Roboto Mono** 等等宽字体,强调金融属性。
-*   **图标风格**: 建议使用 **线性图标 (Outline)**,选中状态为 **填充 (Filled)**。线条需圆润,减少锐利感。
-
----
-
-## 2. 用户端小程序 (C端) - 界面设计要点
-
-### 2.1 首页 (Home)
-首页即是“开门钥匙”,需极致简化。
-*   **背景**: 全屏地图 (Map),展示附近的点位锚点 (Marker)。
-*   **核心操作区 (Bottom Sheet)**:
-    *   **扫码按钮**: **巨大化设计**,悬浮在底部中央,带有呼吸动效或阴影,暗示“点击这里开始”。
-    *   **辅助信息**: 扫码按钮下方展示“微信支付分 | 550分以上免押金”字样,增加用户信任。
-*   **侧边/角落入口**: 个人中心(头像)、客服(耳机图标)、附近列表。
-
-### 2.2 状态机页面 (购物全流程 - 核心难点)
-这是一个根据状态变化的**动态页面**,切勿设计成多个割裂的页面。
-
-*   **状态 A: 开门申请中 (Loading)**
-    *   **视觉**: 锁定屏幕,中间显示圆形进度条。
-    *   **文案**: “正在安全检测...”、“正在解锁...”。
-*   **状态 B: 门已开 (Shopping)**
-    *   **视觉**: 
-        *   背景色转为**柔和的绿色渐变**。
-        *   中央大图标:**开锁图标** 或 **购物袋图标**。
-        *   **醒目文案**: “门已开,请选购”。
-        *   **底部提示**: “选购完成后,关门自动结算”。
-*   **状态 C: 识别结算中 (Processing - 需缓解焦虑)**
-    *   *注:此状态可能持续 5-15 秒。*
-    *   **视觉**:
-        *   **核心动效**: 一个“AI大脑”或“扫描光线”在不断分析商品的动画。
-        *   **文案轮播**: “AI正在清点商品...” -> “正在核对价格...” -> “即将生成账单...”。
-    *   **禁止**: 不要使用死板的“加载中”转圈,要让用户感觉系统在“思考”。
-*   **状态 D: 结算完成 (Receipt)**
-    *   **布局**: 小票样式 (Receipt Card)。
-    *   **顶部**: 支付金额(大字)。
-    *   **中部**: 商品列表(缩略图 + 名称 + 数量 + 单价)。
-    *   **底部**: “查看购物监控”按钮(线框按钮)。
-
-### 2.3 订单详情与证据链
-*   **视频播放器**:
-    *   放在页面顶部或显眼位置。
-    *   **默认状态**: 展示封面图 + 播放按钮 + “购物过程回放”标签。
-    *   **播放状态**: 简易播放器,支持全屏。
-*   **售后入口**:
-    *   在商品列表下方,设计“商品识别有误?”的文字链接。
-
----
-
-## 3. 商户运营端 (PC Web) - 界面设计要点
-
-### 3.1 布局框架
-*   使用 **侧边栏 (Sidebar) + 顶部导航 (Navbar)** 的经典布局(类似 Ant Design Pro / Element Plus admin)。
-*   **风格**: 干净、高对比度,适合长时间操作。
-
-### 3.2 商品管理 (AI 模型关联)
-此页面不同于普通电商后台。
-*   **图片上传区**: 
-    *   需强调图片规范。设计一个虚线框区域,并在旁边展示**“正确示例(白底)” vs “错误示例(杂乱背景)”**的对比图。
-*   **状态标签**:
-    *   设计 distinct 的状态 Tag:`未同步` (灰)、`训练中` (蓝)、`已生效` (绿)、`训练失败` (红)。
-
-### 3.3 挂起订单审核页 (人工介入)
-这是一个**分屏操作页**,用于处理AI识别不准的订单。
-*   **左侧 (40% 宽度)**: **视频播放区**。需支持逐帧播放、倍速播放。
-*   **右侧 (60% 宽度)**: **识别结果修正区**。
-    *   显示 AI 识别出的商品列表(带删除按钮)。
-    *   提供“添加商品”的搜索框。
-    *   底部固定栏:显示“修正后总金额”,以及“确认扣款”与“取消订单”按钮。
-
----
-
-## 4. 商家小程序 (B端) - 界面设计要点
-
-### 4.1 运维工作台
-*   **卡片式设计**: 每个设备是一个卡片。
-*   **状态指示灯**: 卡片右上角用颜色点表示状态(绿=在线,灰=离线,红=缺货/故障)。
-*   **关键数据**: 卡片内展示“当前SKU数 / 满载率”。
-
-### 4.2 补货确认页 (Before/After)
-运维关门后,系统会推送补货结果。
-*   **视觉重点**: 突出**“变化量”**。
-*   **列表样式**:
-    *   商品 A: `库存 5 -> 10` (+5) [高亮绿色]
-    *   商品 B: `库存 3 -> 2` (-1) [高亮红色,可能是拿走了]
-*   **修正交互**: 每一行提供 `+` `-` 步进器,允许运维快速修正数字。
-
----
-
-## 5. 组件与动效规范 (Interaction Specs)
-
-### 5.1 弹窗 (Dialog/Modal)
-*   **C端**: 使用底部弹窗 (Half-screen Dialog/Action Sheet) 为主,例如选择支付方式、查看优惠券。
-*   **B端**: 使用居中弹窗,用于确认操作。
-
-### 5.2 骨架屏 (Skeleton)
-*   在地图加载、订单列表加载、商品详情加载时,**必须**使用骨架屏过渡,避免页面白屏闪烁。
-
-### 5.3 异常反馈 (Toast/Alert)
-*   **轻微提示**: 顶部下滑 Toast (如“网络连接已恢复”)。
-*   **阻断提示**: 居中 Modal (如“您有未支付订单,请先处理”),配上警示插画。
-
----
-
-## 6. 设计交付物清单
-
-1.  **UI Kit**: 包含标准色板、字体规范、通用按钮、表单输入框、Iconfont/SVG图标集。
-2.  **关键页面高保真图 (Hi-Fi)**:
-    *   C端: 首页扫码态、开门鉴权弹窗、购物倒计时态、结算清单页、订单详情视频页。
-    *   B端 PC: 仪表盘、商品上传页、视频审核页。
-    *   B端 Mobile: 运维设备列表、补货差异核对页。
-3.  **动效 Demo**: “AI识别中”的加载动画演示 (MP4/Lottie/Gif)。
-
----
-
-这份文档请设计师结合具体的 UI 框架(如 uView UI 或 Element Plus)进行落地设计。

+ 42 - 44
haha-admin-mp/src/components/CustomTabBar.vue

@@ -145,32 +145,30 @@ const handleScan = () => {
   justify-content: center;
   flex: 1;
   height: 100%;
-  
-  .tab-icon {
-    width: 44rpx;
-    height: 44rpx;
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    
-    image {
-      width: 44rpx;
-      height: 44rpx;
-    }
-  }
-  
-  .tab-text {
-    font-size: 20rpx;
-    color: #94a3b8;
-    margin-top: 4rpx;
-  }
-  
-  &.active {
-    .tab-text {
-      color: #10b981;
-      font-weight: 500;
-    }
-  }
+}
+
+.tab-icon {
+  width: 44rpx;
+  height: 44rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.tab-icon image {
+  width: 44rpx;
+  height: 44rpx;
+}
+
+.tab-text {
+  font-size: 20rpx;
+  color: #94a3b8;
+  margin-top: 4rpx;
+}
+
+.tab-item.active .tab-text {
+  color: #10b981;
+  font-weight: 500;
 }
 
 /* 扫码按钮 */
@@ -195,11 +193,11 @@ const handleScan = () => {
   align-items: center;
   justify-content: center;
   box-shadow: 0 4rpx 16rpx rgba(16, 185, 129, 0.4);
-  
-  &:active {
-    transform: scale(0.95);
-    background: #059669;
-  }
+}
+
+.scan-btn:active {
+  transform: scale(0.95);
+  background: #059669;
 }
 
 .scan-icon {
@@ -209,19 +207,19 @@ const handleScan = () => {
   display: flex;
   align-items: center;
   justify-content: center;
-  
-  .scan-line {
-    position: absolute;
-    width: 24rpx;
-    height: 4rpx;
-    background: #ffffff;
-    border-radius: 2rpx;
-    
-    &.vertical {
-      width: 4rpx;
-      height: 24rpx;
-    }
-  }
+}
+
+.scan-line {
+  position: absolute;
+  width: 24rpx;
+  height: 4rpx;
+  background: #ffffff;
+  border-radius: 2rpx;
+}
+
+.scan-line.vertical {
+  width: 4rpx;
+  height: 24rpx;
 }
 
 .scan-text {

+ 108 - 182
haha-admin-mp/src/pages/device/list.vue

@@ -36,46 +36,27 @@
           :key="device.id"
           @click="goDetail(device.id)"
         >
-          <view class="card-header">
-            <view class="device-info">
+          <!-- 第一行:名称 + 编号 + 状态 -->
+          <view class="card-row-main">
+            <view class="device-title">
               <text class="device-name">{{ device.name }}</text>
               <text class="device-no">{{ device.deviceNo }}</text>
             </view>
-            <view class="status-badge" :class="getStatusClass(device.status)">
-              <view class="status-dot"></view>
+            <view class="status-tag" :class="getStatusClass(device.status)">
               <text>{{ getStatusText(device.status) }}</text>
             </view>
           </view>
           
-          <view class="card-body">
-            <view class="info-row">
-              <view class="info-icon shop">
-                <view class="icon-inner"></view>
-              </view>
+          <!-- 第二行:门店 + 销售数据 + 库存 -->
+          <view class="card-row-info">
+            <view class="info-left">
               <text class="info-text">{{ device.shopName }}</text>
+              <text class="info-divider" v-if="device.status === 1">·</text>
+              <text class="info-temp" v-if="device.status === 1">{{ device.temperature }}°C</text>
             </view>
-            <view class="info-row" v-if="device.status === 1">
-              <view class="info-icon temp">
-                <view class="icon-inner"></view>
-              </view>
-              <text class="info-text">温度: {{ device.temperature }}°C</text>
-            </view>
-          </view>
-          
-          <view class="card-stats">
-            <view class="stat-item">
-              <text class="stat-value accent">¥{{ formatMoney(device.todaySales) }}</text>
-              <text class="stat-label">今日销售</text>
-            </view>
-            <view class="stat-divider"></view>
-            <view class="stat-item">
-              <text class="stat-value">{{ device.inventory }}/{{ device.capacity }}</text>
-              <text class="stat-label">库存</text>
-            </view>
-            <view class="stat-divider"></view>
-            <view class="stat-item">
-              <text class="stat-value">{{ device.lastOnlineTime ? formatTime(device.lastOnlineTime) : '-' }}</text>
-              <text class="stat-label">最后在线</text>
+            <view class="info-right">
+              <text class="sales-value">¥{{ formatMoney(device.todaySales) }}</text>
+              <text class="stock-value">{{ device.inventory }}/{{ device.capacity }}</text>
             </view>
           </view>
         </view>
@@ -258,15 +239,15 @@ onMounted(() => {
 }
 
 .device-list {
-  padding: 16rpx 24rpx;
+  padding: 12rpx 24rpx;
 }
 
 .device-card {
   background: #ffffff;
   border: 1rpx solid #e2e8f0;
-  border-radius: 16rpx;
-  margin-bottom: 12rpx;
-  overflow: hidden;
+  border-radius: 12rpx;
+  margin-bottom: 8rpx;
+  padding: 14rpx 16rpx;
   transition: transform 0.15s;
   
   &:active {
@@ -274,170 +255,115 @@ onMounted(() => {
   }
 }
 
-.card-header {
+/* 第一行:名称 + 状态 */
+.card-row-main {
   display: flex;
   justify-content: space-between;
-  align-items: flex-start;
-  padding: 20rpx 24rpx 16rpx;
+  align-items: center;
+  margin-bottom: 8rpx;
+}
+
+.device-title {
+  display: flex;
+  align-items: center;
+  flex: 1;
+  min-width: 0;
   
-  .device-info {
-    .device-name {
-      display: block;
-      font-size: 30rpx;
-      font-weight: 600;
-      color: #1e293b;
-      margin-bottom: 4rpx;
-    }
-    
-    .device-no {
-      font-size: 22rpx;
-      color: #94a3b8;
-      font-family: monospace;
-    }
+  .device-name {
+    font-size: 28rpx;
+    font-weight: 600;
+    color: #1e293b;
+    margin-right: 12rpx;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
   }
   
-  .status-badge {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    gap: 6rpx;
-    padding: 8rpx 16rpx;
-    border-radius: 8rpx;
-    font-size: 22rpx;
-    font-weight: 500;
-    
-    .status-dot {
-      width: 8rpx;
-      height: 8rpx;
-      border-radius: 50%;
-    }
-    
-    &.online {
-      background: #ecfdf5;
-      color: #10b981;
-      .status-dot { background: #10b981; }
-    }
-    
-    &.offline {
-      background: #fff7ed;
-      color: #f97316;
-      .status-dot { background: #f97316; }
-    }
-    
-    &.maintenance {
-      background: #faf5ff;
-      color: #a855f7;
-      .status-dot { background: #a855f7; }
-    }
+  .device-no {
+    font-size: 20rpx;
+    color: #94a3b8;
+    font-family: monospace;
+    flex-shrink: 0;
   }
 }
 
-.card-body {
-  padding: 0 24rpx 16rpx;
+.status-tag {
+  padding: 4rpx 12rpx;
+  border-radius: 6rpx;
+  font-size: 20rpx;
+  font-weight: 500;
+  flex-shrink: 0;
+  margin-left: 12rpx;
   
-  .info-row {
-    display: flex;
-    align-items: center;
-    margin-bottom: 8rpx;
-    
-    &:last-child {
-      margin-bottom: 0;
-    }
-    
-    .info-icon {
-      width: 28rpx;
-      height: 28rpx;
-      border-radius: 6rpx;
-      margin-right: 10rpx;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      
-      &.shop {
-        background: #fff7ed;
-        
-        .icon-inner {
-          width: 14rpx;
-          height: 12rpx;
-          background: #f97316;
-          border-radius: 0 0 3rpx 3rpx;
-          position: relative;
-          
-          &::before {
-            content: '';
-            position: absolute;
-            top: -6rpx;
-            left: 50%;
-            transform: translateX(-50%);
-            width: 0;
-            height: 0;
-            border-left: 8rpx solid transparent;
-            border-right: 8rpx solid transparent;
-            border-bottom: 6rpx solid #f97316;
-          }
-        }
-      }
-      
-      &.temp {
-        background: #ecfdf5;
-        
-        .icon-inner {
-          width: 12rpx;
-          height: 12rpx;
-          border: 3rpx solid #10b981;
-          border-radius: 50%;
-        }
-      }
-    }
-    
-    .info-text {
-      font-size: 26rpx;
-      color: #475569;
-    }
+  &.online {
+    background: #ecfdf5;
+    color: #10b981;
+  }
+  
+  &.offline {
+    background: #fff7ed;
+    color: #f97316;
+  }
+  
+  &.maintenance {
+    background: #faf5ff;
+    color: #a855f7;
   }
 }
 
-.card-stats {
+/* 第二行:门店 + 销售数据 */
+.card-row-info {
   display: flex;
+  justify-content: space-between;
   align-items: center;
-  background: #f8fafc;
-  border-top: 1rpx solid #f1f5f9;
-  padding: 16rpx 12rpx;
+}
+
+.info-left {
+  display: flex;
+  align-items: center;
+  flex: 1;
+  min-width: 0;
   
-  .stat-item {
-    flex: 1;
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    
-    .stat-value {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 26rpx;
-      font-weight: 600;
-      color: #1e293b;
-      margin-bottom: 4rpx;
-      
-      &.accent {
-        color: #f97316;
-      }
-    }
-    
-    .stat-label {
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      font-size: 20rpx;
-      color: #94a3b8;
-    }
+  .info-text {
+    font-size: 22rpx;
+    color: #64748b;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
   }
   
-  .stat-divider {
-    width: 1rpx;
-    height: 40rpx;
-    background: #e2e8f0;
+  .info-divider {
+    margin: 0 8rpx;
+    color: #cbd5e1;
+    font-size: 22rpx;
+  }
+  
+  .info-temp {
+    font-size: 22rpx;
+    color: #10b981;
+    font-weight: 500;
+  }
+}
+
+.info-right {
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+  margin-left: 16rpx;
+  
+  .sales-value {
+    font-size: 24rpx;
+    font-weight: 600;
+    color: #f97316;
+    margin-right: 16rpx;
+  }
+  
+  .stock-value {
+    font-size: 22rpx;
+    color: #64748b;
+    background: #f1f5f9;
+    padding: 4rpx 10rpx;
+    border-radius: 4rpx;
   }
 }
 

+ 0 - 218
开发指导.md

@@ -1,218 +0,0 @@
-这是一份面向**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 补货逻辑** 部分,这是最容易出错的环节。

+ 0 - 236
需求文档.md

@@ -1,236 +0,0 @@
-这是一个纯粹的、面向业务逻辑和交互细节的**产品需求文档 (PRD)**。
-
-该文档完全剔除了技术实现代码(如Java类、数据库SQL、API参数),专注于**功能定义、业务流程、界面元素、交互逻辑及异常处理**,旨在让UI设计师知道画什么,让前端/后端开发人员知道业务逻辑是什么。
-
----
-
-## 目录
-1. [产品全局概述](#1-产品全局概述)
-   - [1.1 产品定位](#11-产品定位)
-   - [1.2 核心业务流程](#12-核心业务流程)
-   - [1.3 关键逻辑说明](#13-关键逻辑说明)
-2. [用户端 - 微信小程序 (C端)](#2-用户端---微信小程序-c端)
-   - [2.1 首页](#21-首页)
-   - [2.2 开门鉴权流程](#22-开门鉴权流程)
-   - [2.3 购物进行中](#23-购物进行中)
-   - [2.4 订单结算页](#24-订单结算页)
-   - [2.5 个人中心](#25-个人中心)
-3. [商户运营端 - PC管理后台 (B端)](#3-商户运营端---pc管理后台-b端)
-   - [3.1 仪表盘](#31-仪表盘)
-   - [3.2 商品管理](#32-商品管理)
-   - [3.3 设备管理](#33-设备管理)
-   - [3.4 订单中心 & 异常处理](#34-订单中心--异常处理)
-   - [3.5 营销中心](#35-营销中心)
-   - [3.6 财务对账](#36-财务对账)
-4. [商家运营端 - 微信小程序 (移动运维)](#4-商家运营端---微信小程序-移动运维)
-   - [4.1 工作台首页](#41-工作台首页)
-   - [4.2 智能补货](#42-智能补货)
-   - [4.3 设备控制](#43-设备控制)
-5. [全局异常逻辑与风控需求](#5-全局异常逻辑与风控需求)
-   - [5.1 逃单与坏账处理](#51-逃单与坏账处理)
-   - [5.2 网络异常处理](#52-网络异常处理)
-   - [5.3 门锁安全](#53-门锁安全)
-6. [交互体验细节](#6-交互体验细节)
-
----
-
-# 智能视觉识别售卖机系统 - 产品需求文档 (PRD)
-
-| 文档属性 | 内容 |
-| :--- | :--- |
-| **项目名称** | 智能视觉识别售卖机系统 |
-| **文档版本** | V1.0 (产品核心版) |
-| **核心模式** | 视觉识别 + 信用免密支付 + 即拿即走 |
-| **底层支撑** | 哈哈零兽 (Haha Lingshou) 智能柜能力 |
-
----
-
-### 版本控制历史
-| 版本号 | 修改日期 | 修改人 | 修改内容 |
-| :--- | :--- | :--- | :--- |
-| V1.0 | 2026-01-23 | 系统 | 初始版本,完成核心功能需求文档 |
-
----
-
-### 术语定义
-| 术语 | 解释 |
-| :--- | :--- |
-| 视觉识别 | 基于人工智能技术,通过摄像头拍摄的视频或图像识别商品的技术 |
-| 信用免密支付 | 基于用户的信用评分(如微信支付分、支付宝芝麻分),无需输入密码即可完成支付的方式 |
-| 即拿即走 | 用户扫码开门后,可直接拿走商品,关门后系统自动识别并扣款的购物模式 |
-| 后结算模式 | 先购物后结算的模式,用户先拿走商品,系统后识别并扣款 |
-| 挂起订单 | 当AI识别置信度低于85%时,系统不自动扣款,生成需要人工审核的订单 |
-| 虚拟货架 | 虽然是视觉柜,但仍需配置的商品摆放信息,用于计算满载率 |
-| 快照对比模式 | 运维补货时,系统对比开门前和关门后的商品快照,自动盘点库存的模式 |
-
----
-
-## 1. 产品全局概述
-
-### 1.1 产品定位
-一款基于人工智能视觉识别技术的智能零售终端管理系统。用户无需逐个扫码商品,只需扫码开门,自由选购,关门后系统自动识别拿走的商品并完成扣款,实现“无感支付”购物体验。
-
-### 1.2 核心业务流程
-1.  **用户**:扫码 -> 授权(微信/支付宝分)-> 开门 -> 拿货 -> 关门 -> 收到扣款通知。
-2.  **运营**:PC端配置商品AI模型 -> 同步设备。
-3.  **运维**:小程序扫码开门 -> 补货 -> 关门 -> 系统自动盘点库存。
-
-### 1.3 关键逻辑说明 (基于视觉柜特性)
-*   **后结算模式**:先购物后结算,必须强依赖“微信支付分”或“支付宝芝麻分”进行风控。
-*   **识别延迟**:用户关门后,云端AI需要5-15秒分析视频,因此订单生成是异步的,前端需有对应的等待交互。
-*   **证据链**:每一笔订单必须关联一段“购物视频”,用于解决用户对AI识别结果的异议。
-
----
-
-## 2. 用户端 - 微信小程序 (C端)
-
-### 2.1 首页
-*   **页面结构**:
-    *   **顶部**:黄色导航栏,左侧房子图标,中间显示“AI零售柜”标题,右侧三个图标(更多、收起、设置)。
-    *   **中部**:背景为商品图案,中间显示“AI零售柜”大标题。
-    *   **底部(核心)**:黄色大圆形按钮,上面有扫码图标和“扫码开门”文字。
-    *   **底部两侧**:左侧“我的”图标和文字,右侧“退款”图标和文字。
-    *   **底部信息**:显示“微信支付分 | 550分及以上优享”和“客服电话:”文字。
-*   **交互逻辑**:
-    *   点击“扫码开门” -> 调用摄像头。
-    *   解析二维码:若非本机柜二维码,提示“无效二维码”;若是,进入开门鉴权流程。
-
-### 2.2 开门鉴权流程 (核心交互)
-*   **登录判断**:未登录用户强制拉起微信手机号授权登录。
-*   **风控前置检查**:
-    1.  **检查未完成订单**:若用户有一笔订单状态为“识别中”或“待支付”,弹窗提示“您有上一笔订单正在处理,请稍候再试”,禁止开门。
-    2.  **检查信用分**:
-        *   调用支付分接口。若分数 < 550(后台可配),弹窗提示“您的信用分不足,请充值余额使用”。
-        *   若未签约免密支付,跳转至微信/支付宝签约页。
-*   **开门反馈**:
-    *   **开门成功**:手机震动反馈,跳转至【购物进行中】页面。
-    *   **开门失败**:弹窗提示具体原因(如:设备离线、设备被占用、库存不足)。
-
-### 2.3 购物进行中 (状态机页面)
-此页面根据设备状态实时变化,不仅是静态页。
-
-*   **阶段一:选购中 (门已开)**
-    *   **文案**:大字提示“门已开,请选购商品”。
-    *   **操作**:显示“遇到问题?(如门没开)”链接,点击可申请“辅助远程开门”或“报修”。
-    *   **倒计时**:若开门超过60秒未关门,页面变红提示“请尽快关门”。
-*   **阶段二:结算中 (门已关)**
-    *   **触发条件**:用户关上柜门。
-    *   **交互**:展示动态Loading动画,文案“AI正在识别商品,请稍候...”。
-    *   **超时处理**:若等待超过30秒未出结果,页面自动跳转至首页,并顶部通告“订单处理中,稍后将推送账单通知”。
-
-### 2.4 订单结算页
-*   **展示要素**:
-    *   购物清单:商品图、名称、数量、单价。
-    *   优惠明细:优惠券扣减、会员折扣。
-    *   实付金额:突出显示。
-*   **支付逻辑**:
-    *   系统默认自动发起免密扣款。
-    *   **扣款成功**:显示“支付成功”,展示“查看购物视频”按钮。
-    *   **扣款失败**(如余额不足):显示“支付失败”,底部出现“立即支付”按钮,引导用户手动完成支付。
-
-### 2.5 个人中心
-*   **我的订单**:
-    *   列表页显示状态:识别中、已完成、待付款、退款/售后。
-    *   **详情页特色功能**:
-        *   **购物回放**:内嵌视频播放器,播放哈哈零兽API返回的该笔交易监控片段(用户存疑时的核心证据)。
-        *   **申请售后**:针对识别错误的商品,用户可勾选并上传照片申诉。
-*   **钱包/卡包**:
-    *   余额充值:固定金额充值卡片(充100送5)。
-    *   免密管理:查看当前免密支付签约状态,支持解约(需校验无欠款)。
-    *   优惠券:查看可用/失效券。
-
----
-
-## 3. 商户运营端 - PC管理后台 (B端)
-
-### 3.1 仪表盘 (Dashboard)
-*   **核心数据**:今日销售额、今日订单数、客单价、新增用户数。
-*   **设备概况**:在线设备数、离线设备数、**缺货设备数**(库存<20%)、**异常设备数**(门未关/摄像头故障)。
-*   **待办事项**:待审核的挂起订单、待处理的退款申请。
-
-### 3.2 商品管理 (关联AI模型)
-由于视觉识别依赖图片训练,此模块与传统电商不同。
-*   **商品库**:
-    *   **基础信息**:商品名、分类、成本价、零售价、规格。
-    *   **条码 (Barcode)**:必须录入标准的69码。
-    *   **AI模型图 (关键)**:
-        *   上传要求:必须上传**白底图**或**6个面的包装展开图**。
-        *   **同步状态**:显示该商品在底层AI库的状态(未同步、同步中、已生效、训练失败)。
-        *   *逻辑约束*:未通过“已生效”状态的商品,严禁上架到设备中(因为AI不认识,会导致无法扣款)。
-
-### 3.3 设备管理
-*   **设备档案**:
-    *   绑定:输入机身序列号 (SN) 绑定到商户。
-    *   点位信息:所属区域(省市区)、详细地址(写字楼/学校)、经纬度(用于小程序地图)。
-*   **虚拟货架**:虽然是视觉柜,但需配置“最大容积”或“推荐摆放图”,以便计算满载率。
-*   **二维码下载**:生成设备专属的开门二维码,用于打印张贴。
-
-### 3.4 订单中心 & 异常处理
-*   **订单列表**:包含所有交易流水。
-*   **挂起订单审核 (特色功能)**:
-    *   **场景**:当AI识别置信度低于85%(如遮挡严重),系统不自动扣款,生成“挂起单”。
-    *   **操作界面**:左侧播放购物视频,右侧显示AI识别出的疑似商品列表。
-    *   **人工修正**:管理员根据视频,勾选或修改实际拿走的商品及数量,点击“确认扣款”,系统再向用户发起扣款。
-
-### 3.5 营销中心
-*   **优惠券配置**:满减券、折扣券、单品券。支持设置有效期、发放总量。
-*   **活动规则**:新用户首单立减、充值赠送规则。
-
-### 3.6 财务对账
-*   **资金流水**:每一笔订单的微信/支付宝交易单号、手续费、入账金额。
-*   **异常账单**:统计“已拿货但扣款失败(坏账)”的记录。
-
----
-
-## 4. 商家运营端 - 微信小程序 (移动运维)
-
-### 4.1 工作台首页
-*   **看板**:展示该运维人员负责区域的设备状态(正常/缺货/离线)。
-*   **快捷入口**:扫码补货、远程开门、故障上报。
-
-### 4.2 智能补货 (快照对比模式)
-*   **场景**:运维人员到达设备前进行补货。
-*   **流程**:
-    1.  **身份识别**:小程序扫码,系统识别为“运维人员”。
-    2.  **开门类型选择**:选择“补货开门”(区别于测试开门或购物开门)。
-    3.  **物理补货**:柜门弹开,运维整理货层,放入商品,关门。
-    4.  **自动盘点**:
-        *   关门后,云端对比“开门前快照”与“关门后快照”。
-        *   **结果确认**:小程序弹出“补货清单确认页”,显示系统识别到的【新增商品及数量】。
-        *   **人工修正**:若AI识别有误(如多识别了一瓶水),运维人员可手动修改数量,点击“确认提交”。系统以运维提交的数据更新本地库存。
-
-### 4.3 设备控制
-*   **远程开门**:用于解决用户扫码无法开门,或门锁卡住的紧急情况。需记录操作日志及原因。
-*   **设备重启**:下发远程指令重启安卓工控机。
-
----
-
-## 5. 全局异常逻辑与风控需求
-
-### 5.1 逃单与坏账处理
-*   **余额不足**:若用户拿走商品后,免密扣款失败。
-    *   系统自动将该用户标记为“欠费状态”。
-    *   小程序端:用户下次打开小程序,弹窗强制要求补缴欠款,否则无法使用任何功能。
-    *   短信触达:发送欠费催缴短信。
-*   **恶意行为**:若识别到视频中有遮挡摄像头、暴力掰门行为,管理员可在后台将该用户拉入黑名单,永久禁止扫码。
-
-### 5.2 网络异常处理
-*   **断网购物**:
-    *   视觉柜强依赖网络上传视频。若检测到网络信号极差,用户扫码时应直接提示“设备网络不佳,暂停服务”,防止开门后视频无法上传导致无法结算。
-*   **开门超时**:
-    *   扫码后若5秒内未收到开门成功指令,小程序自动停止Loading并提示“连接超时,请重试”。
-
-### 5.3 门锁安全
-*   **门未关报警**:若门锁传感器检测到开启状态超过5分钟,向商家端小程序推送最高级别报警,并发送短信给运维人员。
-
----
-
-## 6. 交互体验细节 (UX Requirements)
-
-1.  **响应速度**:扫码到开门动作必须在 3秒内 完成(依赖后端优化与Redis缓存)。
-2.  **Loading动效**:在“识别结算中”阶段,必须使用趣味性文案(如“AI正在数数...”)或进度条,缓解用户的焦虑感。
-3.  **视频播放**:订单详情页的视频播放需支持全屏、拖动进度条,且默认静音,点击开启声音。
-
----
-
-这份文档通过业务视角,详细规定了系统应该“长什么样”以及“怎么工作”,开发团队可依据此文档进行详细的技术方案设计。