gcx/docs/guides/07-遥测与版本管理开发指南.md

14 KiB
Raw Permalink Blame History

07 - 遥测 (Telemetry) 与移动端版本管理开发指南

参考项目: rwadurian/backend/services/presence-service + admin-service 目标: 为 Genex 券金融平台增加 用户遥测分析移动端 OTA 版本管理 能力


一、功能概览

1.1 遥测系统 (Telemetry)

能力 说明
心跳检测 客户端定时上报心跳,服务端通过 Redis Sorted Set 实时计算在线用户数
事件采集 批量上报客户端事件(会话开始、页面浏览、操作行为等),写入 PostgreSQL
DAU 统计 基于 app_session_start 事件按 userId/installId 去重,支持省市维度
在线快照 每分钟记录在线用户数快照,支持 1m/5m/1h 区间聚合查询
Prometheus 暴露 /metrics 端点供 Grafana 抓取心跳数、事件数、在线人数、DAU

1.2 版本管理 (App Version / OTA Update)

能力 说明
版本 CRUD 管理员创建/编辑/删除/启禁用版本记录
APK/IPA 上传 上传安装包,自动解析 versionCode/versionName/minSdkVersion
强制更新 isForceUpdate 标志,客户端据此决定是否阻断使用
检查更新 API 移动端调用返回是否有更新、下载地址、SHA256 校验
断点续传下载 HTTP Range 206 支持,大文件友好
文件完整性 SHA256 哈希校验,防篡改

二、Genex 项目适配方案

2.1 架构决策

参考 rwadurian 项目的独立服务架构Genex 同样采用独立微服务方案:

功能 归属服务 端口 理由
遥测 (Telemetry) telemetry-service :3011 独立微服务专注心跳检测、事件采集、DAU 统计、Prometheus 指标
版本管理 (App Version) admin-service :3012 独立微服务,专注 APK/IPA 管理、OTA 更新、MinIO 文件存储

2.2 与参考项目的差异

维度 rwadurian Genex
ORM Prisma TypeORM (与现有一致)
架构 独立服务 + CQRS 独立服务 + DDD 四层架构
文件存储 本地 ./uploads MinIO (已有基础设施)
事件总线 Kafka Kafka (KRaft 模式)
缓存 Redis Redis (已有)
APK 解析 adbkit-apkreader 同方案
IPA 解析 unzipper + bplist-parser 同方案
Prometheus prom-client 同方案
在线检测窗口 180s (3 min) 180s
DAU 计算 每天凌晨 1:00 + 每小时滚动 同方案
下载端点 Express 流式 NestJS StreamableFile + Range

2.3 数据库表 (新增 4 张)

telemetry_events — 事件日志 (append-only)

CREATE TABLE IF NOT EXISTS telemetry_events (
    id BIGSERIAL PRIMARY KEY,
    user_id UUID REFERENCES users(id),
    install_id VARCHAR(128) NOT NULL,
    event_name VARCHAR(64) NOT NULL,
    event_time TIMESTAMPTZ NOT NULL,
    properties JSONB DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_telemetry_events_time ON telemetry_events(event_time);
CREATE INDEX idx_telemetry_events_name_time ON telemetry_events(event_name, event_time);
CREATE INDEX idx_telemetry_events_user ON telemetry_events(user_id);

daily_active_stats — DAU 日统计

CREATE TABLE IF NOT EXISTS daily_active_stats (
    day DATE PRIMARY KEY,
    dau_count INT NOT NULL DEFAULT 0,
    dau_by_platform JSONB DEFAULT '{}',
    dau_by_region JSONB DEFAULT '{}',
    calculated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    version INT NOT NULL DEFAULT 1
);

online_snapshots — 在线快照

CREATE TABLE IF NOT EXISTS online_snapshots (
    id BIGSERIAL PRIMARY KEY,
    ts TIMESTAMPTZ NOT NULL UNIQUE,
    online_count INT NOT NULL DEFAULT 0,
    window_seconds INT NOT NULL DEFAULT 180
);
CREATE INDEX idx_online_snapshots_ts ON online_snapshots(ts DESC);

app_versions — 应用版本

CREATE TABLE IF NOT EXISTS app_versions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    platform VARCHAR(10) NOT NULL CHECK (platform IN ('ANDROID', 'IOS')),
    version_code INT NOT NULL,
    version_name VARCHAR(32) NOT NULL,
    build_number VARCHAR(64) NOT NULL,
    download_url TEXT NOT NULL,
    file_size BIGINT NOT NULL DEFAULT 0,
    file_sha256 VARCHAR(64) NOT NULL,
    min_os_version VARCHAR(16),
    changelog TEXT NOT NULL DEFAULT '',
    is_force_update BOOLEAN NOT NULL DEFAULT FALSE,
    is_enabled BOOLEAN NOT NULL DEFAULT TRUE,
    release_date TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by UUID,
    updated_by UUID
);
CREATE INDEX idx_app_versions_platform ON app_versions(platform, is_enabled);
CREATE INDEX idx_app_versions_code ON app_versions(platform, version_code DESC);

