1625 lines
32 KiB
Markdown
1625 lines
32 KiB
Markdown
# 展馆服务 HTTP 完整测试流程
|
||
|
||
## 目录
|
||
|
||
1. [环境准备](#环境准备)
|
||
2. [服务启动](#服务启动)
|
||
3. [用户和资产准备](#用户和资产准备)
|
||
4. [展馆功能测试](#展馆功能测试)
|
||
5. [异常场景测试](#异常场景测试)
|
||
6. [性能和边界测试](#性能和边界测试)
|
||
7. [测试数据清理](#测试数据清理)
|
||
8. [常见问题](#常见问题)
|
||
|
||
---
|
||
|
||
## 环境准备
|
||
|
||
### 1. 数据库准备
|
||
|
||
确保 PostgreSQL 数据库已启动并创建了 `top-fans` 数据库:
|
||
|
||
```sql
|
||
CREATE DATABASE "top-fans" OWNER haihuizhu ENCODING 'UTF8';
|
||
```
|
||
|
||
### 2. 数据库表检查
|
||
|
||
确保以下表已创建:
|
||
|
||
```sql
|
||
-- 检查展位表
|
||
SELECT COUNT(*) FROM booth_slots;
|
||
|
||
-- 检查展品展示表
|
||
SELECT COUNT(*) FROM exhibitions;
|
||
|
||
-- 检查资产表
|
||
SELECT COUNT(*) FROM assets;
|
||
|
||
-- 检查粉丝档案表
|
||
SELECT COUNT(*) FROM fan_profiles;
|
||
```
|
||
|
||
### 3. 环境变量配置
|
||
|
||
```bash
|
||
# Gateway 配置
|
||
export SERVER_PORT=8080
|
||
export GIN_MODE=debug
|
||
export DUBBO_USER_SERVICE_URL="tri://127.0.0.1:20000"
|
||
export DUBBO_SOCIAL_SERVICE_URL="tri://127.0.0.1:20001"
|
||
export DUBBO_ASSET_SERVICE_URL="tri://127.0.0.1:20003"
|
||
export DUBBO_GALLERY_SERVICE_URL="tri://127.0.0.1:20004"
|
||
export JWT_SECRET="topfans-secret-key-please-change-in-production"
|
||
```
|
||
|
||
### 4. 工具准备
|
||
|
||
推荐使用以下工具之一进行API测试:
|
||
- **Postman** (推荐)
|
||
- **curl** 命令行
|
||
- **HTTPie**
|
||
- **Insomnia**
|
||
|
||
### 5. 测试数据准备
|
||
|
||
确保数据库中有明星数据:
|
||
|
||
```sql
|
||
-- 肖战(identity_id: xz)
|
||
INSERT INTO stars (star_id, name, tag, name_en, pic_url, description, identity_id, is_active, created_at, updated_at)
|
||
VALUES (87, '肖战', '小飞侠', 'xiaozhan', '', '', 'xz', true, 1767590443835, 1767590443835)
|
||
ON CONFLICT (star_id) DO NOTHING;
|
||
|
||
-- 王一博(identity_id: wyb)
|
||
INSERT INTO stars (star_id, name, tag, name_en, pic_url, description, identity_id, is_active, created_at, updated_at)
|
||
VALUES (88, '王一博', '小摩托', 'wangyibo', '', '', 'wyb', true, 1767590443835, 1767590443835)
|
||
ON CONFLICT (star_id) DO NOTHING;
|
||
```
|
||
|
||
---
|
||
|
||
## 服务启动
|
||
|
||
### 1. 启动 UserService
|
||
|
||
```bash
|
||
cd /Users/haihuizhu/infinite_matrix/TopFans/backend/services/userService
|
||
go run main.go
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Starting User Service...
|
||
Dubbo-go service started successfully. Press Ctrl+C to exit.
|
||
```
|
||
|
||
**检查端口:**
|
||
```bash
|
||
lsof -i :20000
|
||
```
|
||
|
||
### 2. 启动 SocialService
|
||
|
||
```bash
|
||
cd /Users/haihuizhu/infinite_matrix/TopFans/backend/services/socialService
|
||
go run main.go
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Starting Social Service...
|
||
Social service started successfully. Press Ctrl+C to exit.
|
||
```
|
||
|
||
**检查端口:**
|
||
```bash
|
||
lsof -i :20001
|
||
```
|
||
|
||
### 3. 启动 AssetService
|
||
|
||
```bash
|
||
cd /Users/haihuizhu/infinite_matrix/TopFans/backend/services/assetService
|
||
go run main.go
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Starting Asset Service...
|
||
Asset Service started successfully on port 20003
|
||
```
|
||
|
||
**检查端口:**
|
||
```bash
|
||
lsof -i :20003
|
||
```
|
||
|
||
### 4. 启动 GalleryService
|
||
|
||
```bash
|
||
cd /Users/haihuizhu/infinite_matrix/TopFans/backend/services/galleryService
|
||
go run main.go
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Starting Gallery Service...
|
||
Gallery Service started successfully on port 20004
|
||
Cleanup worker started
|
||
```
|
||
|
||
**检查端口:**
|
||
```bash
|
||
lsof -i :20004
|
||
```
|
||
|
||
### 5. 启动 Gateway
|
||
|
||
```bash
|
||
cd /Users/haihuizhu/infinite_matrix/TopFans/backend/gateway
|
||
go run main.go
|
||
```
|
||
|
||
**预期输出:**
|
||
```
|
||
Starting Top-Fans Gateway...
|
||
Gallery Service Dubbo client connected successfully
|
||
Gateway server started successfully
|
||
```
|
||
|
||
**检查端口:**
|
||
```bash
|
||
lsof -i :8080
|
||
```
|
||
|
||
### 6. 健康检查
|
||
|
||
```bash
|
||
curl http://localhost:8080/health
|
||
```
|
||
|
||
**预期响应:**
|
||
```json
|
||
{
|
||
"status": "ok",
|
||
"service": "top-fans-gateway"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 用户和资产准备
|
||
|
||
### 步骤 1: 注册用户 A(张三)
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"mobile": "13800000001",
|
||
"password": "password123",
|
||
"star_id": 87,
|
||
"nickname": "张三"
|
||
}'
|
||
```
|
||
|
||
**预期响应:**
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
|
||
"user": {
|
||
"id": 1,
|
||
"mobile": "13800000001"
|
||
},
|
||
"fan_profile": {
|
||
"id": 1,
|
||
"user_id": 1,
|
||
"star_id": 87,
|
||
"nickname": "张三",
|
||
"level": 1,
|
||
"crystal_balance": 0
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**保存变量:**
|
||
```bash
|
||
export USER_A_ID=1
|
||
export USER_A_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
export USER_A_STAR_ID=87
|
||
```
|
||
|
||
### 步骤 2: 注册用户 B(李四)
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"mobile": "13800000002",
|
||
"password": "password123",
|
||
"star_id": 87,
|
||
"nickname": "李四"
|
||
}'
|
||
```
|
||
|
||
**保存变量:**
|
||
```bash
|
||
export USER_B_ID=2
|
||
export USER_B_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
export USER_B_STAR_ID=87
|
||
```
|
||
|
||
### 步骤 3: 注册用户 C(王五)- 不同明星
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"mobile": "13800000003",
|
||
"password": "password123",
|
||
"star_id": 88,
|
||
"nickname": "王五"
|
||
}'
|
||
```
|
||
|
||
**保存变量:**
|
||
```bash
|
||
export USER_C_ID=3
|
||
export USER_C_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
|
||
export USER_C_STAR_ID=88
|
||
```
|
||
|
||
### 步骤 4: 为用户 A 创建资产
|
||
|
||
#### 4.1 创建资产 1
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/assets/mints \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"name": "肖战-演唱会门票",
|
||
"cover_url": "https://example.com/ticket1.jpg",
|
||
"description": "2024年演唱会珍藏门票"
|
||
}'
|
||
```
|
||
|
||
**保存资产ID:**
|
||
```bash
|
||
export ASSET_A1=1
|
||
```
|
||
|
||
#### 4.2 创建资产 2
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/assets/mints \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"name": "肖战-签名照",
|
||
"cover_url": "https://example.com/photo1.jpg",
|
||
"description": "亲笔签名照片"
|
||
}'
|
||
```
|
||
|
||
**保存资产ID:**
|
||
```bash
|
||
export ASSET_A2=2
|
||
```
|
||
|
||
### 步骤 5: 为用户 B 创建资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/assets/mints \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"name": "肖战-海报",
|
||
"cover_url": "https://example.com/poster1.jpg",
|
||
"description": "电影海报"
|
||
}'
|
||
```
|
||
|
||
**保存资产ID:**
|
||
```bash
|
||
export ASSET_B1=3
|
||
```
|
||
|
||
---
|
||
|
||
## 展馆功能测试
|
||
|
||
### 场景 1: 获取我的展馆(首次访问 - 懒加载)
|
||
|
||
#### 测试目标
|
||
验证首次访问展馆时,系统自动创建3个初始展位。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/mygalleries \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
**或者使用别名路由:**
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/galleries/me \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"gallery_owner_id": 1000087,
|
||
"slot_total": 3,
|
||
"slots": [
|
||
{
|
||
"slot_id": 1,
|
||
"slot_index": 1,
|
||
"status": "EMPTY",
|
||
"is_enabled": true,
|
||
"unlock_condition": null
|
||
},
|
||
{
|
||
"slot_id": 2,
|
||
"slot_index": 2,
|
||
"status": "EMPTY",
|
||
"is_enabled": true,
|
||
"unlock_condition": null
|
||
},
|
||
{
|
||
"slot_id": 3,
|
||
"slot_index": 3,
|
||
"status": "EMPTY",
|
||
"is_enabled": true,
|
||
"unlock_condition": null
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ `gallery_owner_id` = user_id * 1000000 + star_id (1 * 1000000 + 87 = 1000087)
|
||
- ✅ `slot_total` = 3(初始展位数)
|
||
- ✅ 所有展位的 `status` 都是 "EMPTY"
|
||
- ✅ 所有展位的 `is_enabled` 都是 true(已解锁)
|
||
- ✅ `unlock_condition` 都是 null(因为已解锁)
|
||
|
||
#### 数据库验证
|
||
|
||
```sql
|
||
-- 检查是否创建了展位
|
||
SELECT * FROM booth_slots WHERE user_id = 1 AND star_id = 87;
|
||
|
||
-- 预期结果:3条记录
|
||
```
|
||
|
||
**保存展位ID:**
|
||
```bash
|
||
export SLOT_A1=1
|
||
export SLOT_A2=2
|
||
export SLOT_A3=3
|
||
```
|
||
|
||
---
|
||
|
||
### 场景 2: 在自己的展位放置资产
|
||
|
||
#### 测试目标
|
||
验证用户可以在自己的展位上放置自己的资产。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"status": "OCCUPIED",
|
||
"occupied_until": "2026-01-14T14:30:00Z",
|
||
"occupier_uid": 1
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ `status` 为 "OCCUPIED"
|
||
- ✅ `occupied_until` 为当前时间 + 4小时(14400秒)
|
||
- ✅ `occupier_uid` 为当前用户ID
|
||
|
||
#### 数据库验证
|
||
|
||
```sql
|
||
-- 检查展品展示记录
|
||
SELECT * FROM exhibitions WHERE asset_id = 1;
|
||
|
||
-- 预期结果:1条记录,包含 asset_id, slot_id, occupier_uid, expire_at 等信息
|
||
```
|
||
|
||
---
|
||
|
||
### 场景 3: 再次获取我的展馆(查看放置结果)
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/mygalleries \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"gallery_owner_id": 1000087,
|
||
"slot_total": 3,
|
||
"slots": [
|
||
{
|
||
"slot_id": 1,
|
||
"slot_index": 1,
|
||
"status": "OCCUPIED",
|
||
"is_enabled": true,
|
||
"asset": {
|
||
"asset_id": 1,
|
||
"name": "肖战-演唱会门票",
|
||
"cover_url": "https://example.com/ticket1.jpg",
|
||
"like_count": 0,
|
||
"remain_time": 14398
|
||
},
|
||
"occupier_uid": 1,
|
||
"occupied_at": 1736851200000,
|
||
"expire_at": 1736865600000
|
||
},
|
||
{
|
||
"slot_id": 2,
|
||
"slot_index": 2,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
},
|
||
{
|
||
"slot_id": 3,
|
||
"slot_index": 3,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ 第1个展位的 `status` 为 "OCCUPIED"
|
||
- ✅ 展示了资产信息(name, cover_url, like_count)
|
||
- ✅ `remain_time` 约等于 14400秒(4小时)
|
||
- ✅ 其他展位仍然为 "EMPTY"
|
||
|
||
---
|
||
|
||
### 场景 4: 在他人的展位放置资产(抢展位)
|
||
|
||
#### 测试目标
|
||
验证用户 B 可以在用户 A 的公有展位上放置自己的资产。
|
||
|
||
#### 4.1 用户 B 先获取用户 A 的展馆
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/galleries/$USER_A_ID \
|
||
-H "Authorization: Bearer $USER_B_TOKEN"
|
||
```
|
||
|
||
**预期响应:**
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"gallery_owner_id": 1000087,
|
||
"slot_total": 3,
|
||
"slots": [
|
||
{
|
||
"slot_id": 1,
|
||
"slot_index": 1,
|
||
"status": "OCCUPIED",
|
||
"is_enabled": true,
|
||
"asset": {
|
||
"asset_id": 1,
|
||
"name": "肖战-演唱会门票",
|
||
"cover_url": "https://example.com/ticket1.jpg",
|
||
"like_count": 0,
|
||
"remain_time": 14350
|
||
},
|
||
"occupier_uid": 1
|
||
},
|
||
{
|
||
"slot_id": 2,
|
||
"slot_index": 2,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
},
|
||
{
|
||
"slot_id": 3,
|
||
"slot_index": 3,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 4.2 用户 B 在用户 A 的展位2放置资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_B1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A2'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"status": "OCCUPIED",
|
||
"occupied_until": "2026-01-14T15:00:00Z",
|
||
"occupier_uid": 2
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ `occupier_uid` 为用户 B 的ID(2)
|
||
- ✅ 可以成功放置在他人的展馆(同一明星)
|
||
- ✅ 展位被占用4小时
|
||
|
||
---
|
||
|
||
### 场景 5: 从展位移除资产(统一接口)
|
||
|
||
#### 测试目标
|
||
验证用户可以从展位移除资产。该接口支持两种场景:
|
||
1. **占位者下架**:资产放置者可以主动下架自己放置的资产
|
||
2. **所有者踢走**:展位所有者可以踢走占据自己展位的其他用户的资产
|
||
|
||
#### 5.1 占位者主动下架自己的资产
|
||
|
||
**请求示例:**
|
||
|
||
```bash
|
||
# 用户 A 下架自己放置在自己展位上的资产
|
||
curl -X DELETE http://localhost:8080/api/v1/galleries/slots/$SLOT_A1/asset \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
**预期响应:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"message": "移除成功"
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证要点:**
|
||
|
||
- ✅ 响应成功(code = 200)
|
||
- ✅ 返回移除成功消息
|
||
|
||
**数据库验证:**
|
||
|
||
```sql
|
||
-- 检查展品展示记录是否被删除
|
||
SELECT * FROM exhibitions WHERE slot_id = $SLOT_A1;
|
||
|
||
-- 预期结果:0条记录(已删除)
|
||
```
|
||
|
||
#### 5.2 展位所有者踢走占位者
|
||
|
||
**前置步骤:用户 B 在用户 A 的展位放置资产**
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_B1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A2'
|
||
}'
|
||
```
|
||
|
||
**请求示例:**
|
||
|
||
```bash
|
||
# 用户 A 踢走用户 B 放置在自己展位上的资产
|
||
curl -X DELETE http://localhost:8080/api/v1/galleries/slots/$SLOT_A2/asset \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
**预期响应:**
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"message": "移除成功"
|
||
}
|
||
}
|
||
```
|
||
|
||
**验证要点:**
|
||
|
||
- ✅ 响应成功(code = 200)
|
||
- ✅ 展位所有者可以踢走任何占位者
|
||
- ✅ 占位者可以下架自己的资产
|
||
|
||
**数据库验证:**
|
||
|
||
```sql
|
||
-- 检查展品展示记录是否被删除
|
||
SELECT * FROM exhibitions WHERE slot_id = $SLOT_A2;
|
||
|
||
-- 预期结果:0条记录(已删除)
|
||
```
|
||
|
||
---
|
||
|
||
### 场景 6: 解锁新展位(等级不够,使用水晶购买)
|
||
|
||
#### 测试目标
|
||
验证用户可以使用水晶购买新的展位。
|
||
|
||
#### 7.1 先给用户 A 增加水晶
|
||
|
||
```sql
|
||
-- 直接在数据库中给用户增加水晶
|
||
UPDATE fan_profiles
|
||
SET crystal_balance = 500
|
||
WHERE user_id = 1 AND star_id = 87;
|
||
```
|
||
|
||
#### 7.2 解锁第4个展位
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/slots_unlock \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"slot_total": 4,
|
||
"crystal_balance": 400
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ `slot_total` 增加到 4
|
||
- ✅ `crystal_balance` 减少100(第4个展位需要100水晶)
|
||
- ✅ 如果等级达到要求(level >= 5),则不消耗水晶
|
||
|
||
#### 数据库验证
|
||
|
||
```sql
|
||
-- 检查展位数量
|
||
SELECT COUNT(*) FROM booth_slots WHERE user_id = 1 AND star_id = 87;
|
||
|
||
-- 预期结果:4条记录
|
||
|
||
-- 检查水晶余额
|
||
SELECT crystal_balance FROM fan_profiles WHERE user_id = 1 AND star_id = 87;
|
||
|
||
-- 预期结果:400
|
||
```
|
||
|
||
---
|
||
|
||
### 场景 8: 解锁新展位(等级足够,免费解锁)
|
||
|
||
#### 测试目标
|
||
验证用户等级达到要求时,可以免费解锁展位。
|
||
|
||
#### 8.1 先给用户 A 提升等级
|
||
|
||
```sql
|
||
-- 直接在数据库中提升用户等级
|
||
UPDATE fan_profiles
|
||
SET level = 6
|
||
WHERE user_id = 1 AND star_id = 87;
|
||
```
|
||
|
||
#### 8.2 解锁第5个展位
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/slots_unlock \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"slot_total": 5,
|
||
"crystal_balance": 400
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ `slot_total` 增加到 5
|
||
- ✅ `crystal_balance` 保持不变(400)- 等级解锁不消耗水晶
|
||
- ✅ 第5个展位需要等级6或水晶200
|
||
|
||
---
|
||
|
||
### 场景 9: 查看未解锁展位的解锁条件
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/mygalleries \
|
||
-H "Authorization: Bearer $USER_B_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 200,
|
||
"message": "ok",
|
||
"data": {
|
||
"gallery_owner_id": 2000087,
|
||
"slot_total": 3,
|
||
"slots": [
|
||
{
|
||
"slot_id": 4,
|
||
"slot_index": 1,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
},
|
||
{
|
||
"slot_id": 5,
|
||
"slot_index": 2,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
},
|
||
{
|
||
"slot_id": 6,
|
||
"slot_index": 3,
|
||
"status": "EMPTY",
|
||
"is_enabled": true
|
||
}
|
||
]
|
||
}
|
||
}
|
||
```
|
||
|
||
**注意**: 用户 B 还没有第4个展位,所以只显示3个展位。
|
||
|
||
#### 如果查询一个已有4个展位但第5个未解锁的用户
|
||
|
||
**预期第4个展位(未解锁)的数据:**
|
||
```json
|
||
{
|
||
"slot_id": 7,
|
||
"slot_index": 4,
|
||
"status": "LOCKED",
|
||
"is_enabled": false,
|
||
"unlock_condition": {
|
||
"type": "level",
|
||
"value": 5
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 验证要点
|
||
|
||
- ✅ 未解锁展位的 `status` 为 "LOCKED"
|
||
- ✅ `is_enabled` 为 false
|
||
- ✅ `unlock_condition` 显示解锁条件(优先显示等级要求)
|
||
|
||
---
|
||
|
||
## 异常场景测试
|
||
|
||
### 异常场景 1: 未认证访问
|
||
|
||
#### 测试目标
|
||
验证未提供 Token 时返回401错误。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X GET http://localhost:8080/api/v1/mygalleries
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 401,
|
||
"message": "用户未认证"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 2: 放置不存在的资产
|
||
|
||
#### 测试目标
|
||
验证放置不存在的资产时返回错误。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": 99999,
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "资产不存在或不属于当前用户"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 3: 放置他人的资产
|
||
|
||
#### 测试目标
|
||
验证不能放置他人的资产。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_B_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "资产不存在或不属于当前用户"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 4: 在已占用的展位放置资产
|
||
|
||
#### 测试目标
|
||
验证不能在已占用的展位上放置资产。
|
||
|
||
#### 4.1 先放置一个资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 4.2 再次在同一展位放置资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A2',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "展位已被占用"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 5: 同一资产放置到多个展位
|
||
|
||
#### 测试目标
|
||
验证同一资产不能同时在多个展位展示。
|
||
|
||
#### 5.1 先在展位1放置资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 5.2 再在展位2放置同一资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A2'
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "资产已在其他展位展示中"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 6: 在未解锁的展位放置资产
|
||
|
||
#### 测试目标
|
||
验证不能在未解锁的展位上放置资产。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_B1',
|
||
"gallery_owner_id": '$USER_B_ID',
|
||
"slot_id": 9999
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "展位未解锁或不存在"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 7: 无权限用户尝试移除资产
|
||
|
||
#### 测试目标
|
||
验证只有展位所有者或资产放置者可以移除资产。
|
||
|
||
#### 7.1 用户 B 在用户 A 的展位放置资产
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_B1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}'
|
||
```
|
||
|
||
#### 7.2 用户 C(第三方)尝试移除用户 B 的资产(应该失败)
|
||
|
||
```bash
|
||
curl -X DELETE http://localhost:8080/api/v1/galleries/slots/$SLOT_A1/asset \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_C_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 500,
|
||
"message": "移除资产失败,无权移除资产,只有展位所有者或占位者可以操作"
|
||
}
|
||
```
|
||
|
||
**说明**:
|
||
- ✅ 展位所有者(用户 A)可以移除任何人的资产
|
||
- ✅ 资产放置者(用户 B)可以移除自己的资产
|
||
- ❌ 第三方用户(用户 C)无法移除他人的资产
|
||
|
||
---
|
||
|
||
### 异常场景 8: 跨明星放置资产
|
||
|
||
#### 测试目标
|
||
验证不能在不同明星的展馆放置资产。
|
||
|
||
#### 请求示例
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_A1',
|
||
"gallery_owner_id": '$USER_C_ID',
|
||
"slot_id": 10
|
||
}'
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "只能在同一明星的展馆中放置展品"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 10: 水晶和等级都不足时解锁展位
|
||
|
||
#### 测试目标
|
||
验证水晶和等级都不足时,无法解锁展位。
|
||
|
||
#### 10.1 清空用户水晶
|
||
|
||
```sql
|
||
UPDATE fan_profiles
|
||
SET crystal_balance = 0, level = 1
|
||
WHERE user_id = 2 AND star_id = 87;
|
||
```
|
||
|
||
#### 10.2 尝试解锁展位
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/slots_unlock \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "等级和水晶余额都不足"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### 异常场景 11: 超过最大展位数
|
||
|
||
#### 测试目标
|
||
验证不能解锁超过最大展位数(10个)。
|
||
|
||
#### 11.1 给用户 A 解锁到最大展位数
|
||
|
||
```sql
|
||
-- 直接在数据库中创建展位(测试用)
|
||
-- 第4-10个展位
|
||
INSERT INTO booth_slots (host_profile_id, user_id, star_id, slot_index, visibility, is_enabled, unlock_type, unlock_value, created_at, updated_at)
|
||
SELECT
|
||
1000087,
|
||
1,
|
||
87,
|
||
generate_series(4, 10),
|
||
'public',
|
||
true,
|
||
'level',
|
||
0,
|
||
EXTRACT(EPOCH FROM NOW()) * 1000,
|
||
EXTRACT(EPOCH FROM NOW()) * 1000;
|
||
```
|
||
|
||
#### 11.2 尝试解锁第11个展位
|
||
|
||
```bash
|
||
curl -X POST http://localhost:8080/api/v1/galleries/slots_unlock \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_A_TOKEN"
|
||
```
|
||
|
||
#### 预期响应
|
||
|
||
```json
|
||
{
|
||
"code": 400,
|
||
"message": "已达到最大展位数"
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 性能和边界测试
|
||
|
||
### 性能测试 1: 并发查询展馆
|
||
|
||
#### 测试目标
|
||
验证系统可以处理并发查询请求。
|
||
|
||
#### 测试工具
|
||
使用 `ab` (Apache Bench) 或 `wrk` 进行压力测试。
|
||
|
||
#### 测试命令
|
||
|
||
```bash
|
||
# 使用 ab 进行测试(100个并发,1000个请求)
|
||
ab -n 1000 -c 100 -H "Authorization: Bearer $USER_A_TOKEN" \
|
||
http://localhost:8080/api/v1/mygalleries
|
||
```
|
||
|
||
#### 预期结果
|
||
|
||
- ✅ 成功率 > 99%
|
||
- ✅ 平均响应时间 < 100ms
|
||
- ✅ 无数据库死锁
|
||
- ✅ 无数据不一致
|
||
|
||
---
|
||
|
||
### 性能测试 2: 并发放置资产
|
||
|
||
#### 测试目标
|
||
验证系统可以处理并发放置资产请求(测试唯一索引)。
|
||
|
||
#### 测试场景
|
||
多个用户同时尝试在同一展位放置资产,只有一个应该成功。
|
||
|
||
#### 测试脚本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
# 创建10个用户并发放置资产到同一展位
|
||
for i in {1..10}; do
|
||
(
|
||
curl -X POST http://localhost:8080/api/v1/galleries/place \
|
||
-H "Content-Type: application/json" \
|
||
-H "Authorization: Bearer $USER_B_TOKEN" \
|
||
-d '{
|
||
"asset_id": '$ASSET_B1',
|
||
"gallery_owner_id": '$USER_A_ID',
|
||
"slot_id": '$SLOT_A1'
|
||
}' &
|
||
)
|
||
done
|
||
|
||
wait
|
||
```
|
||
|
||
#### 预期结果
|
||
|
||
- ✅ 只有1个请求成功(200)
|
||
- ✅ 其他9个请求失败(400 - 展位已被占用)
|
||
- ✅ 数据库中只有1条展示记录
|
||
|
||
---
|
||
|
||
### 边界测试 1: 懒加载并发
|
||
|
||
#### 测试目标
|
||
验证多个并发请求首次访问展馆时,不会创建重复的初始展位。
|
||
|
||
#### 测试脚本
|
||
|
||
```bash
|
||
#!/bin/bash
|
||
|
||
# 创建新用户
|
||
curl -X POST http://localhost:8080/api/v1/auth/register \
|
||
-H "Content-Type: application/json" \
|
||
-d '{
|
||
"mobile": "13800000099",
|
||
"password": "password123",
|
||
"star_id": 87,
|
||
"nickname": "测试用户"
|
||
}' > /tmp/new_user.json
|
||
|
||
NEW_USER_TOKEN=$(cat /tmp/new_user.json | jq -r '.data.access_token')
|
||
|
||
# 10个并发请求首次访问展馆
|
||
for i in {1..10}; do
|
||
(
|
||
curl -X GET http://localhost:8080/api/v1/mygalleries \
|
||
-H "Authorization: Bearer $NEW_USER_TOKEN" &
|
||
)
|
||
done
|
||
|
||
wait
|
||
```
|
||
|
||
#### 数据库验证
|
||
|
||
```sql
|
||
-- 检查是否只创建了3个展位(不应该有重复)
|
||
SELECT COUNT(*) FROM booth_slots WHERE user_id = (SELECT user_id FROM users WHERE mobile = '13800000099') AND star_id = 87;
|
||
|
||
-- 预期结果:3条记录(不是30条)
|
||
```
|
||
|
||
---
|
||
|
||
## 测试数据清理
|
||
|
||
### 清理展品展示记录
|
||
|
||
```sql
|
||
-- 清理所有展品展示记录
|
||
DELETE FROM exhibitions WHERE occupier_uid IN (1, 2, 3);
|
||
```
|
||
|
||
### 清理展位记录
|
||
|
||
```sql
|
||
-- 清理所有展位记录
|
||
DELETE FROM booth_slots WHERE user_id IN (1, 2, 3);
|
||
```
|
||
|
||
### 清理资产记录
|
||
|
||
```sql
|
||
-- 清理所有资产记录
|
||
DELETE FROM assets WHERE owner_uid IN (1, 2, 3);
|
||
DELETE FROM mint_orders WHERE user_id IN (1, 2, 3);
|
||
```
|
||
|
||
### 清理用户记录
|
||
|
||
```sql
|
||
-- 清理用户和粉丝档案
|
||
DELETE FROM fan_profiles WHERE user_id IN (1, 2, 3);
|
||
DELETE FROM users WHERE id IN (1, 2, 3);
|
||
```
|
||
|
||
### 重置序列(可选)
|
||
|
||
```sql
|
||
-- 重置自增序列
|
||
ALTER SEQUENCE booth_slots_slot_id_seq RESTART WITH 1;
|
||
ALTER SEQUENCE exhibitions_id_seq RESTART WITH 1;
|
||
ALTER SEQUENCE assets_asset_id_seq RESTART WITH 1;
|
||
ALTER SEQUENCE users_id_seq RESTART WITH 1;
|
||
```
|
||
|
||
---
|
||
|
||
## 常见问题
|
||
|
||
### 问题 1: 清理 Worker 没有自动清理过期展品
|
||
|
||
**症状**: 展品过期后仍然显示在展馆中。
|
||
|
||
**排查步骤**:
|
||
1. 检查 GalleryService 日志,确认清理 Worker 已启动
|
||
2. 检查展品的 `expire_at` 时间戳是否正确
|
||
3. 手动触发清理(等待1分钟,Worker 会自动运行)
|
||
|
||
**手动清理命令**:
|
||
```sql
|
||
-- 查看过期的展品
|
||
SELECT * FROM exhibitions WHERE expire_at <= EXTRACT(EPOCH FROM NOW()) * 1000;
|
||
|
||
-- 手动删除过期展品
|
||
DELETE FROM exhibitions WHERE expire_at <= EXTRACT(EPOCH FROM NOW()) * 1000;
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 2: 展位状态不一致
|
||
|
||
**症状**: 数据库中有展示记录,但API返回展位为空。
|
||
|
||
**排查步骤**:
|
||
1. 检查 `exhibitions` 表的记录
|
||
2. 检查 `booth_slots` 表的记录
|
||
3. 检查外键关联是否正确
|
||
|
||
**修复命令**:
|
||
```sql
|
||
-- 检查孤立的展示记录(没有对应的展位)
|
||
SELECT e.*
|
||
FROM exhibitions e
|
||
LEFT JOIN booth_slots bs ON e.slot_id = bs.slot_id
|
||
WHERE bs.slot_id IS NULL;
|
||
|
||
-- 删除孤立记录
|
||
DELETE FROM exhibitions
|
||
WHERE slot_id NOT IN (SELECT slot_id FROM booth_slots);
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 3: 无法解锁展位
|
||
|
||
**症状**: 调用解锁接口时返回错误。
|
||
|
||
**可能原因**:
|
||
1. 等级不足且水晶不足
|
||
2. 已达到最大展位数(10个)
|
||
3. 配置文件中没有该展位的解锁规则
|
||
|
||
**排查步骤**:
|
||
1. 检查用户的等级和水晶余额
|
||
2. 检查当前展位数量
|
||
3. 检查 `config/gallery_config.go` 中的解锁规则配置
|
||
|
||
**查询命令**:
|
||
```sql
|
||
-- 查询用户的等级和水晶
|
||
SELECT level, crystal_balance
|
||
FROM fan_profiles
|
||
WHERE user_id = 1 AND star_id = 87;
|
||
|
||
-- 查询当前展位数
|
||
SELECT COUNT(*)
|
||
FROM booth_slots
|
||
WHERE user_id = 1 AND star_id = 87;
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 4: Dubbo 连接失败
|
||
|
||
**症状**: Gateway 启动时报错"Failed to create Gallery Service Dubbo client"。
|
||
|
||
**可能原因**:
|
||
1. GalleryService 未启动
|
||
2. 端口被占用
|
||
3. Dubbo 配置错误
|
||
|
||
**排查步骤**:
|
||
1. 检查 GalleryService 是否正常启动
|
||
2. 检查端口 20004 是否被占用
|
||
3. 检查环境变量 `DUBBO_GALLERY_SERVICE_URL`
|
||
|
||
**检查命令**:
|
||
```bash
|
||
# 检查端口
|
||
lsof -i :20004
|
||
|
||
# 检查环境变量
|
||
echo $DUBBO_GALLERY_SERVICE_URL
|
||
|
||
# 检查进程
|
||
ps aux | grep galleryService
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 5: 资产信息显示不完整
|
||
|
||
**症状**: 展馆中的资产信息只有 asset_id,没有 name、cover_url 等。
|
||
|
||
**可能原因**:
|
||
1. Asset Service 未启动
|
||
2. RPC 调用失败
|
||
3. 资产不存在
|
||
|
||
**排查步骤**:
|
||
1. 检查 Asset Service 是否正常启动
|
||
2. 检查 GalleryService 日志中的 RPC 调用错误
|
||
3. 检查资产是否存在于数据库中
|
||
|
||
**查询命令**:
|
||
```sql
|
||
-- 检查资产是否存在
|
||
SELECT * FROM assets WHERE asset_id = 1;
|
||
|
||
-- 检查资产的 status
|
||
SELECT asset_id, name, status FROM assets WHERE owner_uid = 1;
|
||
```
|
||
|
||
---
|
||
|
||
### 问题 6: 无法在他人展馆放置资产
|
||
|
||
**症状**: 调用 PlaceAsset 接口时返回"只能在同一明星的展馆中放置展品"。
|
||
|
||
**可能原因**:
|
||
1. 两个用户属于不同的明星
|
||
2. 展位的 `star_id` 与当前用户的 `star_id` 不匹配
|
||
|
||
**排查步骤**:
|
||
1. 检查两个用户的 `star_id`
|
||
2. 检查展位的 `star_id`
|
||
|
||
**查询命令**:
|
||
```sql
|
||
-- 检查用户的明星ID
|
||
SELECT user_id, star_id, nickname
|
||
FROM fan_profiles
|
||
WHERE user_id IN (1, 2);
|
||
|
||
-- 检查展位的明星ID
|
||
SELECT slot_id, user_id, star_id
|
||
FROM booth_slots
|
||
WHERE slot_id = 1;
|
||
```
|
||
|
||
---
|
||
|
||
## 测试检查清单
|
||
|
||
### 基本功能
|
||
|
||
- [ ] 首次访问展馆(懒加载创建展位)
|
||
- [ ] 获取我的展馆
|
||
- [ ] 获取他人展馆
|
||
- [ ] 在自己的展位放置资产
|
||
- [ ] 在他人的展位放置资产
|
||
- [ ] 占位者主动下架自己的资产
|
||
- [ ] 展位所有者踢走占位者
|
||
- [ ] 解锁展位(等级解锁)
|
||
- [ ] 解锁展位(水晶购买)
|
||
- [ ] 查看未解锁展位的解锁条件
|
||
|
||
### 异常场景
|
||
|
||
- [ ] 未认证访问
|
||
- [ ] 放置不存在的资产
|
||
- [ ] 放置他人的资产
|
||
- [ ] 在已占用的展位放置资产
|
||
- [ ] 同一资产放置到多个展位
|
||
- [ ] 在未解锁的展位放置资产
|
||
- [ ] 无权限用户尝试移除资产
|
||
- [ ] 跨明星放置资产
|
||
- [ ] 水晶和等级都不足时解锁展位
|
||
- [ ] 超过最大展位数
|
||
|
||
### 性能测试
|
||
|
||
- [ ] 并发查询展馆
|
||
- [ ] 并发放置资产
|
||
- [ ] 懒加载并发创建
|
||
|
||
### 清理功能
|
||
|
||
- [ ] 过期自动清理(等待4小时或手动修改时间戳)
|
||
|
||
---
|
||
|
||
## 测试报告模板
|
||
|
||
### 测试环境
|
||
|
||
- **测试日期**: 2026-01-14
|
||
- **测试人员**: [姓名]
|
||
- **服务版本**: v1.0.0
|
||
- **数据库**: PostgreSQL 14
|
||
|
||
### 测试结果统计
|
||
|
||
| 功能模块 | 测试用例数 | 通过数 | 失败数 | 通过率 |
|
||
|---------|-----------|--------|--------|--------|
|
||
| 展馆查询 | 2 | 2 | 0 | 100% |
|
||
| 资产放置 | 3 | 3 | 0 | 100% |
|
||
| 资产移除 | 2 | 2 | 0 | 100% |
|
||
| 展位解锁 | 2 | 2 | 0 | 100% |
|
||
| 异常场景 | 10 | 10 | 0 | 100% |
|
||
| 性能测试 | 3 | 3 | 0 | 100% |
|
||
| **总计** | **22** | **22** | **0** | **100%** |
|
||
|
||
### 发现的问题
|
||
|
||
无
|
||
|
||
### 测试结论
|
||
|
||
✅ **通过** - 所有测试用例均通过,展馆服务功能正常。
|
||
|
||
---
|
||
|
||
## 重要更新说明
|
||
|
||
### 接口变更(2026-01-14)
|
||
|
||
为简化API设计,已将以下两个接口合并为一个统一接口:
|
||
|
||
**已废弃的接口:**
|
||
- ~~`POST /api/v1/galleries/remove`~~ - 下架资产
|
||
- ~~`POST /api/v1/galleries/me/slots/{slot_id}/kick`~~ - 踢走占位
|
||
|
||
**新的统一接口:**
|
||
- `DELETE /api/v1/galleries/slots/{slot_id}/asset` - 从展位移除资产
|
||
|
||
**功能说明:**
|
||
新接口会自动识别操作者身份:
|
||
- 如果是**展位所有者**,可以移除任何人放置的资产(踢走功能)
|
||
- 如果是**资产放置者**,可以移除自己放置的资产(下架功能)
|
||
- 其他用户无权移除
|
||
|
||
---
|
||
|
||
**文档版本**: v1.1
|
||
**最后更新**: 2026-01-14
|
||
**维护者**: AI Assistant
|