# 妈妈计数器 前后端匹配与多设备架构

## 一、前后端如何匹配

### 1.1 架构概览

```
┌─────────────────────────────────────────────────────────────────────┐
│                           用户设备层                                  │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │  设备 #1    │  │  设备 #2    │  │  设备 #3    │  │  设备 #4    │ │
│  │ 小宝的计数器 │  │ 二宝的计数器 │  │ 送给朋友A   │  │ 送给朋友B   │ │
│  │  (家里)     │  │  (家里)     │  │  (外部)     │  │  (外部)     │ │
│  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘  └──────┬──────┘ │
└─────────┼────────────────┼────────────────┼────────────────┼────────┘
          │                │                │                │
          │ MQTT/HTTP      │ MQTT/HTTP      │ MQTT/HTTP      │ MQTT/HTTP
          │ 每5分钟同步     │ 每5分钟同步     │ 每5分钟同步     │ 每5分钟同步
          │                │                │                │
          └────────────────┴────────────────┴────────────────┘
                                     │
                    ┌────────────────┼────────────────┐
                    │                │                │
          ┌─────────▼─────────┐ ┌────▼────┐  ┌───────▼────────┐
          │   设备网关服务     │ │  MQTT   │  │   云数据库      │
          │  (Device Gateway) │ │ Broker  │  │   (MongoDB)     │
          └─────────┬─────────┘ └─────────┘  └────────────────┘
                    │
                    │ REST API / WebSocket
                    │
┌───────────────────┼───────────────────────────────────────────────────┐
│                   │                                                   │
│  ┌────────────────▼──────────┐  ┌──────────────────────────────────┐  │
│  │      REST API 服务         │  │          Web 前端                 │  │
│  │  ┌──────────────────────┐  │  │  ┌────────────────────────────┐  │  │
│  │  │ /api/devices         │  │  │  │  数据看板 (按设备筛选)       │  │  │
│  │  │ /api/stats           │  │  │  │  日历热力图                   │  │  │
│  │  │ /api/voice-samples   │  │  │  │  声纹管理                    │  │  │
│  │  │ /api/shares          │  │  │  │  对比分析                    │  │  │
│  │  └──────────────────────┘  │  │  └────────────────────────────┘  │  │
│  └───────────────────────────┘  └──────────────────────────────────┘  │
│                                                                       │
└───────────────────────────────────────────────────────────────────────┘
```

### 1.2 设备身份识别

每个设备有**唯一ID**，基于ESP32芯片MAC地址生成:

```cpp
// 设备ID格式: MC-XXXXXXXX
// 示例: MC-A1B2C3D4
void generateDeviceID() {
    uint64_t chipid = ESP.getEfuseMac();
    snprintf(device_id, sizeof(device_id), "MC-%04X%08X", 
             (uint32_t)(chipid >> 32), (uint32_t)chipid);
}
```

**设备首次启动流程:**

```
[设备上电]
    ↓
[生成/读取设备ID] → Flash存储 (设备ID永久不变)
    ↓
[尝试连接WiFi] → 有配置? 直接连 : 启动AP配网模式
    ↓
[向服务器注册] → POST /devices/register
    ↓
[绑定到用户] → 用户扫码/输入设备ID绑定
    ↓
[开始正常工作] → 检测声音 + 同步数据
```

### 1.3 数据同步机制

**设备 → 服务器 (上报):**

```json
POST /api/devices/MC-A1B2C3D4/sync
{
    "device_id": "MC-A1B2C3D4",
    "timestamp": 1711098000,
    "today_count": 23,
    "periods": { "morning": 5, "forenoon": 8, "afternoon": 6, "evening": 4 },
    "battery": 78,
    "firmware": "2.0.0"
}
```

**服务器 → 设备 (配置下发):**

```json
Response:
{
    "server_time": 1711098005,
    "config_update": {
        "audio_threshold": 5200,  // 灵敏度调整
        "voice_model_version": "v3"  // 新声纹模型
    },
    "ota_update": null  // 或固件更新URL
}
```

### 1.4 用户如何查看数据

**方式1: Web页面 (多设备切换)**

