docs: 修改文档

This commit is contained in:
zerosaturation 2026-04-28 16:53:17 +08:00
parent 6b26ef26db
commit ad773ffc27

View File

@ -1,6 +1,6 @@
# 灵感瀑布(横向瀑布流藏品展示)设计文档 # 灵感瀑布Inspiration Flow)设计文档
> **创建日期:** 2026-04-27 > **创建日期:** 2026-04-28
> **项目:** TopFans 横向瀑布流藏品展示 > **项目:** TopFans 横向瀑布流藏品展示
> **服务:** galleryService (Go Dubbo-go) > **服务:** galleryService (Go Dubbo-go)
> **状态:** 设计中 > **状态:** 设计中
@ -9,7 +9,9 @@
## 一、设计目标 ## 一、设计目标
横向瀑布流展示该 star_id 下所有用户展出的藏品,支持随机展示、分页、按类型过滤。 横向瀑布流展示该 star_id 下所有用户展出的藏品,支持随机展示、无限滚动加载、按类型过滤。
**无限滚动实现方式:** 使用游标分页Cursor-based Pagination前端滚动到底部时携带 `cursor` 参数加载下一批数据。
--- ---
@ -17,20 +19,20 @@
**主表:** Exhibition展品展示表 **主表:** Exhibition展品展示表
**关联表:** Asset资产表- 用于按 material_type 过滤 **关联表:**
- Asset资产表- 用于获取藏品名称、封面、点赞数
- FanProfile粉丝档案表- 用于获取展出者昵称
**筛选条件:** **筛选条件:**
- `occupier_star_id = ?` (当前用户 star_id) - `occupier_star_id = ?` (当前用户 star_id)
- `expire_at > now` (未过期) - `expire_at > now` (未过期)
- `deleted_at IS NULL` (未删除) - `deleted_at IS NULL` (未删除)
**按 type 过滤:** JOIN Asset 表WHERE assets.material_type = ?
--- ---
## 三、API 设计 ## 三、API 设计
### 3.1 获取灵感瀑布(横向瀑布流)藏品列表 ### 3.1 获取灵感瀑布藏品列表
``` ```
GET /api/v1/inspiration-flow GET /api/v1/inspiration-flow
@ -40,8 +42,8 @@ GET /api/v1/inspiration-flow
| 参数 | 类型 | 必填 | 默认值 | 说明 | | 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------| |------|------|------|--------|------|
| page | int | 否 | 1 | 页码 | | cursor | string | 否 | 空 | 游标(首次请求为空,加载更多时传上次返回的 cursor |
| page_size | int | 否 | 20 | 每页数量最大100 | | limit | int | 否 | 20 | 每页数量(最大 50 |
| type | string | 否 | all | 过滤类型badge/poster/original/all | | type | string | 否 | all | 过滤类型badge/poster/original/all |
**HTTP 响应:** **HTTP 响应:**
@ -60,14 +62,20 @@ GET /api/v1/inspiration-flow
"owner_nickname": "粉丝昵称" "owner_nickname": "粉丝昵称"
} }
], ],
"page": 1, "cursor": "eyJsaW1pdCI6MjAsIm9mZnNldCI6MjB9",
"page_size": 20,
"total": 100,
"has_more": true "has_more": true
} }
} }
``` ```
**响应字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| items | array | 藏品列表 |
| cursor | string | 下次请求的游标base64 编码的 JSON |
| has_more | bool | 是否还有更多数据 |
**错误码:** **错误码:**
| code | 说明 | | code | 说明 |
@ -85,9 +93,9 @@ GET /api/v1/inspiration-flow
```protobuf ```protobuf
// 获取灵感瀑布藏品列表请求 // 获取灵感瀑布藏品列表请求
message GetInspirationFlowRequest { message GetInspirationFlowRequest {
int32 page = 1; // 页码默认1 string cursor = 1; // 游标(首次请求为空
int32 page_size = 2; // 每页数量默认20最大100 int32 limit = 2; // 每页数量默认20最大50
string type = 3; // 过滤类型badge/poster/original/all默认all string type = 3; // 过滤类型badge/poster/original/all默认all
} }
// 获取灵感瀑布藏品列表响应 // 获取灵感瀑布藏品列表响应
@ -99,16 +107,14 @@ message GetInspirationFlowResponse {
// 灵感瀑布数据 // 灵感瀑布数据
message InspirationFlowData { message InspirationFlowData {
repeated InspirationFlowItem items = 1; // 藏品列表 repeated InspirationFlowItem items = 1; // 藏品列表
int32 page = 2; // 当前页码 string cursor = 2; // 下次请求的游标
int32 page_size = 3; // 每页数量 bool has_more = 3; // 是否有更多
int64 total = 4; // 总数量
bool has_more = 5; // 是否有更多
} }
// 灵感瀑布藏品项 // 灵感瀑布藏品项
message InspirationFlowItem { message InspirationFlowItem {
int64 asset_id = 1; // 资产ID int64 asset_id = 1; // 资产ID
string name = 2; // 藏品名称 string name = 2; // 藏品名称
string cover_url = 3; // 封面图URL string cover_url = 3; // 封面图URL
int32 like_count = 4; // 点赞数 int32 like_count = 4; // 点赞数
string owner_nickname = 5; // 展出者昵称 string owner_nickname = 5; // 展出者昵称
@ -137,7 +143,22 @@ service GalleryService {
## 五、核心逻辑 ## 五、核心逻辑
### 5.1 查询逻辑 ### 5.1 游标分页设计
**游标结构JSONbase64 编码):**
```json
{
"offset": 20,
"limit": 20
}
```
**为什么用游标分页而非 offset 分页:**
1. **性能稳定**offset 翻页越深性能越差,游标分页性能恒定
2. **无限滚动友好**:用户滚动过程中数据可能变化,游标避免重复/遗漏
3. **适合随机排序**:配合 RANDOM() 避免翻页时数据错位
### 5.2 查询逻辑
```sql ```sql
SELECT SELECT
@ -156,35 +177,30 @@ WHERE e.occupier_star_id = ?
AND a.is_active = true AND a.is_active = true
AND (? = 'all' OR a.material_type = ?) AND (? = 'all' OR a.material_type = ?)
ORDER BY RANDOM() ORDER BY RANDOM()
LIMIT ? OFFSET ?; LIMIT ?;
``` ```
**参数说明:** **参数说明:**
- `? = star_id` (当前用户 star_id) - `? = star_id` (当前用户 star_id)
- `? = now` (当前时间戳) - `? = now` (当前时间戳)
- `? = type` (过滤类型) - `? = type` (过滤类型)
- `? = page_size` - `? = limit` (每页数量)
- `? = (page - 1) * page_size`
### 5.2 6小时自动下架 ### 5.3 游标编解码
已实现的 `GetExpiredExhibitions` 方法查询已过期的展览记录,调度器定期清理。 **编码(服务端):**
```go
cursor := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf(`{"offset":%d,"limit":%d}`, offset, limit)))
```
**下架时执行:** **解码(服务端):**
1. 设置 `deleted_at = now` ```go
2. 调用 `ClearAssetLikeRecords` RPC 重置点赞数为 0 decoded, _ := base64.StdEncoding.DecodeString(cursor)
3. 更新 `asset_registry.display_status = 0` var cursorData map[string]int
json.Unmarshal(decoded, &cursorData)
### 5.3 重新上架逻辑 offset := cursorData["offset"]
limit := cursorData["limit"]
当藏品重新展示时: ```
1. 创建新的 Exhibition 记录
2. 更新 `asset_registry.display_status = 1`
3. 点赞数从 0 开始(因为之前下架时已清除点赞记录)
### 5.4 随机展示
使用 `ORDER BY RANDOM()` 实现随机排序。
--- ---
@ -219,94 +235,103 @@ type Asset struct {
} }
``` ```
**新增字段(需数据库变更):**
```sql
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 | 展览时长(秒) | 144004小时可配置 | | inspiration_flow_limit | 默认每页数量 | 20 |
| inspiration_flow_page_size | 默认每页数量 | 20 | | inspiration_flow_max_limit | 最大每页数量 | 50 |
| inspiration_flow_max_page_size | 最大每页数量 | 100 |
--- ---
## 、项目文件结构 ## 、项目文件结构
``` ```
backend/ backend/
├── proto/ ├── proto/
│ └── gallery.proto # 修改:新增 GetInspirationFlow 方法 │ └── gallery.proto # 修改:新增 GetInspirationFlow 方法
├── pkg/proto/ ├── pkg/proto/
│ ├── gallery/ │ ├── gallery/
│ │ ├── gallery.pb.go # 重新生成 │ │ ├── gallery.pb.go # 重新生成
│ │ └── gallery.triple.go # 重新生成 │ │ └── gallery.triple.go # 重新生成
├── services/galleryService/ ├── services/galleryService/
│ ├── repository/ │ ├── repository/
│ │ └── gallery_repository.go # 修改:新增 GetActiveExhibitions 方法 │ │ └── gallery_repository.go # 修改:新增 GetInspirationFlow 方法
│ │ │ │
│ ├── service/ │ ├── service/
│ │ └── gallery_service.go # 修改:新增 GetInspirationFlow 方法 │ │ └── gallery_service.go # 修改:新增 GetInspirationFlow 方法
│ │ │ │
│ ├── provider/ │ ├── provider/
│ │ └── gallery_provider.go # 修改:新增 GetInspirationFlow Handler │ │ └── gallery_provider.go # 修改:新增 GetInspirationFlow Handler
│ │ │ │
│ └── config/ │ └── config/
│ └── gallery_config.go # 修改:新增灵感瀑布配置项 │ └── gallery_config.go # 修改:新增灵感瀑布配置项
├── scripts/
│ └── migrate_add_material_type.sql # 新增material_type 字段迁移脚本
└── gateway/ └── gateway/
├── controller/ ├── controller/
│ └── gallery_controller.go # 修改:新增 GetInspirationFlow 路由处理 │ └── gallery_controller.go # 修改:新增 GetInspirationFlow 路由处理
├── dto/
│ ├── gallery_dto.go # 修改:新增 InspirationFlow DTO
│ └── gallery_converter.go # 修改:新增转换函数
└── router/ └── router/
└── router.go # 修改:新增 /api/v1/inspiration-flow 路由 └── router.go # 修改:新增 /api/v1/inspiration-flow 路由
``` ```
--- ---
## 十、待确认事项 ## 九、数据库变更(必须执行)
1. **material_type 枚举值**badge/poster/original后续按需扩展 **注意:** 当前 `assets` 表**没有** `material_type` 字段,此变更**必须执行**后才能支持按类型过滤。
2. **随机排序策略**ORDER BY RANDOM() 在数据量大时可能有性能问题,后续可考虑按 like_count 随机采样
3. **展览时长**:默认 6 小时21600 秒),可通过配置修改
## 十一、数据库变更 ### 9.1 DDL
### 11.1 新增字段
```sql ```sql
-- assets 表新增 material_type 字段 -- assets 表新增 material_type 字段
ALTER TABLE assets ADD COLUMN material_type VARCHAR(50) DEFAULT 'original'; ALTER TABLE assets ADD COLUMN IF NOT EXISTS material_type VARCHAR(50) DEFAULT 'original';
-- 创建索引优化查询 -- 创建索引优化查询
CREATE INDEX IF NOT EXISTS idx_assets_material_type ON assets(material_type); CREATE INDEX IF NOT EXISTS idx_assets_material_type ON assets(material_type);
``` ```
### 11.2 迁移脚本 ### 9.2 迁移脚本
```sql ```sql
-- 迁移脚本backend/scripts/migrate_add_material_type.sql -- backend/scripts/migrate_add_material_type.sql
ALTER TABLE assets ADD COLUMN IF NOT EXISTS material_type VARCHAR(50) DEFAULT 'original';
CREATE INDEX IF NOT EXISTS idx_assets_material_type ON assets(material_type);
``` ```
---
## 十、无限滚动前端对接说明
### 10.1 首次请求
```
GET /api/v1/inspiration-flow?limit=20&type=all
```
### 10.2 加载更多
```
GET /api/v1/inspiration-flow?cursor=eyJsaW1pdCI6MjAsIm9mZnNldCI6MjB9&limit=20&type=all
```
### 10.3 前端逻辑
1. 首次请求 cursor 为空
2. 解析响应中的 cursor 和 has_more
3. 滚动到底部时,若 has_more=true携带 cursor 发起下一页请求
4. 数据变化时重置 cursor 重新加载
---
## 十一、待确认事项
1. **material_type 枚举值**badge/poster/original后续按需扩展
2. **随机排序策略**ORDER BY RANDOM() 在数据量大时可能有性能问题,后续可考虑按 like_count 随机采样
3. **每页数量上限**:当前设为 50是否合适
4. **是否需要缓存**:热门 star_id 的数据可以考虑 Redis 缓存