feat: 实现我的作品统计接口(点赞/展出)

- 新增 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 <noreply@anthropic.com>
This commit is contained in:
zerosaturation 2026-04-29 13:35:21 +08:00
parent ca3eeb8b5a
commit e1a61c4519

View File

@ -19,19 +19,26 @@
**主表:** asset_likes点赞记录表 **主表:** asset_likes点赞记录表
**关联表:** assets资产表- 用于获取藏品信息 **关联表:**
- assets资产表- 用于获取藏品信息
- exhibitions展品展示表- 用于过滤展出中且未过期的藏品
- exhibition_revenue_records收益记录表- 用于获取当前可领取收益
**筛选条件:** **筛选条件:**
- `user_id = ?` (当前用户) - `user_id = ?` (当前用户)
- `star_id = ?` (当前 star_id) - `star_id = ?` (当前 star_id)
- `assets.deleted_at IS NULL` (藏品未删除) - `assets.deleted_at IS NULL` (藏品未删除)
- `assets.is_active = true` (藏品已激活) - `assets.is_active = true` (藏品已激活)
- `exhibitions.deleted_at IS NULL` (展出记录未删除)
- `exhibitions.expire_at > now` (展出未过期)
### 2.2 我展出的作品 ### 2.2 我展出的作品
**主表:** exhibitions展品展示表 **主表:** exhibitions展品展示表
**关联表:** assets资产表- 用于获取藏品信息 **关联表:**
- assets资产表- 用于获取藏品信息
- exhibition_revenue_records收益记录表- 用于获取当前可领取收益
**筛选条件:** **筛选条件:**
- `occupier_uid = ?` (当前用户) - `occupier_uid = ?` (当前用户)
@ -88,6 +95,7 @@ GET /api/v1/me/liked-assets
| cover_url | string | 封面图URL | | cover_url | string | 封面图URL |
| like_count | int32 | 实时点赞数(来自 assets 表) | | like_count | int32 | 实时点赞数(来自 assets 表) |
| liked_at | int64 | 用户点赞该作品的时间(毫秒时间戳) | | liked_at | int64 | 用户点赞该作品的时间(毫秒时间戳) |
| earnings | int64 | 当前可领取收益status='claimable' 的 crystal_amount 汇总) |
--- ---
@ -106,6 +114,224 @@ GET /api/v1/me/exhibited-assets
**HTTP 响应:** **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 ```json
{ {
"code": 200, "code": 200,
@ -140,9 +366,11 @@ GET /api/v1/me/exhibited-assets
| exhibited_at | int64 | 展出开始时间(毫秒时间戳) | | exhibited_at | int64 | 展出开始时间(毫秒时间戳) |
| expire_at | int64 | 展出过期时间(毫秒时间戳) | | expire_at | int64 | 展出过期时间(毫秒时间戳) |
> **状态:暂不实现**
--- ---
### 3.3 错误码 ### 3.7 错误码
| code | 说明 | | code | 说明 |
|------|------| |------|------|
@ -185,6 +413,7 @@ message LikedAssetItem {
string cover_url = 3; // 封面图URL string cover_url = 3; // 封面图URL
int32 like_count = 4; // 实时点赞数 int32 like_count = 4; // 实时点赞数
int64 liked_at = 5; // 点赞时间(毫秒时间戳) int64 liked_at = 5; // 点赞时间(毫秒时间戳)
int64 earnings = 6; // 当前可领取收益
} }
``` ```
@ -222,12 +451,79 @@ message ExhibitedAssetItem {
int32 like_count = 4; // 实时点赞数 int32 like_count = 4; // 实时点赞数
int64 exhibited_at = 5; // 展出开始时间(毫秒时间戳) int64 exhibited_at = 5; // 展出开始时间(毫秒时间戳)
int64 expire_at = 6; // 展出过期时间(毫秒时间戳) 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 中新增方法: 在 SocialService 中新增方法:
@ -242,6 +538,27 @@ service SocialService {
get: "/api/v1/me/liked-assets" 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" 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 ```sql
SELECT SELECT
@ -273,29 +597,39 @@ SELECT
a.name, a.name,
a.cover_url, a.cover_url,
a.like_count, 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 FROM asset_likes al
JOIN assets a ON al.asset_id = a.id 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 = ? WHERE al.user_id = ?
AND al.star_id = ? AND al.star_id = ?
AND a.deleted_at IS NULL 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 > ?
GROUP BY al.asset_id, a.name, a.cover_url, a.like_count, al.created_at
ORDER BY al.created_at DESC ORDER BY al.created_at DESC
LIMIT ? OFFSET ?; LIMIT ? OFFSET ?;
-- 计数 -- 计数
SELECT COUNT(*) SELECT COUNT(DISTINCT al.asset_id)
FROM asset_likes al FROM asset_likes al
JOIN assets a ON al.asset_id = a.id JOIN assets a ON al.asset_id = a.id
JOIN exhibitions e ON e.asset_id = a.id
WHERE al.user_id = ? WHERE al.user_id = ?
AND al.star_id = ? AND al.star_id = ?
AND a.deleted_at IS NULL 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` (当前用户) - `? = user_id` (当前用户)
- `? = star_id` (当前 star_id) - `? = star_id` (当前 star_id)
- `? = now` (当前时间戳,只显示展出中且未过期的)
- `? = page_size` - `? = page_size`
- `? = (page - 1) * page_size` - `? = (page - 1) * page_size`
@ -310,13 +644,16 @@ SELECT
a.cover_url, a.cover_url,
a.like_count, a.like_count,
e.start_time as exhibited_at, e.start_time as exhibited_at,
e.expire_at e.expire_at,
COALESCE(SUM(err.crystal_amount), 0) as earnings
FROM exhibitions e FROM exhibitions e
JOIN assets a ON e.asset_id = a.id 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 = ? WHERE e.occupier_uid = ?
AND e.occupier_star_id = ? AND e.occupier_star_id = ?
AND e.deleted_at IS NULL AND e.deleted_at IS NULL
AND e.expire_at > ? -- 只返回未过期的 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 ORDER BY e.start_time DESC
LIMIT ? OFFSET ?; LIMIT ? OFFSET ?;
@ -437,4 +774,8 @@ backend/
1. **只显示展出中的作品** — 通过 `expire_at > now` 过滤 1. **只显示展出中的作品** — 通过 `expire_at > now` 过滤
2. **排序方式** — 按展出时间倒序start_time DESC 2. **排序方式** — 按展出时间倒序start_time DESC
3. **分页大小** — 默认 20最大 100 3. **分页大小** — 默认 20最大 100
4. **点赞作品也只显示展出中且未过期的** — 通过 JOIN exhibitions 并过滤 `expire_at > now`
5. **今日/本周点赞暂不实现** — API 和 Proto 已定义,但代码实现待后续
6. **每个藏品返回当前可领取收益** — 关联 exhibition_revenue_records 表,汇总 `status='claimable'``crystal_amount`
7. **他人点赞/展出的作品列表暂不实现** — API 和 Proto 已定义,但代码实现待后续