```
用户登录 → 获取绑定设备列表 → 选择设备 → 显示该设备数据

URL结构:
- /dashboard          → 默认显示第一个设备
- /dashboard?device=MC-A1B2C3D4 → 指定设备
```

**方式2: 独立分享链接 (给非所有者)**

```
用户A创建分享链接 → 发送给用户B → B通过链接查看 (只读)

URL: /share/shr_abc123 (24小时有效)
```

---

## 二、多设备架构 (3-4个设备)

### 2.1 场景分析

| 场景 | 设备分配 | 数据隔离 | 权限 |
|------|----------|----------|------|
**家庭多孩** | 大宝一个，二宝一个 | 各自独立 | 父母查看全部 |
**送朋友** | 朋友A一个，朋友B一个 | 完全隔离 | 朋友只能看自己的 |
**混合** | 自家2个 + 送朋友2个 | 按设备隔离 | 所有者决定分享权限 |

### 2.2 数据模型设计

**设备表 (devices):**

```json
{
    "_id": "dev_MC-A1B2C3D4",
    "device_id": "MC-A1B2C3D4",
    "name": "小宝的计数器",           // 可自定义名称
    "owner_id": "usr_dad_001",        // 所有者用户ID
    "model": "MC-ESP32S3-185",
    "firmware_version": "2.0.0",
    "status": "online",
    "last_seen": "2026-03-22T15:30:00Z",
    "settings": {
        "audio_threshold": 5000,
        "flip_mute": true
    },
    "created_at": "2026-03-22T10:00:00Z",
    "shared_with": [                   // 分享给谁
        { "user_id": "usr_grandma_001", "permission": "read" },
        { "user_id": "usr_mom_001", "permission": "admin" }
    ]
}
```

**统计表 (stats_daily):**

```json
{
    "_id": "stat_xxx",
    "device_id": "MC-A1B2C3D4",       // 关联设备
    "date": "2026-03-22",
    "total": 59,
    "periods": { "morning": 12, "forenoon": 18, "afternoon": 15, "evening": 14 },
    "hourly": [...],
    "synced_at": "2026-03-22T23:59:59Z"
}
```

**用户表 (users):**

```json
{
    "_id": "usr_dad_001",
    "name": "爸爸",
    "email": "dad@example.com",
    "wechat_openid": "wx_xxx",
    "devices": ["MC-A1B2C3D4", "MC-E5F6G7H8"],  // 拥有的设备
    "shared_devices": ["MC-I9J0K1L2"],           // 别人分享给我的
    "created_at": "2026-03-22T10:00:00Z"
}
```

### 2.3 Web端多设备界面

**设备选择器 (顶部导航):**

```
┌─────────────────────────────────────────────────────────────┐
│ 👤 爸爸                                    [🔔] [⚙️] [👤]  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  选择设备:  [小宝的计数器 ▼]                               │
│             ├─ 小宝的计数器 (MC-A1B2C3D4) ● 在线           │
│             ├─ 二宝的计数器 (MC-E5F6G7H8) ● 在线           │
│             ├─ 朋友A的设备   (MC-I9J0K1L2) ○ 离线          │
│             └─ + 添加新设备                                │
│                                                             │
│  [📊 数据看板] [📅 日历] [📊 对比] [🎤 声纹] [⚙️ 设置]     │
└─────────────────────────────────────────────────────────────┘
```

**多设备对比视图:**

