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