2.4 Redis 数据结构

Key 类型 用途
genex:presence:online Sorted Set member=userId, score=heartbeat Unix时间戳
genex:dau:{YYYY-MM-DD} HyperLogLog 实时近似 DAUuserId/installId
genex:dau:{YYYY-MM-DD}:ttl - 自动过期 48h

2.5 Kafka Topics (新增)

Topic 生产者 消费者 用途
telemetry.session.started user-service notification-service 会话开始事件
telemetry.heartbeat user-service - 心跳事件(可选)

2.6 MinIO Bucket

Bucket 用途
app-releases 存放 APK/IPA 安装包

三、API 设计

3.1 遥测 API

POST /api/v1/telemetry/events          — 批量上报事件 (无需认证,支持匿名)
POST /api/v1/telemetry/heartbeat       — 心跳上报 (需认证)
GET  /api/v1/telemetry/online-count    — 当前在线人数 (需认证)
GET  /api/v1/telemetry/online-history  — 在线历史趋势 (需认证)

# Admin (遥测分析 — 独立路径避免与 issuer 的 analytics 冲突)
GET  /api/v1/admin/telemetry/dau       — DAU 统计查询
GET  /api/v1/admin/telemetry/events    — 事件列表查询
GET  /api/v1/admin/telemetry/realtime  — 实时数据面板

3.2 版本管理 API

# 移动端 (无需/轻认证)
GET  /api/v1/app/version/check         — 检查更新
GET  /api/v1/app/version/download/:id  — 下载安装包 (支持断点续传)

# Admin
GET    /api/v1/admin/versions          — 版本列表
GET    /api/v1/admin/versions/:id      — 版本详情
POST   /api/v1/admin/versions          — 创建版本 (手动填写)
POST   /api/v1/admin/versions/upload   — 上传 APK/IPA 自动创建
POST   /api/v1/admin/versions/parse    — 解析安装包 (不保存)
PUT    /api/v1/admin/versions/:id      — 更新版本信息
PATCH  /api/v1/admin/versions/:id/toggle — 启用/禁用
DELETE /api/v1/admin/versions/:id      — 删除版本

3.3 检查更新响应示例

{
  "code": 0,
  "data": {
    "needUpdate": true,
    "forceUpdate": true,
    "version": "2.1.0",
    "versionCode": 210,
    "downloadUrl": "https://minio.gogenex.com/app-releases/ANDROID-2.1.0.apk",
    "fileSize": 52428800,
    "fileSizeFriendly": "50.0 MB",
    "sha256": "a1b2c3d4e5f6...",
    "updateLog": "1. 新增券转让功能\n2. 修复钱包余额显示\n3. 性能优化",
    "releaseDate": "2026-02-12T10:00:00Z"
  }
}

3.4 强制更新决策流程

客户端 → GET /app/version/check?platform=android&current_version_code=200
                    ↓
服务端: 查找该平台最新 enabled 版本
                    ↓
          ┌── 无更新版本 → { needUpdate: false }
          │
          └── 有更新版本 (latestCode > currentCode)
                    ↓
              isForceUpdate && isEnabled?
              ├── YES → { needUpdate: true, forceUpdate: true, ... }
              └── NO  → { needUpdate: true, forceUpdate: false, ... }
                    ↓
客户端:
  forceUpdate=true  → 弹窗阻断,只能更新
  forceUpdate=false → 提示可选更新

四、实现文件清单

4.1 遥测服务 (telemetry-service :3011)

services/telemetry-service/src/
├── domain/
│   ├── entities/
│   │   ├── telemetry-event.entity.ts       # 事件日志实体
│   │   ├── online-snapshot.entity.ts       # 在线快照实体
│   │   └── daily-active-stats.entity.ts    # DAU 统计实体
│   ├── value-objects/
│   │   ├── event-name.vo.ts                # 事件名值对象
│   │   ├── install-id.vo.ts                # 安装ID值对象
│   │   └── time-window.vo.ts               # 时间窗口值对象
│   └── events/
│       ├── session-started.event.ts        # 会话开始领域事件
│       └── heartbeat-received.event.ts     # 心跳接收领域事件
├── application/services/
│   ├── telemetry.service.ts                # 事件采集 + 心跳 + DAU
│   └── telemetry-scheduler.service.ts      # 定时任务 (快照/DAU/清理)
├── infrastructure/
│   ├── redis/
│   │   └── presence-redis.service.ts       # Redis 在线检测操作
│   ├── kafka/
│   │   └── telemetry-producer.service.ts   # Kafka 事件发布
│   └── metrics/
│       └── telemetry-metrics.service.ts    # Prometheus 指标
└── interface/http/
    ├── controllers/
    │   ├── telemetry.controller.ts         # 遥测 API (心跳/事件/在线)
    │   ├── admin-telemetry.controller.ts   # Admin 遥测分析 API
    │   ├── metrics.controller.ts           # GET /metrics (Prometheus)
    │   └── health.controller.ts            # 健康检查
    └── dto/
        ├── batch-events.dto.ts
        ├── heartbeat.dto.ts
        └── query-dau.dto.ts