```
┌─────────────────────────────────────────────────────────────┐
│  📊 多设备对比                                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  选择对比设备:                                              │
│  ☑️ 小宝的计数器                                            │
│  ☑️ 二宝的计数器                                            │
│  ☐ 朋友A的设备 (无权限)                                     │
│                                                             │
│  ─────────────────────────────────────────────────────────  │
│                                                             │
│  今日数据对比:                                              │
│                                                             │
│  设备              次数    早晨   上午   下午   晚上        │
│  ─────────────────────────────────────────────────────      │
│  小宝的计数器       59      12     18     15     14         │
│  二宝的计数器       42       8     12     12     10         │
│                                                             │
│  💡 小宝今天比二宝多喊了17次妈妈                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

### 2.4 权限控制

| 角色 | 权限 | 说明 |
|------|------|------|
| **所有者 (Owner)** | 读/写/分享/删除 | 设备的拥有者，完全控制 |
| **管理员 (Admin)** | 读/写/设置 | 被所有者授权，可管理设置 |
| **观察者 (Viewer)** | 只读 | 只能查看数据，不能修改 |
| **临时访客 (Guest)** | 限时只读 | 通过分享链接访问，24小时有效 |

**权限检查流程:**

```javascript
function canAccessDevice(userId, deviceId, action) {
    const device = db.devices.findOne({ device_id: deviceId });
    
    // 检查是否是所有者
    if (device.owner_id === userId) return true;
    
    // 检查分享权限
    const share = device.shared_with.find(s => s.user_id === userId);
    if (!share) return false;
    
    // 根据action检查权限
    switch(action) {
        case 'read': return true;  // 所有分享都可以读
        case 'write': return share.permission === 'admin';
        case 'settings': return share.permission === 'admin';
        case 'delete': return false;  // 只有所有者可以删除
        default: return false;
    }
}
```

---

## 三、实际部署建议

### 3.1 最小可行方案 (MVP)

如果你现在就想让3-4个设备跑起来:

**方案: 静态JSON + 设备ID参数**

```javascript
// web-app/data/devices.js
const DEVICES_DATA = {
    "MC-A1B2C3D4": {
        name: "小宝的计数器",
        owner: "爸爸",
        stats: { /* 统计数据 */ }
    },
    "MC-E5F6G7H8": {
        name: "二宝的计数器",
        owner: "爸爸",
        stats: { /* 统计数据 */ }
    },
    "MC-I9J0K1L2": {
        name: "朋友A的设备",
        owner: "朋友A",
        stats: { /* 统计数据 */ }
    }
};

// URL: /web-app/?device=MC-A1B2C3D4
// 页面根据device参数加载对应数据
```

**固件修改:**

```cpp
// 修改 MamaCounter_ESP32S3.ino 中的 API_BASE_URL
const char* API_BASE_URL = "https://hiyascott.github.io/scott-portfolio/api";

// 使用静态JSON端点
// POST /api/data/MC-A1B2C3D4.json
// 存储到GitHub Pages (通过GitHub Actions自动更新)
```

### 3.2 完整云方案

如果需要真正的实时同步:

**后端技术栈:**
- **服务器**: Node.js + Express
- **数据库**: MongoDB (设备/用户/统计)
- **实时通信**: Socket.io (设备状态推送)
- **MQTT**: Mosquitto (设备消息队列)
- **部署**: VPS / 阿里云 / 腾讯云

**成本估算:**
- VPS (1核2G): ~￥50/月
- MongoDB (免费版): 512MB存储
- 域名 + HTTPS: ~￥50/年

### 3.3 设备分发流程

**你制作4个设备送给朋友:**

```
1. 烧录固件到4个ESP32
   ↓
2. 每个设备生成唯一ID
   - 设备A: MC-001A2B3C
   - 设备B: MC-004D5E6F
   - 设备C: MC-007G8H9I
   - 设备D: MC-010J1K2L
   ↓
3. 在Web端创建设备条目
   ↓
4. 送给朋友时告知:
   - 设备ID
   - 配网方法 (连接MamaCounter-XXXX热点)
   - 查看数据的网址
   ↓
5. 朋友收到后:
   - 配网 → 设备自动注册到云端
   - 访问网址查看自己设备的数据
```

---

## 四、总结

| 问题 | 答案 |
|------|------|
| 前后端如何匹配? | 设备通过唯一ID上报数据，Web端通过用户绑定关系查询数据 |
| 多设备数据隔离? | 每个设备独立存储，通过device_id关联，用户权限控制访问 |
| 独立信息页面? | 支持，每个设备有独立URL (?device=ID) 或在多设备界面切换 |
| 送朋友后谁看数据? | 朋友是设备所有者，自己看自己的；你可以被授权查看 |

**下一步行动:**
1. 硬件到了后烧录固件
2. 选择数据存储方案 (静态JSON or 云服务器)
3. 部署Web应用到GitHub Pages
4. 朋友配网后即可查看数据
