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

10 KiB
Raw Blame History

我的作品统计(点赞/展出)设计文档

创建日期: 2026-04-27 项目: TopFans 我的作品统计 服务: socialService / galleryService 状态: 设计中


一、设计目标

提供用户查看自己点赞过的作品和展出过的作品的统计接口,返回实时点赞数。


二、数据来源

2.1 我点赞的作品

主表: asset_likes点赞记录表

关联表: assets资产表- 用于获取藏品信息

筛选条件:

  • user_id = ? (当前用户)
  • star_id = ? (当前 star_id)
  • assets.deleted_at IS NULL (藏品未删除)
  • assets.is_active = true (藏品已激活)

2.2 我展出的作品

主表: exhibitions展品展示表

关联表: assets资产表- 用于获取藏品信息

筛选条件:

  • occupier_uid = ? (当前用户)
  • occupier_star_id = ? (当前 star_id)
  • deleted_at IS NULL (未删除)

三、API 设计

3.1 获取我点赞的作品列表

GET /api/v1/me/liked-assets

Query 参数:

参数 类型 必填 默认值 说明
page int 1 页码
page_size int 20 每页数量最大100

HTTP 响应:

{
  "code": 200,
  "message": "ok",
  "data": {
    "items": [
      {
        "asset_id": 123,
        "name": "藏品名称",
        "cover_url": "https://xxx.com/cover.png",
        "like_count": 100,
        "liked_at": 1714214400000
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 50,
    "has_more": true
  }
}

字段说明:

字段 类型 说明
asset_id int64 资产ID
name string 藏品名称
cover_url string 封面图URL
like_count int32 实时点赞数(来自 assets 表)
liked_at int64 用户点赞该作品的时间(毫秒时间戳)

3.2 获取我展出的作品列表

GET /api/v1/me/exhibited-assets

Query 参数:

参数 类型 必填 默认值 说明
page int 1 页码
page_size int 20 每页数量最大100

HTTP 响应:

{
  "code": 200,
  "message": "ok",
  "data": {
    "items": [
      {
        "asset_id": 123,
        "name": "藏品名称",
        "cover_url": "https://xxx.com/cover.png",
        "like_count": 100,
        "exhibited_at": 1714214400000,
        "expire_at": 1714278400000
      }
    ],
    "page": 1,
    "page_size": 20,
    "total": 10,
    "has_more": false
  }
}

字段说明:

字段 类型 说明
asset_id int64 资产ID
name string 藏品名称
cover_url string 封面图URL
like_count int32 实时点赞数(来自 assets 表)
exhibited_at int64 展出开始时间(毫秒时间戳)
expire_at int64 展出过期时间(毫秒时间戳)

3.3 错误码

code 说明
200 成功
401 用户认证失败
500 服务器内部错误

四、Proto 定义

4.1 我点赞的作品

// 获取我点赞的作品列表请求
message GetMyLikedAssetsRequest {
  int32 page = 1;        // 页码默认1
  int32 page_size = 2;   // 每页数量默认20最大100
}

// 获取我点赞的作品列表响应
message GetMyLikedAssetsResponse {
  topfans.common.BaseResponse base = 1;
  LikedAssetsData data = 2;
}

// 点赞作品数据
message LikedAssetsData {
  repeated LikedAssetItem items = 1;  // 作品列表
  int32 page = 2;                     // 当前页码
  int32 page_size = 3;                // 每页数量
  int64 total = 4;                     // 总数量
  bool has_more = 5;                   // 是否有更多
}

// 点赞作品项
message LikedAssetItem {
  int64 asset_id = 1;        // 资产ID
  string name = 2;           // 藏品名称
  string cover_url = 3;      // 封面图URL
  int32 like_count = 4;      // 实时点赞数
  int64 liked_at = 5;        // 点赞时间(毫秒时间戳)
}

4.2 我展出的作品

// 获取我展出的作品列表请求
message GetMyExhibitedAssetsRequest {
  int32 page = 1;        // 页码默认1
  int32 page_size = 2;   // 每页数量默认20最大100
}

// 获取我展出的作品列表响应
message GetMyExhibitedAssetsResponse {
  topfans.common.BaseResponse base = 1;
  ExhibitedAssetsData data = 2;
}

// 展出作品数据
message ExhibitedAssetsData {
  repeated ExhibitedAssetItem items = 1;  // 作品列表
  int32 page = 2;                         // 当前页码
  int32 page_size = 3;                     // 每页数量
  int64 total = 4;                         // 总数量
  bool has_more = 5;                       // 是否有更多
}

// 展出作品项
message ExhibitedAssetItem {
  int64 asset_id = 1;        // 资产ID
  string name = 2;           // 藏品名称
  string cover_url = 3;      // 封面图URL
  int32 like_count = 4;      // 实时点赞数
  int64 exhibited_at = 5;    // 展出开始时间(毫秒时间戳)
  int64 expire_at = 6;        // 展出过期时间(毫秒时间戳)
}

4.3 Service 方法

在 SocialService 中新增方法:

// 社交服务
service SocialService {
  // ... 现有方法 ...

  // 获取我点赞的作品列表
  rpc GetMyLikedAssets(GetMyLikedAssetsRequest) returns (GetMyLikedAssetsResponse) {
    option (google.api.http) = {
      get: "/api/v1/me/liked-assets"
    };
  }
}

在 GalleryService 中新增方法:

// 展馆服务
service GalleryService {
  // ... 现有方法 ...

  // 获取我展出的作品列表
  rpc GetMyExhibitedAssets(GetMyExhibitedAssetsRequest) returns (GetMyExhibitedAssetsResponse) {
    option (google.api.http) = {
      get: "/api/v1/me/exhibited-assets"
    };
  }
}

五、核心逻辑

5.1 查询我点赞的作品

SELECT
    al.asset_id,
    a.name,
    a.cover_url,
    a.like_count,
    al.created_at as liked_at
FROM asset_likes al
JOIN assets a ON al.asset_id = a.id
WHERE al.user_id = ?
  AND al.star_id = ?
  AND a.deleted_at IS NULL
  AND a.is_active = true
ORDER BY al.created_at DESC
LIMIT ? OFFSET ?;

-- 计数
SELECT COUNT(*)
FROM asset_likes al
JOIN assets a ON al.asset_id = a.id
WHERE al.user_id = ?
  AND al.star_id = ?
  AND a.deleted_at IS NULL
  AND a.is_active = true;

参数说明:

  • ? = user_id (当前用户)
  • ? = star_id (当前 star_id)
  • ? = page_size
  • ? = (page - 1) * page_size

5.2 查询我展出的作品(只返回展出中的)

SELECT
    e.asset_id,
    a.name,
    a.cover_url,
    a.like_count,
    e.start_time as exhibited_at,
    e.expire_at
FROM exhibitions e
JOIN assets a ON e.asset_id = a.id
WHERE e.occupier_uid = ?
  AND e.occupier_star_id = ?
  AND e.deleted_at IS NULL
  AND e.expire_at > ?  -- 只返回未过期的
ORDER BY e.start_time DESC
LIMIT ? OFFSET ?;

-- 计数
SELECT COUNT(*)
FROM exhibitions e
WHERE e.occupier_uid = ?
  AND e.occupier_star_id = ?
  AND e.deleted_at IS NULL
  AND e.expire_at > ?;  -- 只返回未过期的

参数说明:

  • ? = user_id (当前用户)
  • ? = star_id (当前 star_id)
  • ? = now (当前时间戳,只显示未过期的)
  • ? = page_size
  • ? = (page - 1) * page_size

六、数据模型

6.1 asset_likes 表(已有字段)

type AssetLike struct {
    ID        int64 `gorm:"primaryKey"`
    AssetID   int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_asset"`
    UserID    int64 `gorm:"not null;uniqueIndex:uk_asset_likes_user_asset"`
    StarID    int64 `gorm:"not null;index"`
    CreatedAt int64 `gorm:"not null;index"`
}

6.2 exhibitions 表(已有字段)

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;index"`
    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.3 assets 表(已有字段)

type Asset struct {
    ID        int64  `gorm:"primaryKey"`
    Name      string `gorm:"type:varchar(100);not null"`
    CoverURL  string `gorm:"type:varchar(500);not null"`
    LikeCount int32  `gorm:"not null;default:0"`
    Status    int32  `gorm:"not null;default:0"`
    DeletedAt *int64 `gorm:"index"`
    IsActive  bool   `gorm:"default:true;not null"`
}

七、项目文件结构

backend/
├── proto/
│   ├── social.proto      # 修改:新增 GetMyLikedAssets 方法
│   └── gallery.proto     # 修改:新增 GetMyExhibitedAssets 方法
│
├── pkg/proto/
│   ├── social/
│   │   ├── social.pb.go         # 重新生成
│   │   └── social.triple.go      # 重新生成
│   └── gallery/
│       ├── gallery.pb.go        # 重新生成
│       └── gallery.triple.go    # 重新生成
│
├── services/socialService/
│   ├── repository/
│   │   └── asset_like_repository.go  # 修改:新增查询方法
│   │
│   ├── service/
│   │   └── asset_like_service.go     # 修改:新增 GetMyLikedAssets 方法
│   │
│   └── provider/
│       └── social_provider.go        # 修改:新增 GetMyLikedAssets Handler
│
├── services/galleryService/
│   ├── repository/
│   │   └── gallery_repository.go     # 修改:新增 GetExhibitionsByOccupier 方法
│   │
│   ├── service/
│   │   └── exhibition_service.go     # 修改:新增 GetMyExhibitedAssets 方法
│   │
│   └── provider/
│       └── gallery_provider.go       # 修改:新增 GetMyExhibitedAssets Handler
│
└── gateway/
    ├── controller/
    │   ├── social_controller.go       # 修改:新增 /api/v1/me/liked-assets 路由
    │   └── gallery_controller.go      # 修改:新增 /api/v1/me/exhibited-assets 路由
    │
    └── router/
        └── router.go                  # 修改:新增路由配置

八、已确认事项

  1. 只显示展出中的作品 — 通过 expire_at > now 过滤
  2. 排序方式 — 按展出时间倒序start_time DESC
  3. 分页大小 — 默认 20最大 100