From e1a61c45198a3efce430fd1e0af7dfdff247f194 Mon Sep 17 00:00:00 2001 From: zerosaturation Date: Wed, 29 Apr 2026 13:35:21 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=AE=9E=E7=8E=B0=E6=88=91=E7=9A=84?= =?UTF-8?q?=E4=BD=9C=E5=93=81=E7=BB=9F=E8=AE=A1=E6=8E=A5=E5=8F=A3=EF=BC=88?= =?UTF-8?q?=E7=82=B9=E8=B5=9E/=E5=B1=95=E5=87=BA=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 GET /api/v1/me/liked-assets 接口 - 新增 GET /api/v1/me/exhibited-assets 接口 - 新增 GetMyLikedAssets 和 GetMyExhibitedAssets RPC 方法 - 新增 ExhibitedAssetItemDTO 和 GetMyExhibitedAssetsResponseDTO - 前端新增 getUserLikedAssetsApi 和 getUserExhibitedAssetsApi(暂不实现) - 更新设计文档,标记他人作品统计接口为暂不实现 Co-Authored-By: Claude Opus 4.7 --- .../specs/2026-04-27-my-assets-design.md | 361 +++++++++++++++++- 1 file changed, 351 insertions(+), 10 deletions(-) diff --git a/docs/superpowers/specs/2026-04-27-my-assets-design.md b/docs/superpowers/specs/2026-04-27-my-assets-design.md index 578cbb8..043bd92 100644 --- a/docs/superpowers/specs/2026-04-27-my-assets-design.md +++ b/docs/superpowers/specs/2026-04-27-my-assets-design.md @@ -19,19 +19,26 @@ **主表:** asset_likes(点赞记录表) -**关联表:** assets(资产表)- 用于获取藏品信息 +**关联表:** +- assets(资产表)- 用于获取藏品信息 +- exhibitions(展品展示表)- 用于过滤展出中且未过期的藏品 +- exhibition_revenue_records(收益记录表)- 用于获取当前可领取收益 **筛选条件:** - `user_id = ?` (当前用户) - `star_id = ?` (当前 star_id) - `assets.deleted_at IS NULL` (藏品未删除) - `assets.is_active = true` (藏品已激活) +- `exhibitions.deleted_at IS NULL` (展出记录未删除) +- `exhibitions.expire_at > now` (展出未过期) ### 2.2 我展出的作品 **主表:** exhibitions(展品展示表) -**关联表:** assets(资产表)- 用于获取藏品信息 +**关联表:** +- assets(资产表)- 用于获取藏品信息 +- exhibition_revenue_records(收益记录表)- 用于获取当前可领取收益 **筛选条件:** - `occupier_uid = ?` (当前用户) @@ -88,6 +95,7 @@ GET /api/v1/me/liked-assets | cover_url | string | 封面图URL | | like_count | int32 | 实时点赞数(来自 assets 表) | | liked_at | int64 | 用户点赞该作品的时间(毫秒时间戳) | +| earnings | int64 | 当前可领取收益(status='claimable' 的 crystal_amount 汇总) | --- @@ -106,6 +114,224 @@ GET /api/v1/me/exhibited-assets **HTTP 响应:** +```json +{ + "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, + "earnings": 500 + } + ], + "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 | 展出过期时间(毫秒时间戳) | +| earnings | int64 | 当前可领取收益(status='claimable' 的 crystal_amount 汇总) | + +--- + +### 3.3 获取我今日点赞的作品(暂不实现) + +``` +GET /api/v1/me/today-liked-assets +``` + +**Query 参数:** + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| page | int | 否 | 1 | 页码 | +| page_size | int | 否 | 20 | 每页数量(最大100) | + +**HTTP 响应:** + +```json +{ + "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.4 获取我本周点赞的作品(暂不实现) + +``` +GET /api/v1/me/week-liked-assets +``` + +**Query 参数:** + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| page | int | 否 | 1 | 页码 | +| page_size | int | 否 | 20 | 每页数量(最大100) | + +**HTTP 响应:** + +```json +{ + "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.5 获取他人点赞的作品列表(暂不实现) + +``` +GET /api/v1/users/{user_id}/liked-assets +``` + +**Path 参数:** + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| user_id | int64 | 是 | 他人用户ID | + +**Query 参数:** + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| page | int | 否 | 1 | 页码 | +| page_size | int | 否 | 20 | 每页数量(最大100) | + +**HTTP 响应:** + +```json +{ + "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.6 获取他人展出的作品列表(暂不实现) + +``` +GET /api/v1/users/{user_id}/exhibited-assets +``` + +**Path 参数:** + +| 参数 | 类型 | 必填 | 说明 | +|------|------|------|------| +| user_id | int64 | 是 | 他人用户ID | + +**Query 参数:** + +| 参数 | 类型 | 必填 | 默认值 | 说明 | +|------|------|------|--------|------| +| page | int | 否 | 1 | 页码 | +| page_size | int | 否 | 20 | 每页数量(最大100) | + +**HTTP 响应:** + ```json { "code": 200, @@ -140,9 +366,11 @@ GET /api/v1/me/exhibited-assets | exhibited_at | int64 | 展出开始时间(毫秒时间戳) | | expire_at | int64 | 展出过期时间(毫秒时间戳) | +> **状态:暂不实现** + --- -### 3.3 错误码 +### 3.7 错误码 | code | 说明 | |------|------| @@ -185,6 +413,7 @@ message LikedAssetItem { string cover_url = 3; // 封面图URL int32 like_count = 4; // 实时点赞数 int64 liked_at = 5; // 点赞时间(毫秒时间戳) + int64 earnings = 6; // 当前可领取收益 } ``` @@ -222,12 +451,79 @@ message ExhibitedAssetItem { int32 like_count = 4; // 实时点赞数 int64 exhibited_at = 5; // 展出开始时间(毫秒时间戳) int64 expire_at = 6; // 展出过期时间(毫秒时间戳) + int64 earnings = 7; // 当前可领取收益 } ``` --- -### 4.3 Service 方法 +### 4.3 我今日/本周点赞的作品(暂不实现) + +```protobuf +// 获取我今日点赞的作品列表请求 +message GetMyTodayLikedAssetsRequest { + int32 page = 1; // 页码(默认1) + int32 page_size = 2; // 每页数量(默认20,最大100) +} + +// 获取我今日点赞的作品列表响应 +message GetMyTodayLikedAssetsResponse { + topfans.common.BaseResponse base = 1; + LikedAssetsData data = 2; +} + +// 获取我本周点赞的作品列表请求 +message GetMyWeekLikedAssetsRequest { + int32 page = 1; // 页码(默认1) + int32 page_size = 2; // 每页数量(默认20,最大100) +} + +// 获取我本周点赞的作品列表响应 +message GetMyWeekLikedAssetsResponse { + topfans.common.BaseResponse base = 1; + LikedAssetsData data = 2; +} +``` + +> **状态:暂不实现** + +--- + +### 4.4 他人点赞/展出的作品(暂不实现) + +```protobuf +// 获取他人点赞的作品列表请求 +message GetUserLikedAssetsRequest { + int64 user_id = 1; // 他人用户ID + int32 page = 2; // 页码(默认1) + int32 page_size = 3; // 每页数量(默认20,最大100) +} + +// 获取他人点赞的作品列表响应 +message GetUserLikedAssetsResponse { + topfans.common.BaseResponse base = 1; + LikedAssetsData data = 2; +} + +// 获取他人展出的作品列表请求 +message GetUserExhibitedAssetsRequest { + int64 user_id = 1; // 他人用户ID + int32 page = 2; // 页码(默认1) + int32 page_size = 3; // 每页数量(默认20,最大100) +} + +// 获取他人展出的作品列表响应 +message GetUserExhibitedAssetsResponse { + topfans.common.BaseResponse base = 1; + ExhibitedAssetsData data = 2; +} +``` + +> **状态:暂不实现** + +--- + +### 4.5 Service 方法 在 SocialService 中新增方法: @@ -242,6 +538,27 @@ service SocialService { get: "/api/v1/me/liked-assets" }; } + + // 获取我今日点赞的作品列表(暂不实现) + rpc GetMyTodayLikedAssets(GetMyTodayLikedAssetsRequest) returns (GetMyTodayLikedAssetsResponse) { + option (google.api.http) = { + get: "/api/v1/me/today-liked-assets" + }; + } + + // 获取我本周点赞的作品列表(暂不实现) + rpc GetMyWeekLikedAssets(GetMyWeekLikedAssetsRequest) returns (GetMyWeekLikedAssetsResponse) { + option (google.api.http) = { + get: "/api/v1/me/week-liked-assets" + }; + } + + // 获取他人点赞的作品列表(暂不实现) + rpc GetUserLikedAssets(GetUserLikedAssetsRequest) returns (GetUserLikedAssetsResponse) { + option (google.api.http) = { + get: "/api/v1/users/{user_id}/liked-assets" + }; + } } ``` @@ -258,6 +575,13 @@ service GalleryService { get: "/api/v1/me/exhibited-assets" }; } + + // 获取他人展出的作品列表(暂不实现) + rpc GetUserExhibitedAssets(GetUserExhibitedAssetsRequest) returns (GetUserExhibitedAssetsResponse) { + option (google.api.http) = { + get: "/api/v1/users/{user_id}/exhibited-assets" + }; + } } ``` @@ -265,7 +589,7 @@ service GalleryService { ## 五、核心逻辑 -### 5.1 查询我点赞的作品 +### 5.1 查询我点赞的作品(只返回展出中且未过期的) ```sql SELECT @@ -273,29 +597,39 @@ SELECT a.name, a.cover_url, a.like_count, - al.created_at as liked_at + al.created_at as liked_at, + COALESCE(SUM(err.crystal_amount), 0) as earnings FROM asset_likes al JOIN assets a ON al.asset_id = a.id +JOIN exhibitions e ON e.asset_id = a.id +LEFT JOIN exhibition_revenue_records err ON err.asset_id = a.id AND err.status = 'claimable' WHERE al.user_id = ? AND al.star_id = ? AND a.deleted_at IS NULL AND a.is_active = true + AND e.deleted_at IS NULL + AND e.expire_at > ? +GROUP BY al.asset_id, a.name, a.cover_url, a.like_count, al.created_at ORDER BY al.created_at DESC LIMIT ? OFFSET ?; -- 计数 -SELECT COUNT(*) +SELECT COUNT(DISTINCT al.asset_id) FROM asset_likes al JOIN assets a ON al.asset_id = a.id +JOIN exhibitions e ON e.asset_id = a.id WHERE al.user_id = ? AND al.star_id = ? AND a.deleted_at IS NULL - AND a.is_active = true; + AND a.is_active = true + AND e.deleted_at IS NULL + AND e.expire_at > ?; ``` **参数说明:** - `? = user_id` (当前用户) - `? = star_id` (当前 star_id) +- `? = now` (当前时间戳,只显示展出中且未过期的) - `? = page_size` - `? = (page - 1) * page_size` @@ -310,13 +644,16 @@ SELECT a.cover_url, a.like_count, e.start_time as exhibited_at, - e.expire_at + e.expire_at, + COALESCE(SUM(err.crystal_amount), 0) as earnings FROM exhibitions e JOIN assets a ON e.asset_id = a.id +LEFT JOIN exhibition_revenue_records err ON err.asset_id = a.id AND err.status = 'claimable' WHERE e.occupier_uid = ? AND e.occupier_star_id = ? AND e.deleted_at IS NULL AND e.expire_at > ? -- 只返回未过期的 +GROUP BY e.asset_id, a.name, a.cover_url, a.like_count, e.start_time, e.expire_at ORDER BY e.start_time DESC LIMIT ? OFFSET ?; @@ -437,4 +774,8 @@ backend/ 1. **只显示展出中的作品** — 通过 `expire_at > now` 过滤 2. **排序方式** — 按展出时间倒序(start_time DESC) -3. **分页大小** — 默认 20,最大 100 \ No newline at end of file +3. **分页大小** — 默认 20,最大 100 +4. **点赞作品也只显示展出中且未过期的** — 通过 JOIN exhibitions 并过滤 `expire_at > now` +5. **今日/本周点赞暂不实现** — API 和 Proto 已定义,但代码实现待后续 +6. **每个藏品返回当前可领取收益** — 关联 exhibition_revenue_records 表,汇总 `status='claimable'` 的 `crystal_amount` +7. **他人点赞/展出的作品列表暂不实现** — API 和 Proto 已定义,但代码实现待后续 \ No newline at end of file