topfans/backend/docs/展馆接口数据库字段说明.md
2026-04-07 22:29:48 +08:00

12 KiB
Raw Blame History

展馆接口数据库字段说明

接口说明

GET /api/v1/galleries/{target_uid} - 获取他人展馆

路径参数

  • target_uid: 目标用户ID数据库 users.id

功能:查看指定用户的展馆信息(展位列表、展品展示情况)


数据库表与字段映射

1. 主要数据表

booth_slots 表(展位表)

数据库字段 Go 模型字段 类型 说明
slot_id SlotID int64 展位ID主键自增
host_profile_id HostProfileID int64 展馆所有者ID计算值user_id * 1000000 + star_id
user_id UserID int64 用户ID外键关联 users.id
star_id StarID int64 明星ID用于数据隔离
slot_index SlotIndex int 展位序号1, 2, 3...,与 host_profile_id 联合唯一)
visibility Visibility string 可见性(默认 'public'
is_enabled IsEnabled bool 是否已解锁(false=未解锁/LOCKEDtrue=已解锁)
unlock_type UnlockType string 解锁类型('level''crystal'
unlock_value UnlockValue int 解锁条件值(等级或水晶数量)
created_at CreatedAt int64 创建时间(毫秒时间戳)
updated_at UpdatedAt int64 更新时间(毫秒时间戳)

查询逻辑

SELECT * FROM booth_slots 
WHERE user_id = ? AND star_id = ? 
ORDER BY slot_index ASC

exhibitions 表(展品展示表)

数据库字段 Go 模型字段 类型 说明
id ID int64 主键(自增)
asset_id AssetID int64 资产ID外键关联 assets.id,唯一索引)
slot_id SlotID int64 展位ID外键关联 booth_slots.slot_id
host_profile_id HostProfileID int64 展馆所有者IDbooth_slots.host_profile_id 一致)
occupier_uid OccupierUID int64 占位者用户ID实际放置藏品的用户
occupier_star_id OccupierStarID int64 占位者明星ID
start_time StartTime int64 占用开始时间(毫秒时间戳)
expire_at ExpireAt int64 占用过期时间(毫秒时间戳,有索引)
created_at CreatedAt int64 创建时间
updated_at UpdatedAt int64 更新时间

查询逻辑(判断展位是否被占用):

SELECT * FROM exhibitions 
WHERE slot_id = ? 
LIMIT 1

响应包字段说明

响应结构

{
  "code": 200,
  "message": "ok",
  "data": {
    "gallery_owner_id": 37000088,
    "slot_total": 3,
    "slots": [...]
  }
}

字段详解

  • 计算方式user_id * 1000000 + star_id
  • 示例37000088 = 37 * 1000000 + 88
    • 表示用户ID=37明星ID=88 的展馆
  • 用途
    • 用于标识展馆的唯一所有者(同一用户在不同明星下有不同的展馆)
    • 在放置藏品时,需要传入此 ID 作为 gallery_owner_id 参数

slot_total(展位总数)

  • 来源booth_slots 表中该用户在该明星下的展位数量
  • 说明:初始展位数量由配置决定(通常为 3 个)

slots[](展位列表)

每个展位对象包含:

字段 类型 说明 数据来源
slot_id int64 展位ID主键 booth_slots.slot_id
slot_index int32 展位序号1, 2, 3... booth_slots.slot_index
status string 展位状态 计算得出(见下方)
is_enabled bool 是否已解锁 booth_slots.is_enabled
asset object|null 展品信息(仅 status=OCCUPIED 时存在) exhibitions + assets 表关联查询
occupier_uid int64|null 占位者用户IDstatus=OCCUPIED 时存在) exhibitions.occupier_uid
occupied_at int64|null 占用开始时间(仅 status=OCCUPIED 时存在) exhibitions.start_time
expire_at int64|null 占用过期时间(仅 status=OCCUPIED 时存在) exhibitions.expire_at
unlock_condition object|null 解锁条件(仅 status=LOCKED 时存在) booth_slots.unlock_type + unlock_value 计算

status 状态计算逻辑

if !slot.IsEnabled {
    status = "LOCKED"  // 未解锁
} else {
    exhibition := GetExhibitionBySlot(slot.SlotID)
    if exhibition == nil {
        status = "EMPTY"  // 已解锁但空展位
    } else {
        status = "OCCUPIED"  // 已解锁且有展品
    }
}

状态值说明

  • LOCKED:展位未解锁(is_enabled=false),需要满足解锁条件才能使用
  • EMPTY:展位已解锁(is_enabled=true),但当前没有展品
  • OCCUPIED:展位已解锁且有展品展示(exhibitions 表中有对应记录)

asset 对象(展品信息)

status=OCCUPIED 时,asset 字段包含:

字段 类型 说明 数据来源
asset_id int64 资产ID exhibitions.asset_idassets.id
name string 资产名称 assets.name
cover_url string 封面图URL assets.cover_url
like_count int32 点赞数 assets.like_count
remain_time int64 剩余时间(秒) (exhibitions.expire_at - now) / 1000

注意asset 信息通过 RPC 调用 AssetService.GetAsset 获取,需要传入 occupier_uidoccupier_star_id(因为资产属于占位者,不是展馆所有者)。

unlock_condition 对象(解锁条件)

status=LOCKED 时,unlock_condition 字段包含:

字段 类型 说明 数据来源
type string 解锁类型:"level""crystal" booth_slots.unlock_type 或配置规则
value int32 解锁条件值(等级或水晶数量) booth_slots.unlock_value 或配置规则

/api/v1/galleries/me 的响应格式对比

响应格式是否一致?

答案:是的,响应格式完全一致。

