docs: 修改文档
This commit is contained in:
parent
6b26ef26db
commit
ad773ffc27
@ -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,8 +93,8 @@ GET /api/v1/inspiration-flow
|
||||
```protobuf
|
||||
// 获取灵感瀑布藏品列表请求
|
||||
message GetInspirationFlowRequest {
|
||||
int32 page = 1; // 页码(默认1)
|
||||
int32 page_size = 2; // 每页数量(默认20,最大100)
|
||||
string cursor = 1; // 游标(首次请求为空)
|
||||
int32 limit = 2; // 每页数量(默认20,最大50)
|
||||
string type = 3; // 过滤类型:badge/poster/original/all(默认all)
|
||||
}
|
||||
|
||||
@ -99,10 +107,8 @@ 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; // 是否有更多
|
||||
}
|
||||
|
||||
// 灵感瀑布藏品项
|
||||
@ -137,7 +143,22 @@ service GalleryService {
|
||||
|
||||
## 五、核心逻辑
|
||||
|
||||
### 5.1 查询逻辑
|
||||
### 5.1 游标分页设计
|
||||
|
||||
**游标结构(JSON,base64 编码):**
|
||||
```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,51 +235,32 @@ 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 | 展览时长(秒) | 14400(4小时,可配置) |
|
||||
| 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.triple.go # 重新生成
|
||||
│
|
||||
|
||||
├── services/galleryService/
|
||||
│ ├── repository/
|
||||
│ │ └── gallery_repository.go # 修改:新增 GetActiveExhibitions 方法
|
||||
│ │ └── gallery_repository.go # 修改:新增 GetInspirationFlow 方法
|
||||
│ │
|
||||
│ ├── service/
|
||||
│ │ └── gallery_service.go # 修改:新增 GetInspirationFlow 方法
|
||||
@ -273,40 +270,68 @@ backend/
|
||||
│ │
|
||||
│ └── config/
|
||||
│ └── gallery_config.go # 修改:新增灵感瀑布配置项
|
||||
│
|
||||
├── scripts/
|
||||
│ └── migrate_add_material_type.sql # 新增:material_type 字段迁移脚本
|
||||
│
|
||||
|
||||
└── gateway/
|
||||
├── controller/
|
||||
│ └── gallery_controller.go # 修改:新增 GetInspirationFlow 路由处理
|
||||
│
|
||||
├── dto/
|
||||
│ ├── gallery_dto.go # 修改:新增 InspirationFlow DTO
|
||||
│ └── gallery_converter.go # 修改:新增转换函数
|
||||
│
|
||||
└── router/
|
||||
└── 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 缓存
|
||||
|
||||
Loading…
Reference in New Issue
Block a user