496 lines
10 KiB
Markdown
496 lines
10 KiB
Markdown
# Presence Service API 文档
|
||
|
||
## 概述
|
||
|
||
Base URL: `/api/v1`
|
||
|
||
所有需要认证的接口都需要在请求头中携带 JWT Token:
|
||
```
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
---
|
||
|
||
## 1. 在线状态 API
|
||
|
||
### 1.1 记录心跳
|
||
|
||
记录用户心跳,更新在线状态。
|
||
|
||
**请求**
|
||
|
||
```
|
||
POST /api/v1/presence/heartbeat
|
||
Authorization: Bearer <token>
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**请求体**
|
||
|
||
| 字段 | 类型 | 必填 | 描述 |
|
||
|-----|------|-----|------|
|
||
| installId | string | 是 | 安装ID (8-64字符) |
|
||
| appVersion | string | 是 | 应用版本号 |
|
||
| clientTs | number | 是 | 客户端时间戳 (Unix秒) |
|
||
|
||
**请求示例**
|
||
|
||
```json
|
||
{
|
||
"installId": "uuid-xxxx-xxxx-xxxx",
|
||
"appVersion": "1.0.0",
|
||
"clientTs": 1732685100
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"ok": true,
|
||
"serverTs": 1732685100123
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| ok | boolean | 是否成功 |
|
||
| serverTs | number | 服务器时间戳 (Unix毫秒) |
|
||
|
||
**错误码**
|
||
|
||
| HTTP 状态码 | 错误码 | 描述 |
|
||
|------------|-------|------|
|
||
| 400 | VALIDATION_ERROR | 参数校验失败 |
|
||
| 401 | UNAUTHORIZED | 未授权 |
|
||
|
||
---
|
||
|
||
### 1.2 查询在线人数
|
||
|
||
查询当前在线用户数量。
|
||
|
||
**请求**
|
||
|
||
```
|
||
GET /api/v1/presence/online-count
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**查询参数**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|
||
|-----|------|-----|-------|------|
|
||
| windowSeconds | number | 否 | 180 | 时间窗口 (秒) |
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"count": 12345,
|
||
"windowSeconds": 180,
|
||
"queriedAt": "2025-01-01T12:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| count | number | 在线用户数 |
|
||
| windowSeconds | number | 统计时间窗口 |
|
||
| queriedAt | string | 查询时间 (ISO 8601) |
|
||
|
||
---
|
||
|
||
### 1.3 查询在线人数历史
|
||
|
||
查询指定时间范围内的在线人数历史快照。
|
||
|
||
**请求**
|
||
|
||
```
|
||
GET /api/v1/presence/online-history
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**查询参数**
|
||
|
||
| 参数 | 类型 | 必填 | 默认值 | 描述 |
|
||
|-----|------|-----|-------|------|
|
||
| startTime | string | 是 | - | 开始时间 (ISO 8601) |
|
||
| endTime | string | 是 | - | 结束时间 (ISO 8601) |
|
||
| interval | string | 否 | 5m | 聚合间隔: 1m, 5m, 15m, 30m, 1h |
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"data": [
|
||
{
|
||
"ts": "2025-01-01T12:00:00.000Z",
|
||
"count": 12345
|
||
},
|
||
{
|
||
"ts": "2025-01-01T12:05:00.000Z",
|
||
"count": 12400
|
||
}
|
||
],
|
||
"interval": "5m",
|
||
"startTime": "2025-01-01T12:00:00.000Z",
|
||
"endTime": "2025-01-01T13:00:00.000Z",
|
||
"total": 12,
|
||
"summary": {
|
||
"max": 12500,
|
||
"min": 12000,
|
||
"avg": 12250
|
||
}
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| data | array | 快照数据列表 |
|
||
| data[].ts | string | 快照时间 |
|
||
| data[].count | number | 在线人数 |
|
||
| interval | string | 聚合间隔 |
|
||
| startTime | string | 实际开始时间 |
|
||
| endTime | string | 实际结束时间 |
|
||
| total | number | 数据点总数 |
|
||
| summary.max | number | 最大在线人数 |
|
||
| summary.min | number | 最小在线人数 |
|
||
| summary.avg | number | 平均在线人数 |
|
||
|
||
**错误码**
|
||
|
||
| HTTP 状态码 | 错误码 | 描述 |
|
||
|------------|-------|------|
|
||
| 400 | INVALID_TIME_RANGE | 无效的时间范围 |
|
||
| 400 | INVALID_INTERVAL | 无效的聚合间隔 |
|
||
|
||
---
|
||
|
||
## 2. 分析事件 API
|
||
|
||
### 2.1 批量上报事件
|
||
|
||
批量上报客户端分析事件。
|
||
|
||
**请求**
|
||
|
||
```
|
||
POST /api/v1/analytics/events
|
||
Content-Type: application/json
|
||
```
|
||
|
||
**请求体**
|
||
|
||
| 字段 | 类型 | 必填 | 描述 |
|
||
|-----|------|-----|------|
|
||
| events | array | 是 | 事件列表 |
|
||
| events[].eventName | string | 是 | 事件名称 (字母开头,1-64字符) |
|
||
| events[].installId | string | 是 | 安装ID |
|
||
| events[].userId | string | 否 | 用户ID (已登录用户) |
|
||
| events[].clientTs | number | 是 | 客户端时间戳 (Unix秒) |
|
||
| events[].properties | object | 否 | 事件属性 |
|
||
|
||
**请求示例**
|
||
|
||
```json
|
||
{
|
||
"events": [
|
||
{
|
||
"eventName": "app_session_start",
|
||
"installId": "uuid-xxxx-xxxx-xxxx",
|
||
"userId": "12345",
|
||
"clientTs": 1732685100,
|
||
"properties": {
|
||
"os": "iOS",
|
||
"osVersion": "17.0",
|
||
"appVersion": "1.0.0",
|
||
"deviceModel": "iPhone 15 Pro",
|
||
"province": "广东省",
|
||
"city": "深圳市"
|
||
}
|
||
},
|
||
{
|
||
"eventName": "presence_heartbeat",
|
||
"installId": "uuid-xxxx-xxxx-xxxx",
|
||
"clientTs": 1732685160
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"accepted": 2,
|
||
"failed": 0,
|
||
"errors": []
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| accepted | number | 成功接收的事件数 |
|
||
| failed | number | 失败的事件数 |
|
||
| errors | array | 错误详情列表 |
|
||
|
||
**预定义事件名称**
|
||
|
||
| 事件名称 | 描述 |
|
||
|---------|------|
|
||
| app_session_start | 应用会话开始 |
|
||
| app_session_end | 应用会话结束 |
|
||
| presence_heartbeat | 心跳事件 |
|
||
| user_login | 用户登录 |
|
||
| user_logout | 用户登出 |
|
||
|
||
---
|
||
|
||
### 2.2 查询日活统计
|
||
|
||
查询指定日期范围的日活统计数据。
|
||
|
||
**请求**
|
||
|
||
```
|
||
GET /api/v1/analytics/dau
|
||
Authorization: Bearer <token>
|
||
```
|
||
|
||
**查询参数**
|
||
|
||
| 参数 | 类型 | 必填 | 描述 |
|
||
|-----|------|-----|------|
|
||
| startDate | string | 是 | 开始日期 (YYYY-MM-DD) |
|
||
| endDate | string | 是 | 结束日期 (YYYY-MM-DD) |
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"data": [
|
||
{
|
||
"day": "2025-01-01",
|
||
"dauCount": 50000,
|
||
"dauByProvince": {
|
||
"广东省": 15000,
|
||
"北京市": 8000,
|
||
"上海市": 7000
|
||
},
|
||
"dauByCity": {
|
||
"深圳市": 10000,
|
||
"北京市": 8000,
|
||
"上海市": 7000
|
||
}
|
||
}
|
||
],
|
||
"total": 1,
|
||
"summary": {
|
||
"totalDau": 50000,
|
||
"avgDau": 50000,
|
||
"maxDau": 50000,
|
||
"minDau": 50000
|
||
}
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| data | array | 日活数据列表 |
|
||
| data[].day | string | 统计日期 |
|
||
| data[].dauCount | number | 日活人数 |
|
||
| data[].dauByProvince | object | 按省份统计 |
|
||
| data[].dauByCity | object | 按城市统计 |
|
||
| total | number | 数据天数 |
|
||
| summary | object | 汇总统计 |
|
||
|
||
**错误码**
|
||
|
||
| HTTP 状态码 | 错误码 | 描述 |
|
||
|------------|-------|------|
|
||
| 400 | INVALID_DATE_FORMAT | 无效的日期格式 |
|
||
| 400 | DATE_RANGE_TOO_LARGE | 日期范围过大 (最多90天) |
|
||
|
||
---
|
||
|
||
## 3. 健康检查 API
|
||
|
||
### 3.1 健康检查
|
||
|
||
检查服务健康状态。
|
||
|
||
**请求**
|
||
|
||
```
|
||
GET /api/v1/health
|
||
```
|
||
|
||
**响应**
|
||
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"service": "presence-service",
|
||
"timestamp": "2025-01-01T12:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 错误响应格式
|
||
|
||
所有错误响应遵循统一格式:
|
||
|
||
```json
|
||
{
|
||
"statusCode": 400,
|
||
"path": "/api/v1/presence/heartbeat",
|
||
"method": "POST",
|
||
"message": "installId must be a string",
|
||
"timestamp": "2025-01-01T12:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
| 字段 | 类型 | 描述 |
|
||
|-----|------|------|
|
||
| statusCode | number | HTTP 状态码 |
|
||
| path | string | 请求路径 |
|
||
| method | string | 请求方法 |
|
||
| message | string | 错误信息 |
|
||
| timestamp | string | 错误时间 |
|
||
|
||
**ValidationPipe 错误格式**
|
||
|
||
当请求体校验失败时,message 字段可能为数组:
|
||
|
||
```json
|
||
{
|
||
"statusCode": 400,
|
||
"path": "/api/v1/presence/heartbeat",
|
||
"method": "POST",
|
||
"message": [
|
||
"installId must be a string",
|
||
"clientTs must be a number"
|
||
],
|
||
"timestamp": "2025-01-01T12:00:00.000Z"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 通用 HTTP 状态码
|
||
|
||
| 状态码 | 描述 |
|
||
|-------|------|
|
||
| 200 | 请求成功 |
|
||
| 201 | 资源创建成功 |
|
||
| 400 | 请求参数错误 |
|
||
| 401 | 未授权 |
|
||
| 403 | 禁止访问 |
|
||
| 404 | 资源不存在 |
|
||
| 422 | 业务逻辑错误 |
|
||
| 500 | 服务器内部错误 |
|
||
|
||
---
|
||
|
||
## 6. 限流策略
|
||
|
||
| 接口 | 限制 | 窗口 |
|
||
|-----|------|-----|
|
||
| POST /heartbeat | 60 次/分钟/用户 | 滑动窗口 |
|
||
| POST /events | 100 次/分钟/IP | 滑动窗口 |
|
||
| GET /online-count | 120 次/分钟/用户 | 滑动窗口 |
|
||
| GET /online-history | 30 次/分钟/用户 | 滑动窗口 |
|
||
| GET /dau | 30 次/分钟/用户 | 滑动窗口 |
|
||
|
||
超出限制返回 `429 Too Many Requests`。
|
||
|
||
---
|
||
|
||
## 7. SDK 示例
|
||
|
||
### JavaScript/TypeScript
|
||
|
||
```typescript
|
||
class PresenceClient {
|
||
private baseUrl: string;
|
||
private token: string;
|
||
|
||
constructor(baseUrl: string, token: string) {
|
||
this.baseUrl = baseUrl;
|
||
this.token = token;
|
||
}
|
||
|
||
async sendHeartbeat(installId: string, appVersion: string): Promise<void> {
|
||
const response = await fetch(`${this.baseUrl}/api/v1/presence/heartbeat`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'Authorization': `Bearer ${this.token}`,
|
||
},
|
||
body: JSON.stringify({
|
||
installId,
|
||
appVersion,
|
||
clientTs: Math.floor(Date.now() / 1000),
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`Heartbeat failed: ${response.status}`);
|
||
}
|
||
}
|
||
|
||
async getOnlineCount(windowSeconds = 180): Promise<number> {
|
||
const response = await fetch(
|
||
`${this.baseUrl}/api/v1/presence/online-count?windowSeconds=${windowSeconds}`,
|
||
{
|
||
headers: {
|
||
'Authorization': `Bearer ${this.token}`,
|
||
},
|
||
}
|
||
);
|
||
|
||
const data = await response.json();
|
||
return data.count;
|
||
}
|
||
}
|
||
```
|
||
|
||
### cURL 示例
|
||
|
||
```bash
|
||
# 发送心跳
|
||
curl -X POST https://api.example.com/api/v1/presence/heartbeat \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer <token>" \
|
||
-d '{
|
||
"installId": "uuid-xxxx-xxxx-xxxx",
|
||
"appVersion": "1.0.0",
|
||
"clientTs": 1732685100
|
||
}'
|
||
|
||
# 查询在线人数
|
||
curl https://api.example.com/api/v1/presence/online-count \
|
||
-H "Authorization: Bearer <token>"
|
||
|
||
# 查询在线历史
|
||
curl "https://api.example.com/api/v1/presence/online-history?startTime=2025-01-01T00:00:00Z&endTime=2025-01-01T12:00:00Z&interval=5m" \
|
||
-H "Authorization: Bearer <token>"
|
||
|
||
# 批量上报事件
|
||
curl -X POST https://api.example.com/api/v1/analytics/events \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"events": [
|
||
{
|
||
"eventName": "app_session_start",
|
||
"installId": "uuid-xxxx-xxxx-xxxx",
|
||
"clientTs": 1732685100,
|
||
"properties": {"os": "iOS"}
|
||
}
|
||
]
|
||
}'
|
||
```
|