代码证据

  1. DTO 定义gateway/dto/gallery_dto.go

    // GetMyGalleryResponseDTO 获取我的展馆响应
    type GetMyGalleryResponseDTO struct {
        GalleryOwnerID int64         `json:"gallery_owner_id"`
        SlotTotal      int32         `json:"slot_total"`
        Slots          []SlotInfoDTO `json:"slots"`
    }
    
    // GetUserGalleryResponseDTO 获取他人展馆响应(与我的展馆响应相同)
    type GetUserGalleryResponseDTO struct {
        GalleryOwnerID int64         `json:"gallery_owner_id"`
        SlotTotal      int32         `json:"slot_total"`
        Slots          []SlotInfoDTO `json:"slots"`
    }
    

    注释明确说明:GetUserGalleryResponseDTOGetMyGalleryResponseDTO 相同

  2. 转换函数gateway/dto/gallery_converter.go

    // ConvertGalleryData 转换展馆数据(我的展馆)
    func ConvertGalleryData(pbData *pbGallery.GalleryData) *GetMyGalleryResponseDTO {
        // ... 转换逻辑
    }
    
    // ConvertGalleryDataToUser 转换展馆数据(他人展馆)
    func ConvertGalleryDataToUser(pbData *pbGallery.GalleryData) *GetUserGalleryResponseDTO {
        // ... 转换逻辑(与 ConvertGalleryData 完全相同)
    }
    

    两个函数的转换逻辑完全一致

  3. Service 层返回services/galleryService/service/gallery_service.go

    // GetMyGallery 和 GetUserGallery 都返回 *pb.GalleryData
    // 结构完全相同,只是查询的目标用户不同
    

实际差异

虽然响应格式相同,但数据内容有区别:

对比项 /api/v1/galleries/me /api/v1/galleries/{target_uid}
查询目标 当前登录用户(user_id 从 JWT 获取) 指定用户(target_uid 从路径参数获取)
gallery_owner_id user_id * 1000000 + star_id(当前用户) target_uid * 1000000 + star_id(目标用户)
slots[] 数据 当前用户的展位 目标用户的展位
权限 可查看自己的所有展位(包括未解锁的) 只能查看目标用户已解锁的展位(is_enabled=true

注意:代码中 buildSlotInfosisOwner 参数用于区分是否显示解锁条件等细节,但响应结构本身是一致的。


使用示例

1. 查看他人展馆

# 查看用户ID=37的展馆
curl -X GET "http://localhost:8080/api/v1/galleries/37" \
  -H "Authorization: Bearer <JWT_TOKEN>"

响应示例

{
  "code": 200,
  "message": "ok",
  "data": {
    "gallery_owner_id": 37000088,  // 37 * 1000000 + 88
    "slot_total": 3,
    "slots": [
      {
        "slot_id": 46,
        "slot_index": 1,
        "status": "EMPTY",        // 已解锁但空展位
        "is_enabled": true,
        // asset, occupier_uid 等字段不存在(因为 status=EMPTY
      },
      {
        "slot_id": 47,
        "slot_index": 2,
        "status": "OCCUPIED",     // 有展品
        "is_enabled": true,
        "asset": {
          "asset_id": 123,
          "name": "数字艺术品",
          "cover_url": "https://...",
          "like_count": 10,
          "remain_time": 86400
        },
        "occupier_uid": 20,       // 占位者用户ID
        "occupied_at": 1705747200000,
        "expire_at": 1705833600000
      },
      {
        "slot_id": 48,
        "slot_index": 3,
        "status": "LOCKED",       // 未解锁
        "is_enabled": false,
        "unlock_condition": {
          "type": "crystal",
          "value": 100
        }
      }
    ]
  }
}

2. 前端使用建议

  1. 展示展位列表

    • 遍历 data.slots[],根据 status 显示不同状态
    • EMPTY:显示空展位占位图
    • OCCUPIED:显示 asset.cover_url 和资产信息
    • LOCKED:显示锁定图标和 unlock_condition
  2. 放置藏品

    • 需要 gallery_owner_idslot_idasset_id
    • 调用 POST /api/v1/galleries/place
  3. 权限判断

    • 只有展馆所有者可以放置/移除展品
    • 他人只能查看已解锁的展位(is_enabled=true

数据库查询示例

查询展位列表

-- 查询用户ID=37明星ID=88的所有展位
SELECT 
    slot_id,
    slot_index,
    is_enabled,
    unlock_type,
    unlock_value
FROM booth_slots
WHERE user_id = 37 AND star_id = 88
ORDER BY slot_index ASC;

查询展品展示情况

-- 查询展位ID=47的展品信息
SELECT 
    e.asset_id,
    e.occupier_uid,
    e.start_time,
    e.expire_at,
    a.name AS asset_name,
    a.cover_url
FROM exhibitions e
LEFT JOIN assets a ON e.asset_id = a.id
WHERE e.slot_id = 47;

总结

  1. 数据库表

    • booth_slots:展位基础信息
    • exhibitions:展品展示关系
  2. 响应格式

    • /api/v1/galleries/me/api/v1/galleries/{target_uid} 响应格式完全一致
    • 都返回 { gallery_owner_id, slot_total, slots[] }
    • 差异仅在于数据内容(查询的目标用户不同)
  3. gallery_owner_id 计算

    • 公式:user_id * 1000000 + star_id
    • 示例用户37、明星88 → 37000088
  4. status 状态

    • LOCKED:未解锁(is_enabled=false
    • EMPTY:已解锁但空展位(is_enabled=trueexhibitions 无记录)
    • OCCUPIED:有展品(is_enabled=trueexhibitions 有记录)