7.8 KiB
7.8 KiB
灵感瀑布(横向瀑布流藏品展示)设计文档
创建日期: 2026-04-27 项目: TopFans 横向瀑布流藏品展示 服务: galleryService (Go Dubbo-go) 状态: 设计中
一、设计目标
横向瀑布流展示该 star_id 下所有用户展出的藏品,支持随机展示、分页、按类型过滤。
二、数据来源
主表: Exhibition(展品展示表)
关联表: Asset(资产表)- 用于按 material_type 过滤
筛选条件:
occupier_star_id = ?(当前用户 star_id)expire_at > now(未过期)deleted_at IS NULL(未删除)
按 type 过滤: JOIN Asset 表,WHERE assets.material_type = ?
三、API 设计
3.1 获取灵感瀑布(横向瀑布流)藏品列表
GET /api/v1/inspiration-flow
Query 参数:
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|---|---|---|---|---|
| page | int | 否 | 1 | 页码 |
| page_size | int | 否 | 20 | 每页数量(最大100) |
| type | string | 否 | all | 过滤类型:badge/poster/original/all |
HTTP 响应:
{
"code": 200,
"message": "ok",
"data": {
"items": [
{
"asset_id": 123,
"name": "藏品名称",
"cover_url": "https://xxx.com/cover.png",
"like_count": 100,
"owner_nickname": "粉丝昵称"
}
],
"page": 1,
"page_size": 20,
"total": 100,
"has_more": true
}
}
错误码:
| code | 说明 |
|---|---|
| 200 | 成功 |
| 401 | 用户认证失败 |
| 500 | 服务器内部错误 |
四、Proto 定义
4.1 Request / Response
// 获取灵感瀑布藏品列表请求
message GetInspirationFlowRequest {
int32 page = 1; // 页码(默认1)
int32 page_size = 2; // 每页数量(默认20,最大100)
string type = 3; // 过滤类型:badge/poster/original/all(默认all)
}
// 获取灵感瀑布藏品列表响应
message GetInspirationFlowResponse {
topfans.common.BaseResponse base = 1;
InspirationFlowData data = 2;
}
// 灵感瀑布数据
message InspirationFlowData {
repeated InspirationFlowItem items = 1; // 藏品列表
int32 page = 2; // 当前页码
int32 page_size = 3; // 每页数量
int64 total = 4; // 总数量
bool has_more = 5; // 是否有更多
}
// 灵感瀑布藏品项
message InspirationFlowItem {
int64 asset_id = 1; // 资产ID
string name = 2; // 藏品名称
string cover_url = 3; // 封面图URL
int32 like_count = 4; // 点赞数
string owner_nickname = 5; // 展出者昵称
}
4.2 Service 方法
在 GalleryService 中新增方法:
// 展馆服务
service GalleryService {
// ... 现有方法 ...
// 获取灵感瀑布藏品列表
rpc GetInspirationFlow(GetInspirationFlowRequest) returns (GetInspirationFlowResponse) {
option (google.api.http) = {
get: "/api/v1/inspiration-flow"
};
}
}
五、核心逻辑
5.1 查询逻辑
SELECT
e.asset_id,
a.name,
a.cover_url,
a.like_count,
fp.nickname as owner_nickname
FROM exhibitions e
JOIN assets a ON e.asset_id = a.id
JOIN fan_profiles fp ON e.occupier_uid = fp.user_id AND e.occupier_star_id = fp.star_id
WHERE e.occupier_star_id = ?
AND e.expire_at > ?
AND e.deleted_at IS NULL
AND a.status = 1
AND a.is_active = true
AND (? = 'all' OR a.material_type = ?)
ORDER BY RANDOM()
LIMIT ? OFFSET ?;
参数说明:
? = star_id(当前用户 star_id)? = now(当前时间戳)? = type(过滤类型)? = page_size? = (page - 1) * page_size
5.2 6小时自动下架
已实现的 GetExpiredExhibitions 方法查询已过期的展览记录,调度器定期清理。
下架时执行:
- 设置
deleted_at = now - 调用
ClearAssetLikeRecordsRPC 重置点赞数为 0 - 更新
asset_registry.display_status = 0
5.3 重新上架逻辑
当藏品重新展示时:
- 创建新的 Exhibition 记录
- 更新
asset_registry.display_status = 1 - 点赞数从 0 开始(因为之前下架时已清除点赞记录)
5.4 随机展示
使用 ORDER BY RANDOM() 实现随机排序。
六、数据模型
6.1 Exhibition 表(已有字段)
type Exhibition struct {
ID int64 `gorm:"primaryKey"`
AssetID int64 `gorm:"not null"`
SlotID int64 `gorm:"not null"`
HostProfileID int64 `gorm:"not null"`
OccupierUID int64 `gorm:"not null"`
OccupierStarID int64 `gorm:"not null;index"`
StartTime int64 `gorm:"not null"`
ExpireAt int64 `gorm:"not null;index"`
CreatedAt int64 `gorm:"not null"`
UpdatedAt int64 `gorm:"not null"`
DeletedAt *int64 `gorm:"index"`
}
6.2 Asset 表关联字段
type Asset struct {
// ... 现有字段 ...
MaterialType string `gorm:"column:material_type"` // 素材类型:badge/poster/original
IsOriginal bool `gorm:"column:is_original"`
LikeCount int32 `gorm:"not null;default:0"`
}
新增字段(需数据库变更):
ALTER TABLE assets ADD COLUMN material_type VARCHAR(50) DEFAULT 'original';
material_type 枚举值:
original— 原创(默认值)badge— 吧唧poster— 海报
七、收益记录
预留: 收益记录通过经济系统已有的 exhibition_revenue change_type 实现。
后续按需在 Exhibition 创建/删除时写入 crystal_transaction_records,change_type 为 exhibition_revenue。
八、配置项
| 配置项 | 说明 | 默认值 |
|---|---|---|
| exhibition_duration | 展览时长(秒) | 14400(4小时,可配置) |
| inspiration_flow_page_size | 默认每页数量 | 20 |
| inspiration_flow_max_page_size | 最大每页数量 | 100 |
九、项目文件结构
backend/
├── proto/
│ └── gallery.proto # 修改:新增 GetInspirationFlow 方法
│
├── pkg/proto/
│ ├── gallery/
│ │ ├── gallery.pb.go # 重新生成
│ │ └── gallery.triple.go # 重新生成
│
├── services/galleryService/
│ ├── repository/
│ │ └── gallery_repository.go # 修改:新增 GetActiveExhibitions 方法
│ │
│ ├── service/
│ │ └── gallery_service.go # 修改:新增 GetInspirationFlow 方法
│ │
│ ├── provider/
│ │ └── gallery_provider.go # 修改:新增 GetInspirationFlow Handler
│ │
│ └── config/
│ └── gallery_config.go # 修改:新增灵感瀑布配置项
│
├── scripts/
│ └── migrate_add_material_type.sql # 新增:material_type 字段迁移脚本
│
└── gateway/
├── controller/
│ └── gallery_controller.go # 修改:新增 GetInspirationFlow 路由处理
│
└── router/
└── router.go # 修改:新增 /api/v1/inspiration-flow 路由
十、待确认事项
- material_type 枚举值:badge/poster/original,后续按需扩展
- 随机排序策略:ORDER BY RANDOM() 在数据量大时可能有性能问题,后续可考虑按 like_count 随机采样
- 展览时长:默认 6 小时(21600 秒),可通过配置修改
十一、数据库变更
11.1 新增字段
-- assets 表新增 material_type 字段
ALTER TABLE assets ADD COLUMN material_type VARCHAR(50) DEFAULT 'original';
-- 创建索引优化查询
CREATE INDEX IF NOT EXISTS idx_assets_material_type ON assets(material_type);
11.2 迁移脚本
-- 迁移脚本:backend/scripts/migrate_add_material_type.sql