rwadurian/backend/services/presence-service/docs/API.md

496 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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"}
}
]
}'
```