574 lines
13 KiB
Markdown
574 lines
13 KiB
Markdown
# 排行榜系统后端API数据格式规范
|
||
|
||
## 概述
|
||
|
||
本文档定义了排行榜系统的后端API接口规范,包括请求参数、响应格式和数据模型。
|
||
|
||
---
|
||
|
||
## 1. 获取排行榜数据
|
||
|
||
### 接口信息
|
||
|
||
- **接口路径**: `/api/ranking/list`
|
||
- **请求方法**: `GET`
|
||
- **接口描述**: 获取指定类型和时间段的排行榜数据
|
||
|
||
### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||
|--------|------|------|------|--------|
|
||
| rankingType | string | 是 | 榜单类型 | `popularity` / `custom` / `activity` |
|
||
| timePeriod | string | 是 | 时间段 | `online` / `monthly` / `history` |
|
||
| page | number | 否 | 页码(默认1) | `1` |
|
||
| pageSize | number | 否 | 每页数量(默认20) | `20` |
|
||
|
||
### 榜单类型枚举
|
||
|
||
```json
|
||
{
|
||
"popularity": "热度榜",
|
||
"custom": "自制榜",
|
||
"activity": "活动榜"
|
||
}
|
||
```
|
||
|
||
### 时间段枚举
|
||
|
||
```json
|
||
{
|
||
"online": "在线",
|
||
"monthly": "本月",
|
||
"history": "历史"
|
||
}
|
||
```
|
||
|
||
### 响应格式
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"rankingType": "popularity",
|
||
"timePeriod": "online",
|
||
"hasArtwork": true,
|
||
"total": 100,
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"list": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "user001",
|
||
"avatar": "https://example.com/avatar/1.jpeg",
|
||
"nickname": "星光璀璨",
|
||
"popularityScore": 73654,
|
||
"artworkImage": "https://example.com/artwork/001.png",
|
||
"artworkId": "artwork001"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 2. 获取当前用户排名信息
|
||
|
||
### 接口信息
|
||
|
||
- **接口路径**: `/api/ranking/current-user`
|
||
- **请求方法**: `GET`
|
||
- **接口描述**: 获取当前登录用户在指定榜单中的排名信息
|
||
|
||
### 请求参数
|
||
|
||
| 参数名 | 类型 | 必填 | 说明 | 示例值 |
|
||
|--------|------|------|------|--------|
|
||
| rankingType | string | 是 | 榜单类型 | `popularity` |
|
||
| timePeriod | string | 是 | 时间段 | `online` |
|
||
|
||
### 响应格式
|
||
|
||
**已上榜状态**:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"userId": "currentUser",
|
||
"avatar": "https://example.com/avatar/7.jpeg",
|
||
"nickname": "我",
|
||
"popularityScore": 45000,
|
||
"rank": 5,
|
||
"artworkImage": "https://example.com/artwork/current.png",
|
||
"artworkId": "currentUserArtwork",
|
||
"isRanked": true
|
||
}
|
||
}
|
||
```
|
||
|
||
**未上榜状态**:
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"userId": "currentUser",
|
||
"avatar": "https://example.com/avatar/7.jpeg",
|
||
"nickname": "我",
|
||
"popularityScore": 371,
|
||
"rank": null,
|
||
"artworkImage": "https://example.com/artwork/current.png",
|
||
"artworkId": "currentUserArtwork",
|
||
"isRanked": false
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 3. 数据模型定义
|
||
|
||
### RankingUser (排行榜用户对象)
|
||
|
||
| 字段名 | 类型 | 必填 | 说明 | 备注 |
|
||
|--------|------|------|------|------|
|
||
| rank | number | 是 | 排名 | 从1开始 |
|
||
| userId | string | 是 | 用户ID | 唯一标识 |
|
||
| avatar | string | 是 | 头像URL | 完整URL地址 |
|
||
| nickname | string | 是 | 用户昵称 | 最大长度20字符 |
|
||
| popularityScore | number | 是 | 人气值/积分 | 非负整数 |
|
||
| artworkImage | string | 否 | 作品图片URL | 热度榜和自制榜必填 |
|
||
| artworkId | string | 否 | 作品ID | 热度榜和自制榜必填 |
|
||
|
||
**TypeScript 类型定义**:
|
||
|
||
```typescript
|
||
interface RankingUser {
|
||
rank: number;
|
||
userId: string;
|
||
avatar: string;
|
||
nickname: string;
|
||
popularityScore: number;
|
||
artworkImage?: string; // 活动榜不需要
|
||
artworkId?: string; // 活动榜不需要
|
||
}
|
||
```
|
||
|
||
### CurrentUserRanking (当前用户排名对象)
|
||
|
||
| 字段名 | 类型 | 必填 | 说明 |
|
||
|--------|------|------|------|
|
||
| userId | string | 是 | 用户ID |
|
||
| avatar | string | 是 | 头像URL |
|
||
| nickname | string | 是 | 用户昵称 |
|
||
| popularityScore | number | 是 | 人气值/积分 |
|
||
| rank | number \| null | 是 | 排名(null表示未上榜) |
|
||
| artworkImage | string | 否 | 作品图片URL |
|
||
| artworkId | string | 否 | 作品ID |
|
||
| isRanked | boolean | 是 | 是否已上榜 |
|
||
|
||
**TypeScript 类型定义**:
|
||
|
||
```typescript
|
||
interface CurrentUserRanking {
|
||
userId: string;
|
||
avatar: string;
|
||
nickname: string;
|
||
popularityScore: number;
|
||
rank: number | null;
|
||
artworkImage?: string;
|
||
artworkId?: string;
|
||
isRanked: boolean;
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 榜单特性配置
|
||
|
||
### 榜单类型特性
|
||
|
||
| 榜单类型 | 类型代码 | 是否包含作品 | 说明 |
|
||
|---------|---------|------------|------|
|
||
| 热度榜 | popularity | ✅ 是 | 包含 artworkImage 和 artworkId |
|
||
| 自制榜 | custom | ✅ 是 | 包含 artworkImage 和 artworkId |
|
||
| 活动榜 | activity | ❌ 否 | 不包含作品相关字段 |
|
||
|
||
### 后端返回字段规则
|
||
|
||
```javascript
|
||
// 热度榜和自制榜
|
||
{
|
||
"hasArtwork": true,
|
||
"list": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "user001",
|
||
"avatar": "...",
|
||
"nickname": "...",
|
||
"popularityScore": 73654,
|
||
"artworkImage": "...", // 必须返回
|
||
"artworkId": "..." // 必须返回
|
||
}
|
||
]
|
||
}
|
||
|
||
// 活动榜
|
||
{
|
||
"hasArtwork": false,
|
||
"list": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "user201",
|
||
"avatar": "...",
|
||
"nickname": "...",
|
||
"popularityScore": 58321
|
||
// 不包含 artworkImage 和 artworkId
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 错误响应格式
|
||
|
||
### 通用错误响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "参数错误:rankingType 必须为 popularity、custom 或 activity",
|
||
"data": null
|
||
}
|
||
```
|
||
|
||
### 错误码定义
|
||
|
||
| 错误码 | 说明 | 示例消息 |
|
||
|--------|------|---------|
|
||
| 400 | 请求参数错误 | "参数错误:rankingType 不能为空" |
|
||
| 401 | 未登录或token失效 | "请先登录" |
|
||
| 404 | 资源不存在 | "榜单数据不存在" |
|
||
| 500 | 服务器内部错误 | "服务器错误,请稍后重试" |
|
||
|
||
---
|
||
|
||
## 6. 请求示例
|
||
|
||
### 示例1: 获取热度榜在线排行
|
||
|
||
**请求**:
|
||
```http
|
||
GET /api/ranking/list?rankingType=popularity&timePeriod=online&page=1&pageSize=20
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"rankingType": "popularity",
|
||
"timePeriod": "online",
|
||
"hasArtwork": true,
|
||
"total": 14,
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"list": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "user001",
|
||
"avatar": "https://cdn.example.com/avatar/1.jpeg",
|
||
"nickname": "星光璀璨",
|
||
"popularityScore": 73654,
|
||
"artworkImage": "https://cdn.example.com/artwork/001.png",
|
||
"artworkId": "artwork001"
|
||
},
|
||
{
|
||
"rank": 2,
|
||
"userId": "user002",
|
||
"avatar": "https://cdn.example.com/avatar/2.jpeg",
|
||
"nickname": "梦想追逐者",
|
||
"popularityScore": 68921,
|
||
"artworkImage": "https://cdn.example.com/artwork/002.jpeg",
|
||
"artworkId": "artwork002"
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 示例2: 获取活动榜本月排行
|
||
|
||
**请求**:
|
||
```http
|
||
GET /api/ranking/list?rankingType=activity&timePeriod=monthly&page=1&pageSize=20
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
**响应**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"rankingType": "activity",
|
||
"timePeriod": "monthly",
|
||
"hasArtwork": false,
|
||
"total": 3,
|
||
"page": 1,
|
||
"pageSize": 20,
|
||
"list": [
|
||
{
|
||
"rank": 1,
|
||
"userId": "user203",
|
||
"avatar": "https://cdn.example.com/avatar/5.jpeg",
|
||
"nickname": "活跃用户",
|
||
"popularityScore": 87654
|
||
},
|
||
{
|
||
"rank": 2,
|
||
"userId": "user201",
|
||
"avatar": "https://cdn.example.com/avatar/3.jpeg",
|
||
"nickname": "活动之星",
|
||
"popularityScore": 76543
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
### 示例3: 获取当前用户排名
|
||
|
||
**请求**:
|
||
```http
|
||
GET /api/ranking/current-user?rankingType=popularity&timePeriod=online
|
||
Authorization: Bearer {token}
|
||
```
|
||
|
||
**响应(已上榜)**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"userId": "currentUser",
|
||
"avatar": "https://cdn.example.com/avatar/7.jpeg",
|
||
"nickname": "我",
|
||
"popularityScore": 45000,
|
||
"rank": 5,
|
||
"artworkImage": "https://cdn.example.com/artwork/current.png",
|
||
"artworkId": "currentUserArtwork",
|
||
"isRanked": true
|
||
}
|
||
}
|
||
```
|
||
|
||
**响应(未上榜)**:
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "success",
|
||
"data": {
|
||
"userId": "currentUser",
|
||
"avatar": "https://cdn.example.com/avatar/7.jpeg",
|
||
"nickname": "我",
|
||
"popularityScore": 371,
|
||
"rank": null,
|
||
"artworkImage": "https://cdn.example.com/artwork/current.png",
|
||
"artworkId": "currentUserArtwork",
|
||
"isRanked": false
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 数据验证规则
|
||
|
||
### 后端验证要求
|
||
|
||
1. **rankingType 验证**
|
||
- 必填
|
||
- 只能是: `popularity`, `custom`, `activity`
|
||
|
||
2. **timePeriod 验证**
|
||
- 必填
|
||
- 只能是: `online`, `monthly`, `history`
|
||
|
||
3. **page 验证**
|
||
- 可选,默认为 1
|
||
- 必须是正整数
|
||
- 最小值: 1
|
||
|
||
4. **pageSize 验证**
|
||
- 可选,默认为 20
|
||
- 必须是正整数
|
||
- 范围: 1-100
|
||
|
||
5. **用户数据验证**
|
||
- `userId`: 非空字符串
|
||
- `nickname`: 非空字符串,最大20字符
|
||
- `avatar`: 有效的URL格式
|
||
- `popularityScore`: 非负整数
|
||
- `rank`: 正整数(从1开始)
|
||
|
||
6. **作品数据验证**(仅热度榜和自制榜)
|
||
- `artworkImage`: 有效的URL格式
|
||
- `artworkId`: 非空字符串
|
||
|
||
---
|
||
|
||
## 8. 性能优化建议
|
||
|
||
### 缓存策略
|
||
|
||
1. **排行榜数据缓存**
|
||
- 缓存时间: 5分钟
|
||
- 缓存键: `ranking:{rankingType}:{timePeriod}:{page}`
|
||
|
||
2. **用户排名缓存**
|
||
- 缓存时间: 1分钟
|
||
- 缓存键: `user_rank:{userId}:{rankingType}:{timePeriod}`
|
||
|
||
### 数据库查询优化
|
||
|
||
1. 为 `popularityScore` 字段建立索引
|
||
2. 为 `userId` 字段建立索引
|
||
3. 使用 Redis 存储实时排行榜数据(Sorted Set)
|
||
|
||
---
|
||
|
||
## 9. 前后端数据映射
|
||
|
||
### 前端 Mock 数据 → 后端数据库字段
|
||
|
||
| 前端字段 | 后端数据库字段 | 类型 | 说明 |
|
||
|---------|--------------|------|------|
|
||
| rank | - | computed | 根据 popularityScore 计算 |
|
||
| userId | user_id | varchar(50) | 用户唯一标识 |
|
||
| avatar | avatar_url | varchar(255) | 头像URL |
|
||
| nickname | nickname | varchar(50) | 用户昵称 |
|
||
| popularityScore | popularity_score | int | 人气值 |
|
||
| artworkImage | artwork_image_url | varchar(255) | 作品图片URL |
|
||
| artworkId | artwork_id | varchar(50) | 作品ID |
|
||
|
||
### 数据库表结构建议
|
||
|
||
```sql
|
||
-- 用户排行榜表
|
||
CREATE TABLE user_rankings (
|
||
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
||
user_id VARCHAR(50) NOT NULL,
|
||
ranking_type ENUM('popularity', 'custom', 'activity') NOT NULL,
|
||
time_period ENUM('online', 'monthly', 'history') NOT NULL,
|
||
popularity_score INT NOT NULL DEFAULT 0,
|
||
artwork_id VARCHAR(50),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||
INDEX idx_ranking_score (ranking_type, time_period, popularity_score DESC),
|
||
INDEX idx_user_ranking (user_id, ranking_type, time_period)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
-- 用户基本信息表
|
||
CREATE TABLE users (
|
||
user_id VARCHAR(50) PRIMARY KEY,
|
||
nickname VARCHAR(50) NOT NULL,
|
||
avatar_url VARCHAR(255),
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
|
||
-- 作品信息表
|
||
CREATE TABLE artworks (
|
||
artwork_id VARCHAR(50) PRIMARY KEY,
|
||
user_id VARCHAR(50) NOT NULL,
|
||
image_url VARCHAR(255) NOT NULL,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
INDEX idx_user_artwork (user_id)
|
||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||
```
|
||
|
||
---
|
||
|
||
## 10. 版本历史
|
||
|
||
| 版本 | 日期 | 修改内容 | 修改人 |
|
||
|------|------|---------|--------|
|
||
| 1.0.0 | 2024-03-11 | 初始版本 | - |
|
||
|
||
---
|
||
|
||
## 附录: TypeScript 完整类型定义
|
||
|
||
```typescript
|
||
// 榜单类型枚举
|
||
export enum RankingType {
|
||
POPULARITY = 'popularity',
|
||
CUSTOM = 'custom',
|
||
ACTIVITY = 'activity'
|
||
}
|
||
|
||
// 时间段枚举
|
||
export enum TimePeriod {
|
||
ONLINE = 'online',
|
||
MONTHLY = 'monthly',
|
||
HISTORY = 'history'
|
||
}
|
||
|
||
// 排行榜用户接口
|
||
export interface RankingUser {
|
||
rank: number;
|
||
userId: string;
|
||
avatar: string;
|
||
nickname: string;
|
||
popularityScore: number;
|
||
artworkImage?: string;
|
||
artworkId?: string;
|
||
}
|
||
|
||
// 当前用户排名接口
|
||
export interface CurrentUserRanking extends RankingUser {
|
||
rank: number | null;
|
||
isRanked: boolean;
|
||
}
|
||
|
||
// 排行榜列表请求参数
|
||
export interface RankingListRequest {
|
||
rankingType: RankingType;
|
||
timePeriod: TimePeriod;
|
||
page?: number;
|
||
pageSize?: number;
|
||
}
|
||
|
||
// 排行榜列表响应数据
|
||
export interface RankingListResponse {
|
||
rankingType: RankingType;
|
||
timePeriod: TimePeriod;
|
||
hasArtwork: boolean;
|
||
total: number;
|
||
page: number;
|
||
pageSize: number;
|
||
list: RankingUser[];
|
||
}
|
||
|
||
// 当前用户排名请求参数
|
||
export interface CurrentUserRankingRequest {
|
||
rankingType: RankingType;
|
||
timePeriod: TimePeriod;
|
||
}
|
||
|
||
// API 响应包装
|
||
export interface ApiResponse<T> {
|
||
code: number;
|
||
message: string;
|
||
data: T | null;
|
||
}
|
||
```
|