banxiang/docs/技术方案-V1.2-进阶架构.md
2026-02-18 18:06:31 +00:00

18 KiB
Raw Blame History

slug
tech-v1-2-advanced

「伴享」技术方案 V1.2 — 进阶架构

版本: V1.2
对应PRD: PRD-V1.2-功能丰富版
更新日期: 2026-02-16


1. 架构变更概述

V1.2引入两个重大变更:健康数据架构(时序数据+预警引擎)和子女端小程序(微信小程序+独立API层

1.1 变更影响矩阵

层级 变更 说明
数据库 新增8张表 健康设备/记录/预警/报告+子女用户+关联+社区内容
API 新增3组端点 /health, /child, /posts
新增服务 健康数据同步Worker 定时拉取手环数据
新增服务 预警检测引擎 实时异常检测
新增客户端 微信小程序(子女端) Taro + React
新增服务产品 候鸟/文教/设备 复用service_products表

1.2 架构图更新

┌───────────────────────────────────────────────────────┐
│                      客户端层                          │
│  ┌──────────────┐  ┌──────────────┐  ┌────────────┐  │
│  │ Flutter APP  │  │ 子女端小程序  │  │ 代付H5     │  │
│  │ (父母端)     │  │ (Taro+React) │  │ (Vue3)     │  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬─────┘  │
└─────────┼─────────────────┼─────────────────┼─────────┘
          │                 │                 │
┌─────────▼─────────────────▼─────────────────▼─────────┐
│                   API网关 (Nginx)                       │
│  /api/v1/*  →  主APP API                                │
│  /api/child/* → 子女端API                               │
│  /api/h5/*   → H5页面API                                │
└─────────┬─────────────────────────────────────────────┘
          │
┌─────────▼─────────────────────────────────────────────┐
│              Node.js 业务服务层                          │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐       │
│  │用户  │ │活动  │ │AI    │ │医疗  │ │支付  │       │
│  └──────┘ └──────┘ └──────┘ └──────┘ └──────┘       │
│  ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐       │
│  │队长  │ │会员  │ │健康⭐│ │子女⭐│ │社区⭐│       │
│  └──────┘ └──────┘ └──────┘ └──────┘ └──────┘       │
└─────────┬─────────────────────────────────────────────┘
          │
┌─────────▼─────────────────────────────────────────────┐
│                 后台Worker层新增                     │
│  ┌─────────────────┐  ┌─────────────────┐             │
│  │ 健康数据同步Worker│  │ 预警检测引擎    │             │
│  │ (每30分钟/每小时) │  │ (实时处理)      │             │
│  └─────────────────┘  └─────────────────┘             │
└───────────────────────────────────────────────────────┘

2. 健康数据架构

2.1 数据流

华为健康API / 小米运动API
     ↓ (OAuth2 + 定时拉取)
┌─────────────────────┐
│  健康数据同步Worker  │  ← Bull队列 + Redis
│  每30分钟执行一次    │
└──────────┬──────────┘
           ↓ 写入
┌─────────────────────┐
│  health_records表   │  ← 时序数据
└──────────┬──────────┘
           ↓ 触发
┌─────────────────────┐
│  预警检测引擎       │
│  检查阈值 →          │
│  ├── 正常 → 跳过     │
│  └── 异常 →          │
│      ├── 写入alerts  │
│      ├── Push用户    │
│      └── Push子女端  │
└─────────────────────┘

2.2 增量DDL

-- ===========================
-- V1.2 增量DDL
-- ===========================

-- 1. 健康设备绑定
CREATE TABLE health_devices (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT REFERENCES users(id),
  brand VARCHAR(20) NOT NULL,
  device_model VARCHAR(50),
  device_id VARCHAR(100),
  auth_token_encrypted TEXT,
  refresh_token_encrypted TEXT,
  sync_interval INTEGER DEFAULT 60,
  last_sync_at TIMESTAMP,
  status VARCHAR(20) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT NOW()
);

-- 2. 健康记录(时序数据,按月分区)
CREATE TABLE health_records (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT NOT NULL,
  record_type VARCHAR(20) NOT NULL,
  value JSONB NOT NULL,
  recorded_at TIMESTAMP NOT NULL,
  source VARCHAR(20) DEFAULT 'device',
  created_at TIMESTAMP DEFAULT NOW()
) PARTITION BY RANGE (recorded_at);

-- 按月创建分区(示例)
CREATE TABLE health_records_2026_04 PARTITION OF health_records
  FOR VALUES FROM ('2026-04-01') TO ('2026-05-01');
CREATE TABLE health_records_2026_05 PARTITION OF health_records
  FOR VALUES FROM ('2026-05-01') TO ('2026-06-01');

CREATE INDEX idx_health_user_type_time 
  ON health_records(user_id, record_type, recorded_at DESC);

-- 3. 健康预警
CREATE TABLE health_alerts (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT REFERENCES users(id),
  alert_type VARCHAR(50),
  record_id BIGINT,
  value JSONB,
  threshold JSONB,
  severity VARCHAR(20) DEFAULT 'warning',
  notified_user BOOLEAN DEFAULT FALSE,
  notified_family BOOLEAN DEFAULT FALSE,
  acknowledged BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 4. 健康周报
CREATE TABLE health_reports (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT REFERENCES users(id),
  report_type VARCHAR(20) DEFAULT 'weekly',
  period_start DATE,
  period_end DATE,
  content JSONB,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 5. 子女用户
CREATE TABLE child_users (
  id BIGSERIAL PRIMARY KEY,
  wx_openid VARCHAR(50) UNIQUE NOT NULL,
  wx_unionid VARCHAR(50),
  nickname VARCHAR(50),
  avatar_url TEXT,
  phone VARCHAR(11),
  created_at TIMESTAMP DEFAULT NOW()
);

-- 6. 子女-父母关联
CREATE TABLE child_parent_links (
  id BIGSERIAL PRIMARY KEY,
  child_id BIGINT REFERENCES child_users(id),
  parent_user_id BIGINT REFERENCES users(id),
  relationship VARCHAR(20) DEFAULT 'child',
  notification_enabled BOOLEAN DEFAULT TRUE,
  alert_phone VARCHAR(11),
  linked_at TIMESTAMP DEFAULT NOW(),
  UNIQUE(child_id, parent_user_id)
);

-- 7. 社区动态
CREATE TABLE posts (
  id BIGSERIAL PRIMARY KEY,
  user_id BIGINT REFERENCES users(id),
  content TEXT,
  images TEXT[],
  topic_id BIGINT,
  location VARCHAR(100),
  like_count INTEGER DEFAULT 0,
  comment_count INTEGER DEFAULT 0,
  status VARCHAR(20) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE post_likes (
  post_id BIGINT REFERENCES posts(id),
  user_id BIGINT REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW(),
  PRIMARY KEY(post_id, user_id)
);

CREATE TABLE post_comments (
  id BIGSERIAL PRIMARY KEY,
  post_id BIGINT REFERENCES posts(id),
  user_id BIGINT REFERENCES users(id),
  content TEXT,
  reply_to_id BIGINT,
  status VARCHAR(20) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE topics (
  id BIGSERIAL PRIMARY KEY,
  name VARCHAR(50) UNIQUE,
  description TEXT,
  post_count INTEGER DEFAULT 0,
  is_official BOOLEAN DEFAULT FALSE,
  created_at TIMESTAMP DEFAULT NOW()
);

-- 8. 通用服务产品表(候鸟/文教/设备)
CREATE TABLE service_products (
  id BIGSERIAL PRIMARY KEY,
  provider_id BIGINT REFERENCES service_providers(id),
  category VARCHAR(50),
  subcategory VARCHAR(50),
  title VARCHAR(200),
  description TEXT,
  images TEXT[],
  price DECIMAL(10,2),
  price_unit VARCHAR(20),
  commission_rate DECIMAL(4,2),
  location VARCHAR(100),
  available_from DATE,
  available_to DATE,
  stock INTEGER,
  sales_count INTEGER DEFAULT 0,
  rating DECIMAL(2,1) DEFAULT 5.0,
  status VARCHAR(20) DEFAULT 'active',
  created_at TIMESTAMP DEFAULT NOW()
);

2.3 健康数据同步Worker

// workers/health-sync.js
const Queue = require('bull');
const healthSyncQueue = new Queue('health-sync', process.env.REDIS_URL);

// 每30分钟同步一次
healthSyncQueue.process(async (job) => {
  const { userId, brand, authToken } = job.data;
  
  let data;
  if (brand === 'huawei') {
    data = await fetchHuaweiHealthData(authToken);
  } else if (brand === 'xiaomi') {
    data = await fetchXiaomiHealthData(authToken);
  }
  
  // 写入数据库
  for (const record of data.records) {
    await db.query(
      `INSERT INTO health_records (user_id, record_type, value, recorded_at, source)
       VALUES ($1, $2, $3, $4, 'device')
       ON CONFLICT DO NOTHING`,
      [userId, record.type, record.value, record.timestamp]
    );
  }
  
  // 触发预警检测
  await alertEngine.check(userId, data.records);
});

// 调度:为每个绑定设备的用户创建定时任务
async function scheduleHealthSync() {
  const devices = await db.query(
    'SELECT * FROM health_devices WHERE status = $1', ['active']
  );
  for (const device of devices.rows) {
    healthSyncQueue.add(
      { userId: device.user_id, brand: device.brand, authToken: device.auth_token_encrypted },
      { repeat: { every: device.sync_interval * 60 * 1000 } }
    );
  }
}

2.4 预警检测引擎

// services/alert-engine.js
const THRESHOLDS = {
  blood_pressure: {
    systolic: { min: 85, max: 150 },
    diastolic: { min: 55, max: 100 }
  },
  heart_rate: { min: 50, max: 120 },
  blood_sugar: { min: 3.5, max: 7.0 }
};

async function checkAlerts(userId, records) {
  for (const record of records) {
    const threshold = THRESHOLDS[record.type];
    if (!threshold) continue;
    
    let isAlert = false;
    let alertValue = {};
    
    if (record.type === 'blood_pressure') {
      const { systolic, diastolic } = record.value;
      if (systolic > threshold.systolic.max || systolic < threshold.systolic.min ||
          diastolic > threshold.diastolic.max || diastolic < threshold.diastolic.min) {
        isAlert = true;
        alertValue = { systolic, diastolic };
      }
    } else if (record.type === 'heart_rate') {
      const { bpm } = record.value;
      if (bpm > threshold.max || bpm < threshold.min) {
        isAlert = true;
        alertValue = { bpm };
      }
    }
    
    if (isAlert) {
      // 写入预警记录
      const alert = await db.query(
        `INSERT INTO health_alerts (user_id, alert_type, value, threshold, severity)
         VALUES ($1, $2, $3, $4, $5) RETURNING id`,
        [userId, record.type, alertValue, threshold, 
         determineSeverity(record.type, alertValue, threshold)]
      );
      
      // 通知用户
      await pushNotification(userId, {
        type: 'HEALTH_ALERT',
        title: '健康提醒',
        message: formatAlertMessage(record.type, alertValue),
        data: { alertId: alert.rows[0].id }
      });
      
      // 通知子女
      const childLinks = await db.query(
        'SELECT child_id FROM child_parent_links WHERE parent_user_id = $1 AND notification_enabled = true',
        [userId]
      );
      for (const link of childLinks.rows) {
        await pushChildNotification(link.child_id, {
          type: 'PARENT_HEALTH_ALERT',
          parentUserId: userId,
          alertType: record.type,
          value: alertValue
        });
      }
    }
  }
}

3. 子女端小程序架构

3.1 技术选型

技术 选择 理由
框架 Taro 3.x + React 跨平台小程序框架,后续可扩支付宝
状态管理 Zustand 轻量、简单
UI库 Taro UI 适配微信小程序
请求 Taro.request封装 小程序网络请求

3.2 项目结构

banxiang-child-miniapp/
├── src/
│   ├── app.tsx
│   ├── app.config.ts
│   ├── pages/
│   │   ├── index/          # 首页(父母状态概览)
│   │   ├── health/         # 健康数据
│   │   ├── activities/     # 活动记录
│   │   ├── proxy/          # 代办服务
│   │   └── profile/        # 我的
│   ├── components/
│   │   ├── HealthCard.tsx
│   │   ├── ActivityCard.tsx
│   │   └── AlertBanner.tsx
│   ├── services/
│   │   ├── api.ts
│   │   └── auth.ts
│   └── stores/
│       ├── authStore.ts
│       └── parentStore.ts
├── config/
└── package.json

3.3 子女端API端点

方法 路径 描述
POST /child/auth/wx-login 微信登录
POST /child/bind 绑定父母
GET /child/parents 已绑定父母列表
GET /child/parents/:id/overview 父母状态概览
GET /child/parents/:id/health 健康数据
GET /child/parents/:id/health/trends 趋势数据
GET /child/parents/:id/health/reports 周报列表
GET /child/parents/:id/activities 活动记录
POST /child/parents/:id/proxy-order 代办下单
POST /child/parents/:id/membership 代付会员
GET /child/alerts 预警通知列表
PUT /child/alerts/:id/ack 确认预警

3.4 微信登录流程

// 子女端登录
router.post('/child/auth/wx-login', async (req, res) => {
  const { code } = req.body;
  
  // 换取openid
  const wxRes = await axios.get(
    `https://api.weixin.qq.com/sns/jscode2session?appid=${APPID}&secret=${SECRET}&js_code=${code}&grant_type=authorization_code`
  );
  
  const { openid, unionid } = wxRes.data;
  
  // 查找或创建用户
  let user = await db.query('SELECT * FROM child_users WHERE wx_openid = $1', [openid]);
  if (!user.rows.length) {
    user = await db.query(
      'INSERT INTO child_users (wx_openid, wx_unionid) VALUES ($1, $2) RETURNING *',
      [openid, unionid]
    );
  }
  
  const token = jwt.sign({ childId: user.rows[0].id, type: 'child' }, JWT_SECRET);
  res.json({ success: true, data: { token, user: user.rows[0] } });
});

4. 内容审核方案

4.1 双重审核机制

用户发布内容
     ↓
┌─────────────────┐
│  机器审核(实时)│
│  ├── 文字:敏感词库 + 通义千问审核
│  └── 图片阿里云内容安全API
└──────┬──────────┘
       ↓
  ├── 通过 → 直接发布
  ├── 疑似 → 标记待人工审核,临时发布
  └── 违规 → 拦截,通知用户
       ↓
┌─────────────────┐
│  人工审核(异步)│  ← 疑似内容队列
│  管理后台审核    │
└─────────────────┘

4.2 敏感词库

  • 基础库:政治/色情/暴力/赌博
  • 医疗库:虚假医疗广告、伪科学养生
  • 诈骗库:常见诈骗话术、钓鱼链接
  • 自定义库:平台运营过程中积累

5. 部署变更

5.1 新增服务

# docker-compose.yml 新增
services:
  # 健康数据同步Worker
  health-worker:
    build: .
    command: node workers/health-sync.js
    environment:
      - REDIS_URL=redis://redis:6379
      - DATABASE_URL=postgres://...
    depends_on:
      - db
      - redis
    restart: always

  # 预警检测与主服务共用通过Bull队列触发
  # 无需独立服务health-worker内集成

5.2 服务器升级

服务 V1.0配置 V1.2配置 原因
ECS 2核4G 4核8G Worker增加
RDS 2核4G 2核4G 暂不变,健康数据用分区
Redis 1G 2G Bull队列+更多缓存
月费 ¥700 ¥1000

6. 华为/小米健康API接入

6.1 华为健康

6.2 小米运动

  • OAuth2授权: 小米开放平台授权
  • API文档: https://dev.mi.com/docs
  • 数据类型: 步数、心率、睡眠
  • 注意: 小米手环不支持血压直接测量,需外接血压计

文档结束