4.2 版本管理服务 (admin-service :3012)

services/admin-service/src/
├── domain/
│   ├── entities/
│   │   └── app-version.entity.ts           # 版本实体 + Platform 枚举
│   └── value-objects/
│       ├── version-code.vo.ts              # 版本号值对象
│       ├── version-name.vo.ts              # 版本名值对象
│       ├── file-sha256.vo.ts               # 文件哈希值对象
│       └── download-url.vo.ts              # 下载链接值对象
├── application/services/
│   ├── app-version.service.ts              # 版本 CRUD + 检查更新
│   └── file-storage.service.ts             # MinIO 文件上传/下载
├── infrastructure/
│   └── parsers/
│       └── package-parser.service.ts       # APK/IPA 解析
└── interface/http/
    ├── controllers/
    │   ├── app-version.controller.ts       # 移动端检查更新 + 下载 (无认证)
    │   ├── admin-version.controller.ts     # Admin 版本 CRUD (JWT+ADMIN)
    │   └── health.controller.ts            # 健康检查
    └── dto/
        ├── check-update.dto.ts
        ├── create-version.dto.ts
        └── upload-version.dto.ts

4.3 数据库迁移

migrations/
├── 032_create_telemetry_events.sql
├── 033_create_daily_active_stats.sql
├── 034_create_online_snapshots.sql
└── 035_create_app_versions.sql

4.4 Kong 路由

# telemetry-service (:3011)
- name: telemetry-service
  url: http://telemetry-service:3011
  routes:
    - name: telemetry-routes
      paths: [/api/v1/telemetry]
    - name: admin-telemetry-routes
      paths: [/api/v1/admin/telemetry]

# admin-service (:3012)
- name: admin-service
  url: http://admin-service:3012
  routes:
    - name: app-version-routes
      paths: [/api/v1/app/version]
    - name: admin-version-routes
      paths: [/api/v1/admin/versions]

4.5 Docker Compose 服务

telemetry-service:
  build: ./services/telemetry-service
  ports: ["3011:3011"]
  depends_on: [postgres, redis, kafka]

admin-service:
  build: ./services/admin-service
  ports: ["3012:3012"]
  depends_on: [postgres, minio]

五、关键实现要点

5.1 心跳在线检测

  • 客户端每 60s 发送心跳
  • 服务端用 Redis ZADD genex:presence:online userId timestamp
  • 在线判定窗口: 180s (3 分钟内有心跳即在线)
  • 在线人数: ZCOUNT genex:presence:online (now-180) +inf
  • 每小时清理 24 小时前的过期数据: ZREMRANGEBYSCORE

5.2 DAU 计算策略

  • 实时近似: Redis HyperLogLog PFADD genex:dau:2026-02-12 userId
  • 精确计算: 每天凌晨 1:00 从 telemetry_events 查询 app_session_start
  • 去重优先级: userId > installId
  • 地理维度: 从 event properties 中提取 province/city

5.3 文件存储 (MinIO)

  • Bucket: app-releases
  • 对象命名: {platform}/{versionName}/{timestamp}-{random}.{ext}
  • 预签名 URL: 下载时生成 24h 有效的签名链接
  • SHA256: 上传时计算,存入 app_versions.file_sha256

5.4 APK/IPA 解析

  • Android: adbkit-apkreader 读取 AndroidManifest.xml
    • 提取: packageName, versionCode, versionName, minSdkVersion
  • iOS: unzipper 解压 → bplist-parser 读取 Info.plist
    • 提取: CFBundleIdentifier, CFBundleVersion, CFBundleShortVersionString, MinimumOSVersion

5.5 断点续传

  • 响应头: Accept-Ranges: bytes
  • 请求头: Range: bytes=1048576-
  • 状态码: 206 Partial Content
  • 流式读取: fs.createReadStream(path, { start, end })

六、与现有系统集成

6.1 Admin-web 管理面板

admin-web 已有 Dashboard 页面,新增:

  • 遥测面板: DAU 趋势图、在线用户数、事件分析
  • 版本管理页: 版本列表、上传、强制更新开关

6.2 genex-mobile / miniapp

Flutter/Taro 客户端需要:

  • 启动时发送 app_session_start 事件
  • 定时心跳 (60s)
  • 启动时调用检查更新 API
  • 根据 forceUpdate 决定是否阻断

6.3 notification-service

消费 telemetry.session.started 事件:

  • 新用户首次登录 → 发送欢迎通知
  • 用户回归 (>7 天未登录) → 发送召回通知

七、Prometheus 指标

指标名 类型 标签 说明
genex_online_users Gauge - 当前在线用户数
genex_dau Gauge date 每日活跃用户数
genex_heartbeat_total Counter app_version 心跳总数
genex_events_total Counter event_name 事件总数
genex_event_batch_duration Histogram - 批量事件处理耗时