topfans/docs/superpowers/specs/2026-04-27-inspiration-flow-design.md
2026-04-27 20:57:03 +08:00

7.8 KiB
Raw Blame History

灵感瀑布(横向瀑布流藏品展示)设计文档

创建日期: 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 方法查询已过期的展览记录,调度器定期清理。

下架时执行:

  1. 设置 deleted_at = now
  2. 调用 ClearAssetLikeRecords RPC 重置点赞数为 0
  3. 更新 asset_registry.display_status = 0

5.3 重新上架逻辑

当藏品重新展示时:

  1. 创建新的 Exhibition 记录
  2. 更新 asset_registry.display_status = 1
  3. 点赞数从 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_recordschange_type 为 exhibition_revenue


八、配置项

配置项 说明 默认值
exhibition_duration 展览时长(秒) 144004小时可配置
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 路由

十、待确认事项

  1. material_type 枚举值badge/poster/original后续按需扩展
  2. 随机排序策略ORDER BY RANDOM() 在数据量大时可能有性能问题,后续可考虑按 like_count 随机采样
  3. 展览时长:默认 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