19 KiB
19 KiB
后端 API 开发指南
本文档为 Flutter 前端已实现功能提供后端 API 接口规范,供后端开发参考。
目录
1. APK 在线升级 API
1.1 版本检测接口
请求
GET /api/app/version/check
Query 参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| platform | string | 是 | 平台类型: android / ios |
| currentVersion | string | 是 | 当前版本号,如 1.0.0 |
| currentVersionCode | int | 是 | 当前版本代码,如 1 |
响应示例 - 有新版本
{
"code": 0,
"message": "success",
"data": {
"hasUpdate": true,
"version": "1.1.0",
"versionCode": 2,
"downloadUrl": "https://cdn.rwadurian.com/releases/app-v1.1.0.apk",
"fileSize": 52428800,
"fileSizeFriendly": "50.0 MB",
"sha256": "a1b2c3d4e5f6...完整64字符哈希",
"forceUpdate": false,
"updateLog": "1. 新增挖矿动画效果\n2. 修复已知BUG\n3. 性能优化",
"releaseDate": "2024-01-15T10:00:00Z"
}
}
响应示例 - 已是最新版本
{
"code": 0,
"message": "success",
"data": {
"hasUpdate": false
}
}
前端对应代码
// lib/core/updater/version_checker.dart
Future<VersionInfo?> checkForUpdate() async {
final response = await _dio.get(
'/api/app/version/check',
queryParameters: {
'platform': 'android',
'currentVersion': packageInfo.version,
'currentVersionCode': int.parse(packageInfo.buildNumber),
},
);
// ...
}
1.2 APK 下载
要求
- 下载链接必须使用 HTTPS
- 支持断点续传 (Range 请求头)
- 提供正确的
Content-Length响应头 - 提供 SHA-256 校验值供前端验证文件完整性
前端下载流程
1. 请求版本检测接口获取下载信息
2. 使用 Dio 下载 APK 到应用私有目录
3. 计算下载文件 SHA-256 与服务器返回值比对
4. 校验通过后调用系统安装器安装
1.3 版本管理后台 (建议)
建议实现后台管理界面支持:
- 上传新版本 APK
- 自动计算文件大小和 SHA-256
- 设置强制更新标志
- 填写更新日志
- 查看各版本下载统计
2. 遥测系统 API
2.1 事件上报接口
请求
POST /api/v1/analytics/events
请求头
Content-Type: application/json
Authorization: Bearer <token> // 可选,用户已登录时携带
请求体
{
"events": [
{
"eventId": "550e8400-e29b-41d4-a716-446655440000",
"name": "page_view",
"type": "page_view",
"level": "info",
"installId": "device-unique-install-id",
"deviceContextId": "ctx-abc123",
"sessionId": "session-xyz789",
"userId": "user-123",
"timestamp": "2024-01-15T10:30:00.000Z",
"properties": {
"page": "/ranking",
"referrer": "/splash"
}
},
{
"eventId": "550e8400-e29b-41d4-a716-446655440001",
"name": "button_click",
"type": "user_action",
"level": "info",
"installId": "device-unique-install-id",
"deviceContextId": "ctx-abc123",
"sessionId": "session-xyz789",
"userId": "user-123",
"timestamp": "2024-01-15T10:30:05.000Z",
"properties": {
"button": "start_mining",
"page": "/mining"
}
}
]
}
事件类型枚举 (EventType)
| 值 | 说明 |
|---|---|
page_view |
页面浏览 |
user_action |
用户操作 |
api_call |
API 调用 |
performance |
性能指标 |
error |
错误 |
crash |
崩溃 |
session |
会话事件 |
presence |
在线状态 |
事件级别枚举 (EventLevel)
| 值 | 说明 |
|---|---|
debug |
调试 |
info |
信息 |
warning |
警告 |
error |
错误 |
响应
{
"code": 0,
"message": "success"
}
前端对应代码
// lib/core/telemetry/uploader/telemetry_uploader.dart
Future<bool> uploadBatch({int batchSize = 20}) async {
final events = storage.dequeueEvents(batchSize);
final response = await _dio.post(
'/api/v1/analytics/events',
data: {
'events': events.map((e) => e.toJson()).toList(),
},
);
// ...
}
2.2 心跳接口 (在线状态)
请求
POST /api/v1/presence/heartbeat
请求头
Content-Type: application/json
Authorization: Bearer <token> // 可选
请求体
{
"installId": "device-unique-install-id",
"sessionId": "session-xyz789",
"userId": "user-123",
"timestamp": "2024-01-15T10:30:00.000Z"
}
响应
{
"code": 0,
"message": "success"
}
前端行为
- 应用在前台时,每 60 秒 发送一次心跳
- 应用进入后台时暂停心跳
- 应用恢复前台时立即发送心跳并恢复定时器
前端对应代码
// lib/core/telemetry/presence/heartbeat_service.dart
void _sendHeartbeat() {
_dio.post(
'/api/v1/presence/heartbeat',
data: {
'installId': _installId,
'sessionId': _sessionId,
'userId': _userId,
'timestamp': DateTime.now().toUtc().toIso8601String(),
},
);
}
2.3 遥测配置接口
请求
GET /api/telemetry/config
响应
{
"code": 0,
"message": "success",
"data": {
"enabled": true,
"samplingRate": 1.0,
"enabledEventTypes": ["page_view", "user_action", "error", "crash", "session", "presence"],
"maxQueueSize": 1000,
"uploadBatchSize": 20,
"uploadIntervalSeconds": 30,
"heartbeatIntervalSeconds": 60,
"sessionTimeoutMinutes": 30
}
}
字段说明
| 字段 | 类型 | 说明 |
|---|---|---|
| enabled | bool | 全局开关,false 时前端停止所有遥测 |
| samplingRate | double | 采样率 0.0~1.0,用于控制上报比例 |
| enabledEventTypes | string[] | 启用的事件类型列表 |
| maxQueueSize | int | 本地队列最大容量 |
| uploadBatchSize | int | 每批上传事件数量 |
| uploadIntervalSeconds | int | 上传间隔秒数 |
| heartbeatIntervalSeconds | int | 心跳间隔秒数 |
| sessionTimeoutMinutes | int | 会话超时分钟数 |
前端对应代码
// lib/core/telemetry/models/telemetry_config.dart
class TelemetryConfig {
final bool enabled;
final double samplingRate;
final List<String> enabledEventTypes;
final int maxQueueSize;
final int uploadBatchSize;
final int uploadIntervalSeconds;
final int heartbeatIntervalSeconds;
final int sessionTimeoutMinutes;
}
3. 数据库设计
3.1 应用版本表 (app_versions)
CREATE TABLE app_versions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
platform ENUM('android', 'ios') NOT NULL,
version VARCHAR(20) NOT NULL COMMENT '版本号 如 1.0.0',
version_code INT NOT NULL COMMENT '版本代码 如 1',
download_url VARCHAR(500) NOT NULL COMMENT 'APK/IPA 下载地址',
file_size BIGINT NOT NULL COMMENT '文件大小(字节)',
sha256 CHAR(64) NOT NULL COMMENT 'SHA-256 哈希值',
force_update TINYINT(1) DEFAULT 0 COMMENT '是否强制更新',
update_log TEXT COMMENT '更新日志',
release_date DATETIME NOT NULL COMMENT '发布时间',
is_active TINYINT(1) DEFAULT 1 COMMENT '是否激活',
download_count INT DEFAULT 0 COMMENT '下载次数',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_platform_version (platform, version_code),
INDEX idx_platform_active (platform, is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用版本管理';
3.2 遥测事件表 (analytics_events)
CREATE TABLE analytics_events (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
event_id VARCHAR(36) NOT NULL COMMENT '事件唯一ID (UUID)',
name VARCHAR(100) NOT NULL COMMENT '事件名称',
type ENUM('page_view', 'user_action', 'api_call', 'performance', 'error', 'crash', 'session', 'presence') NOT NULL,
level ENUM('debug', 'info', 'warning', 'error') DEFAULT 'info',
install_id VARCHAR(100) NOT NULL COMMENT '设备安装ID',
device_context_id VARCHAR(100) COMMENT '设备上下文ID',
session_id VARCHAR(100) COMMENT '会话ID',
user_id VARCHAR(100) COMMENT '用户ID',
properties JSON COMMENT '事件属性',
event_time DATETIME(3) NOT NULL COMMENT '事件发生时间',
received_at DATETIME(3) DEFAULT CURRENT_TIMESTAMP(3) COMMENT '服务器接收时间',
UNIQUE KEY uk_event_id (event_id),
INDEX idx_install_id (install_id),
INDEX idx_user_id (user_id),
INDEX idx_session_id (session_id),
INDEX idx_type_time (type, event_time),
INDEX idx_event_time (event_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='遥测事件记录'
PARTITION BY RANGE (TO_DAYS(event_time)) (
PARTITION p_default VALUES LESS THAN MAXVALUE
);
建议按天分区,便于数据清理和查询优化
3.3 设备上下文表 (device_contexts)
CREATE TABLE device_contexts (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
context_id VARCHAR(100) NOT NULL COMMENT '上下文ID',
install_id VARCHAR(100) NOT NULL COMMENT '设备安装ID',
platform VARCHAR(20) NOT NULL COMMENT '平台 android/ios',
brand VARCHAR(50) COMMENT '品牌',
model VARCHAR(100) COMMENT '型号',
manufacturer VARCHAR(100) COMMENT '制造商',
is_physical_device TINYINT(1) COMMENT '是否真机',
os_version VARCHAR(50) COMMENT '系统版本',
sdk_int INT COMMENT 'SDK版本号',
android_id VARCHAR(100) COMMENT 'Android ID',
screen_width INT COMMENT '屏幕宽度',
screen_height INT COMMENT '屏幕高度',
screen_density DECIMAL(4,2) COMMENT '屏幕密度',
app_name VARCHAR(100) COMMENT '应用名称',
package_name VARCHAR(200) COMMENT '包名',
app_version VARCHAR(20) COMMENT '应用版本',
build_number VARCHAR(20) COMMENT '构建号',
build_mode VARCHAR(20) COMMENT '构建模式 debug/release',
locale VARCHAR(20) COMMENT '语言区域',
timezone VARCHAR(50) COMMENT '时区',
network_type VARCHAR(20) COMMENT '网络类型',
is_dark_mode TINYINT(1) COMMENT '是否深色模式',
collected_at DATETIME(3) NOT NULL COMMENT '采集时间',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
UNIQUE KEY uk_context_id (context_id),
INDEX idx_install_id (install_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备上下文信息';
3.4 日活统计表 (analytics_daily_active)
CREATE TABLE analytics_daily_active (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
stat_date DATE NOT NULL COMMENT '统计日期',
total_dau INT DEFAULT 0 COMMENT '总DAU',
new_users INT DEFAULT 0 COMMENT '新用户数',
returning_users INT DEFAULT 0 COMMENT '回访用户数',
total_sessions INT DEFAULT 0 COMMENT '总会话数',
avg_session_duration INT DEFAULT 0 COMMENT '平均会话时长(秒)',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY uk_stat_date (stat_date)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='日活统计汇总';
3.5 在线快照表 (analytics_online_snapshots)
CREATE TABLE analytics_online_snapshots (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
snapshot_time DATETIME NOT NULL COMMENT '快照时间',
online_count INT NOT NULL COMMENT '在线人数',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
INDEX idx_snapshot_time (snapshot_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='在线人数快照';
4. Redis 数据结构
4.1 在线用户集合 (实时在线统计)
Key: presence:online_users
Type: Sorted Set (ZSET)
Score: 最后心跳时间戳 (Unix timestamp)
Member: installId 或 userId (取决于是否登录)
# 用户发送心跳时
ZADD presence:online_users <timestamp> <installId>
# 获取在线人数 (2分钟内有心跳的用户)
ZCOUNT presence:online_users <now - 120> <now>
# 清理过期用户 (可选,定时任务执行)
ZREMRANGEBYSCORE presence:online_users -inf <now - 120>
在线判定规则
用户最后心跳时间在 2 分钟 内视为在线(心跳间隔 60 秒,允许 1 次丢失)
4.2 日活去重集合
Key: analytics:dau:<date> (如 analytics:dau:2024-01-15)
Type: Set
Member: installId
TTL: 48 小时
# 记录日活
SADD analytics:dau:2024-01-15 <installId>
EXPIRE analytics:dau:2024-01-15 172800
# 获取当日DAU
SCARD analytics:dau:2024-01-15
4.3 会话缓存
Key: session:<sessionId>
Type: Hash
TTL: 30 分钟 (随活动更新)
HSET session:xyz789
installId "device-xxx"
userId "user-123"
startTime "2024-01-15T10:00:00Z"
lastActiveTime "2024-01-15T10:30:00Z"
EXPIRE session:xyz789 1800
5. 定时任务
5.1 DAU 统计任务
执行频率: 每天凌晨 00:05
# 伪代码
def calculate_daily_dau():
yesterday = date.today() - timedelta(days=1)
date_str = yesterday.strftime('%Y-%m-%d')
# 从 Redis 获取昨日 DAU
dau_key = f'analytics:dau:{date_str}'
total_dau = redis.scard(dau_key)
# 或从数据库统计 (session_start 事件去重)
total_dau = db.query("""
SELECT COUNT(DISTINCT install_id)
FROM analytics_events
WHERE type = 'session'
AND name = 'session_start'
AND DATE(event_time) = %s
""", [date_str])
# 统计新用户 (首次出现的 install_id)
new_users = db.query("""
SELECT COUNT(*) FROM (
SELECT install_id
FROM analytics_events
WHERE type = 'session' AND name = 'session_start'
GROUP BY install_id
HAVING MIN(DATE(event_time)) = %s
) t
""", [date_str])
# 写入统计表
db.upsert('analytics_daily_active', {
'stat_date': date_str,
'total_dau': total_dau,
'new_users': new_users,
'returning_users': total_dau - new_users
})
5.2 在线人数快照任务
执行频率: 每 5 分钟
# 伪代码
def snapshot_online_count():
now = datetime.now()
threshold = now - timedelta(minutes=2)
# 从 Redis 获取在线人数
online_count = redis.zcount(
'presence:online_users',
threshold.timestamp(),
now.timestamp()
)
# 写入快照表
db.insert('analytics_online_snapshots', {
'snapshot_time': now,
'online_count': online_count
})
5.3 过期数据清理任务
执行频率: 每天凌晨 03:00
# 伪代码
def cleanup_expired_data():
# 清理 30 天前的事件数据
retention_days = 30
cutoff_date = date.today() - timedelta(days=retention_days)
db.execute("""
DELETE FROM analytics_events
WHERE event_time < %s
LIMIT 100000
""", [cutoff_date])
# 清理 Redis 过期在线状态
redis.zremrangebyscore(
'presence:online_users',
'-inf',
(datetime.now() - timedelta(minutes=5)).timestamp()
)
6. 前端关键行为说明
6.1 会话管理
会话开始条件:
- 应用冷启动
- 应用从后台恢复且距上次活动超过 30 分钟
会话结束条件:
- 应用进入后台
前端发送事件:
// 会话开始
TelemetryService().trackEvent(
name: SessionEvents.sessionStart,
type: EventType.session,
properties: {'trigger': 'app_launch'},
);
// 会话结束
TelemetryService().trackEvent(
name: SessionEvents.sessionEnd,
type: EventType.session,
properties: {
'duration_seconds': duration,
'trigger': 'app_pause',
},
);
6.2 事件上报策略
- 队列阈值触发: 本地队列 ≥10 条时触发上传
- 定时触发: 每 30 秒检查一次
- 应用退出触发: 强制上传全部队列
- 批量大小: 每批最多 20 条
6.3 设备上下文
前端采集设备信息后生成唯一 deviceContextId,同一设备配置不变时复用相同 ID。设备信息变化(如系统升级)时生成新 ID。
7. API 错误码规范
| code | 说明 |
|---|---|
| 0 | 成功 |
| 1001 | 参数错误 |
| 1002 | 认证失败 |
| 2001 | 服务器内部错误 |
| 2002 | 数据库错误 |
| 3001 | 版本不存在 |
| 3002 | 下载链接已过期 |
8. 安全建议
-
APK 下载
- 使用 HTTPS
- 提供 SHA-256 校验
- 下载链接可设置有效期
-
遥测数据
- 不采集敏感个人信息 (如通讯录、短信)
- installId 使用 UUID 生成,不关联设备硬件标识
- 支持用户关闭遥测 (通过远程配置)
-
心跳接口
- 防刷限流 (每个 installId 每分钟最多 2 次)
- 异常流量监控
附录: 前端代码目录结构
lib/core/
├── updater/ # APK升级模块
│ ├── models/
│ │ ├── version_info.dart # 版本信息模型
│ │ └── update_config.dart # 更新配置模型
│ ├── channels/
│ │ ├── google_play_updater.dart # Google Play更新器
│ │ └── self_hosted_updater.dart # 自建服务器更新器
│ ├── version_checker.dart # 版本检测器
│ ├── download_manager.dart # 下载管理器
│ ├── apk_installer.dart # APK安装器
│ ├── app_market_detector.dart # 应用市场检测
│ └── update_service.dart # 统一更新服务
│
└── telemetry/ # 遥测模块
├── models/
│ ├── device_context.dart # 设备上下文模型
│ ├── telemetry_event.dart # 遥测事件模型
│ └── telemetry_config.dart # 遥测配置模型
├── collectors/
│ └── device_info_collector.dart # 设备信息采集器
├── storage/
│ └── telemetry_storage.dart # 本地事件存储
├── uploader/
│ └── telemetry_uploader.dart # 事件上传器
├── session/
│ ├── session_events.dart # 会话事件常量
│ └── session_manager.dart # 会话管理器
├── presence/
│ ├── presence_config.dart # 心跳配置
│ └── heartbeat_service.dart # 心跳服务
└── telemetry_service.dart # 遥测服务入口