10 KiB
10 KiB
TopFans 排行榜功能需求文档
1. 功能概述
1.1 产品背景
排行榜功能旨在提升用户活跃度和参与度,通过展示热门藏品和自制佳作榜单,激励用户创作和互动。
1.2 排行榜类型
| 排行类型 | 统计维度 | 数据来源 |
|---|---|---|
| 热度排行 | 展示中 / 本月 / 全部 | 藏品点赞数 |
| 自制排行 | 展示中 / 本月 / 全部 | 自制藏品点赞数 |
注意: 活动排行榜功能待后续单独设计
2. 数据模型设计
2.1 现有数据分析
Asset (藏品表)
LikeCount- 点赞数(累计)OwnerUID- 拥有者IDStarID- 粉丝身份IDCreatedAt- 创建时间
AssetLike (点赞记录表)
AssetID- 藏品IDUserID- 点赞用户IDCreatedAt- 点赞时间(用于本月统计)
Exhibition (展品展示表)
AssetID- 展品IDStartTime- 开始展示时间ExpireAt- 过期时间(用于"展示中"判定)
2.2 新增数据模型
2.2.1 藏品表扩展
在 Asset 表中增加 IsOriginal 字段(true=自制藏品):
type Asset struct {
// ... 现有字段
IsOriginal bool `gorm:"default:false;column:is_original"` // 是否自制藏品
}
3. API 接口设计
3.1 通用响应格式
{
"code": 200,
"message": "ok",
"data": {
"items": [],
"page": 1,
"page_size": 10,
"total": 100
}
}
3.2 热度排行榜
3.2.1 获取热度排行榜
接口: GET /api/v1/rankings/hot
认证: 需认证 (JWT Token)
Query 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dimension | string | 是 | 统计维度: displaying(展示中) / month(本月) / total(全部) |
| star_id | int64 | 否 | 粉丝身份ID筛选,不传则返回当前身份数据 |
| page | int | 否 | 页码,默认1 |
| page_size | int | 否 | 每页数量,默认10 |
返回示例:
{
"code": 200,
"message": "ok",
"data": {
"my_ranking": {
"rank": 5,
"asset_id": 1005,
"asset_name": "我的藏品",
"cover_url": "https://...",
"like_count": 88,
"status": "ranked"
},
"items": [
{
"rank": 1,
"asset_id": 1001,
"asset_name": "战战生贺",
"cover_url": "https://...",
"owner_uid": 12345,
"owner_nickname": "爱战战",
"like_count": 999,
"is_original": false
},
{
"rank": 2,
"asset_id": 1002,
"asset_name": "肖战同框",
"cover_url": "https://...",
"owner_uid": 12346,
"owner_nickname": "小飞侠",
"like_count": 888,
"is_original": true
}
],
"page": 1,
"page_size": 10,
"total": 100
}
}
我的排名返回说明:
| 状态 | rank | 说明 |
|---|---|---|
| 上榜 | 5 | 在 Top N 内,显示具体排名 |
| 未上榜 | null | 不在 Top N 内,显示差距 |
上榜返回示例 (my_ranking):
{
"my_ranking": {
"rank": 5,
"asset_id": 1005,
"asset_name": "我的藏品",
"cover_url": "https://...",
"like_count": 88,
"status": "ranked"
}
}
未上榜返回示例 (my_ranking):
{
"my_ranking": {
"rank": null,
"asset_id": 1005,
"asset_name": "我的藏品",
"cover_url": "https://...",
"like_count": 50,
"status": "unranked",
"diff_to_rank": 50
}
}
统计逻辑:
| 维度 | 说明 |
|---|---|
displaying |
统计当前正在展示的藏品的点赞数(Exhibition.ExpireAt > Now()) |
month |
统计当前自然月内(2026年3月1日-3月31日)获得的点赞数 |
total |
统计藏品累计点赞数 |
多藏品处理:
- 用户有多个藏品时,返回排行最高的那个藏品排名
分页规则:
- 每次返回 10 条(page_size 默认 10)
- 榜单最多显示前 100 名(可配置)
3.3 自制排行榜
3.3.1 获取自制排行榜
接口: GET /api/v1/rankings/original
认证: 需认证 (JWT Token)
Query 参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| dimension | string | 是 | 统计维度: displaying(展示中) / month(本月) / total(全部) |
| star_id | int64 | 否 | 粉丝身份ID筛选,不传则返回当前身份数据 |
| page | int | 否 | 页码,默认1 |
| page_size | int | 否 | 每页数量,默认10 |
返回示例:
{
"code": 200,
"message": "ok",
"data": {
"my_ranking": {
"rank": 3,
"asset_id": 1002,
"asset_name": "自制海报",
"cover_url": "https://...",
"like_count": 666,
"status": "ranked"
},
"items": [
{
"rank": 1,
"asset_id": 1002,
"asset_name": "自制海报",
"cover_url": "https://...",
"owner_uid": 12346,
"owner_nickname": "小飞侠",
"like_count": 888,
"material_url": "https://...",
"is_original": true
}
],
"page": 1,
"page_size": 10,
"total": 50
}
}
统计逻辑:
- 仅统计
is_original = true的自制藏品 - 其他维度逻辑同热度排行榜
4. 前端界面设计
4.1 排行榜入口
在首页广场添加"排行榜"Tab,或在个人中心添加入口。
4.2 排行榜页面结构
┌─────────────────────────────┐
│ 排行榜 │
├─────────────────────────────┤
│ [热度] [自制] │ ← Tab 切换
├─────────────────────────────┤
│ │
│ 🥇 用户A 999 赞 │
│ [图片] │
│ │
│ 🥈 用户B 888 赞 │
│ [图片] │
│ │
│ 🥉 用户C 777 赞 │
│ [图片] │
│ │
├─────────────────────────────┤
│ [展示中] [本月] [全部] │ ← 维度切换
└─────────────────────────────┘
4.3 我的排名吸底展示
上榜状态:
┌─────────────────────────────┐
│ 排行榜 │
│ ... │
│ 8. 用户H 100 赞 │
│ [图片] │
│ 9. 用户I 99 赞 │
│ [图片] │
│ 10. 用户J 98 赞 │
│ [图片] │
├─────────────────────────────┤
│ 🏆 我的排名: 第5名 │
│ ❤️ [图片] 我的藏品 88赞 │
└─────────────────────────────┘
未上榜状态:
┌─────────────────────────────┐
│ 排行榜 │
│ ... │
│ 98. 用户H 60 赞 │
│ [图片] │
│ 99. 用户I 59 赞 │
│ [图片] │
│ 100. 用户J 58 赞 │
│ [图片] │
├─────────────────────────────┤
│ 😢 未上榜 │
│ 距离上榜还差 8 热度 │
│ [图片] 我的藏品 50赞 │
└─────────────────────────────┘
空状态:
┌─────────────────────────────┐
│ 排行榜 │
├─────────────────────────────┤
│ │
│ 暂无数据,快去创作吧 │
│ │
│ [去铸造] │
└─────────────────────────────┘
5. 数据库变更
5.1 藏品表扩展
ALTER TABLE assets ADD COLUMN is_original BOOLEAN DEFAULT FALSE;
6. 性能优化
6.1 缓存策略
| 数据 | 缓存策略 | 过期时间 |
|---|---|---|
| 热度排行榜 | Redis ZSet | 5分钟 |
| 自制排行榜 | Redis ZSet | 5分钟 |
6.2 定时任务
- 排行榜定时刷新 - 每5分钟更新一次排行榜缓存
- 排名计算 - 每日凌晨重新计算精确排名
7. 业务流程
7.1 点赞时更新排行榜
用户点赞藏品
↓
AssetLike 表新增记录 (记录点赞时间用于本月统计)
↓
Asset.LikeCount +1
↓
更新 Redis 热度/自制排行 ZSet
7.2 展示中藏品统计
定时任务扫描 Exhibition 表
↓
筛选 ExpireAt > Now() 的记录
↓
关联 Asset 表获取点赞数
↓
更新 Redis 展示中排行 ZSet
7.3 本月统计
用户查询本月排行时
↓
计算当月起始时间戳 (2026-03-01 00:00:00)
↓
筛选 AssetLike.CreatedAt >= 月起始时间
↓
按 AssetID 分组统计点赞数
↓
返回排行结果
7.4 我的排名计算
用户请求排行榜
↓
获取用户在该粉丝身份下所有藏品
↓
找出点赞数最高的藏品
↓
查询该藏品在榜单中的排名
↓
如果不在 Top 100,计算与第100名差距
↓
返回 my_ranking 信息(含 cover_url)
8. 待确认问题
-
粉丝身份隔离: 排行榜是否需要按粉丝身份隔离?
- ✅ 已确认:按当前用户的 star_id 过滤
-
展示中判定: 展位被占后4小时自动下架,如何处理?
- ✅ Exhibition.ExpireAt > Now() 即为展示中
-
本月统计: 自然月切换时(如3月1日),是否需要清理缓存?
- 待定:建议跨月时刷新缓存
-
Top N 默认值: 默认显示前多少名?
- ✅ 已确认:默认 100
9. 版本历史
| 版本 | 日期 | 说明 |
|---|---|---|
| V1.0 | 2026-03-11 | 初始版本 |
| V1.1 | 2026-03-12 | 移除活动排行榜,补充我的排名、差距计算、分页规则 |
| V1.2 | 2026-03-12 | my_ranking 补充 cover_url 字段 |