# TopFans 排行榜功能需求文档 ## 1. 功能概述 ### 1.1 产品背景 排行榜功能旨在提升用户活跃度和参与度,通过展示热门藏品和自制佳作榜单,激励用户创作和互动。 ### 1.2 排行榜类型 | 排行类型 | 统计维度 | 数据来源 | |----------|----------|----------| | 热度排行 | 展示中 / 本月 / 全部 | 藏品点赞数 | | 自制排行 | 展示中 / 本月 / 全部 | 自制藏品点赞数 | > **注意**: 活动排行榜功能待后续单独设计 --- ## 2. 数据模型设计 ### 2.1 现有数据分析 **Asset (藏品表)** - `LikeCount` - 点赞数(累计) - `OwnerUID` - 拥有者ID - `StarID` - 粉丝身份ID - `CreatedAt` - 创建时间 **AssetLike (点赞记录表)** - `AssetID` - 藏品ID - `UserID` - 点赞用户ID - `CreatedAt` - 点赞时间(用于本月统计) **Exhibition (展品展示表)** - `AssetID` - 展品ID - `StartTime` - 开始展示时间 - `ExpireAt` - 过期时间(用于"展示中"判定) ### 2.2 新增数据模型 #### 2.2.1 藏品表扩展 在 Asset 表中增加 `IsOriginal` 字段(true=自制藏品): ```go type Asset struct { // ... 现有字段 IsOriginal bool `gorm:"default:false;column:is_original"` // 是否自制藏品 } ``` --- ## 3. API 接口设计 ### 3.1 通用响应格式 ```json { "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 | **返回示例**: ```json { "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)**: ```json { "my_ranking": { "rank": 5, "asset_id": 1005, "asset_name": "我的藏品", "cover_url": "https://...", "like_count": 88, "status": "ranked" } } ``` **未上榜返回示例 (my_ranking)**: ```json { "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 | **返回示例**: ```json { "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 藏品表扩展 ```sql ALTER TABLE assets ADD COLUMN is_original BOOLEAN DEFAULT FALSE; ``` --- ## 6. 性能优化 ### 6.1 缓存策略 | 数据 | 缓存策略 | 过期时间 | |------|----------|----------| | 热度排行榜 | Redis ZSet | 5分钟 | | 自制排行榜 | Redis ZSet | 5分钟 | ### 6.2 定时任务 1. **排行榜定时刷新** - 每5分钟更新一次排行榜缓存 2. **排名计算** - 每日凌晨重新计算精确排名 --- ## 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. 待确认问题 1. **粉丝身份隔离**: 排行榜是否需要按粉丝身份隔离? - ✅ 已确认:按当前用户的 star_id 过滤 2. **展示中判定**: 展位被占后4小时自动下架,如何处理? - ✅ Exhibition.ExpireAt > Now() 即为展示中 3. **本月统计**: 自然月切换时(如3月1日),是否需要清理缓存? - 待定:建议跨月时刷新缓存 4. **Top N 默认值**: 默认显示前多少名? - ✅ 已确认:默认 100 --- ## 9. 版本历史 | 版本 | 日期 | 说明 | |------|------|------| | V1.0 | 2026-03-11 | 初始版本 | | V1.1 | 2026-03-12 | 移除活动排行榜,补充我的排名、差距计算、分页规则 | | V1.2 | 2026-03-12 | my_ranking 补充 cover_url 字段 |