2026-04-21 20:21:28 +08:00

230 lines
8.0 KiB
Go

package baishun
import (
"chatapp3-golang/internal/model"
"context"
"fmt"
"net/http"
"sort"
"strconv"
"strings"
"gorm.io/gorm"
)
// ListShortcutGames 返回房间里的快捷游戏列表,数量上限为 5 个。
func (s *BaishunService) ListShortcutGames(ctx context.Context, user AuthUser, roomID string) ([]RoomGameListItem, error) {
items, err := s.listRoomGames(ctx, user.SysOrigin, roomID, "")
if err != nil {
return nil, err
}
if len(items) > 5 {
items = items[:5]
}
return items, nil
}
// ListRoomGames 返回房间可启动的完整游戏列表。
func (s *BaishunService) ListRoomGames(ctx context.Context, user AuthUser, roomID string, category string) (*RoomGameListResponse, error) {
items, err := s.listRoomGames(ctx, user.SysOrigin, roomID, category)
if err != nil {
return nil, err
}
return &RoomGameListResponse{Items: items}, nil
}
// GetRoomState 返回房间当前的游戏挂载状态。
func (s *BaishunService) GetRoomState(ctx context.Context, user AuthUser, roomID string) (*BaishunRoomStateResponse, error) {
var state model.BaishunRoomState
if err := s.repo.DB.WithContext(ctx).
Where("sys_origin = ? AND room_id = ?", user.SysOrigin, roomID).
First(&state).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return &BaishunRoomStateResponse{RoomID: roomID, State: baishunRoomStateIdle}, nil
}
return nil, err
}
resp := &BaishunRoomStateResponse{
RoomID: roomID,
State: defaultIfBlank(state.State, baishunRoomStateIdle),
GameSessionID: state.GameSessionID,
CurrentGameID: state.CurrentGameID,
CurrentGameName: state.CurrentGameName,
CurrentGameCover: state.CurrentGameCover,
HostUserID: state.HostUserID,
}
if state.CurrentVendorGameID != nil {
resp.CurrentVendorGameID = *state.CurrentVendorGameID
}
return resp, nil
}
// listRoomGames 联表读取房间可启动游戏,并按展示规则排序。
func (s *BaishunService) listRoomGames(ctx context.Context, sysOrigin, roomID, category string) ([]RoomGameListItem, error) {
var rows []gameListRow
query := s.repo.DB.WithContext(ctx).
Table("sys_game_list_config AS cfg").
Select(`
cfg.id AS config_id,
cfg.sys_origin AS sys_origin,
cfg.game_id AS internal_game_id,
cfg.name AS name,
cfg.category AS category,
cfg.cover AS cover,
cfg.sort AS sort,
cfg.full_screen AS full_screen,
cfg.game_mode AS game_mode_raw,
ext.vendor_type AS vendor_type,
ext.vendor_game_id AS vendor_game_id,
ext.launch_mode AS launch_mode,
ext.package_version AS ext_package_version,
ext.preview_url AS ext_preview_url,
ext.package_url AS package_url,
ext.orientation AS ext_orientation,
ext.safe_height AS ext_safe_height,
ext.currency_icon AS currency_icon,
ext.gsp AS ext_gsp,
cat.name AS catalog_name,
cat.cover AS catalog_cover,
cat.preview_url AS catalog_preview_url,
cat.download_url AS catalog_download_url,
cat.package_version AS catalog_package_version,
cat.orientation AS catalog_orientation,
cat.safe_height AS catalog_safe_height,
cat.status AS catalog_status
`).
Joins("JOIN sys_game_list_vendor_ext ext ON ext.game_list_config_id = cfg.id AND ext.enabled = 1").
Joins("LEFT JOIN baishun_game_catalog cat ON cat.sys_origin = cfg.sys_origin AND CAST(cat.vendor_game_id AS CHAR) = ext.vendor_game_id").
Where("cfg.sys_origin = ? AND cfg.is_showcase = 1 AND ext.vendor_type = ?", sysOrigin, baishunVendorType)
if strings.TrimSpace(category) != "" && !strings.EqualFold(strings.TrimSpace(category), "CHAT_ROOM") {
query = query.Where("cfg.category = ?", category)
}
if err := query.Order("cfg.sort DESC").Scan(&rows).Error; err != nil {
return nil, err
}
items := make([]RoomGameListItem, 0, len(rows))
for _, row := range rows {
items = append(items, row.toListItem())
}
if len(items) > 0 {
return items, nil
}
var catalogs []model.BaishunGameCatalog
if err := s.repo.DB.WithContext(ctx).
Where("sys_origin = ? AND status = ?", sysOrigin, baishunCatalogEnabled).
Order("vendor_game_id ASC").
Find(&catalogs).Error; err != nil {
return nil, err
}
for _, catalog := range catalogs {
gameID := defaultIfBlank(catalog.InternalGameID, fmt.Sprintf("bs_%d", catalog.VendorGameID))
items = append(items, RoomGameListItem{
GameID: gameID,
VendorType: baishunVendorType,
VendorGameID: catalog.VendorGameID,
Name: catalog.Name,
Cover: defaultIfBlank(catalog.Cover, catalog.PreviewURL),
Category: "CHAT_ROOM",
Sort: int64(catalog.VendorGameID),
LaunchMode: baishunLaunchModeRemote,
FullScreen: true,
GameMode: baishunFixedGameMode,
SafeHeight: catalog.SafeHeight,
Orientation: derefInt(catalog.Orientation),
PackageVersion: catalog.PackageVersion,
Status: catalog.Status,
})
}
sort.Slice(items, func(i, j int) bool {
if items[i].Sort == items[j].Sort {
return items[i].VendorGameID < items[j].VendorGameID
}
return items[i].Sort > items[j].Sort
})
return items, nil
}
// findGameRow 按内部游戏 ID 查询游戏聚合信息。
func (s *BaishunService) findGameRow(ctx context.Context, sysOrigin, gameID string) (gameListRow, error) {
var row gameListRow
err := s.repo.DB.WithContext(ctx).
Table("sys_game_list_config AS cfg").
Select(`
cfg.id AS config_id,
cfg.sys_origin AS sys_origin,
cfg.game_id AS internal_game_id,
cfg.name AS name,
cfg.category AS category,
cfg.cover AS cover,
cfg.sort AS sort,
cfg.full_screen AS full_screen,
cfg.game_mode AS game_mode_raw,
ext.vendor_type AS vendor_type,
ext.vendor_game_id AS vendor_game_id,
ext.launch_mode AS launch_mode,
ext.package_version AS ext_package_version,
ext.preview_url AS ext_preview_url,
ext.package_url AS package_url,
ext.orientation AS ext_orientation,
ext.safe_height AS ext_safe_height,
ext.currency_icon AS currency_icon,
ext.gsp AS ext_gsp,
cat.name AS catalog_name,
cat.cover AS catalog_cover,
cat.preview_url AS catalog_preview_url,
cat.download_url AS catalog_download_url,
cat.package_version AS catalog_package_version,
cat.orientation AS catalog_orientation,
cat.safe_height AS catalog_safe_height,
cat.status AS catalog_status
`).
Joins("JOIN sys_game_list_vendor_ext ext ON ext.game_list_config_id = cfg.id AND ext.enabled = 1").
Joins("LEFT JOIN baishun_game_catalog cat ON cat.sys_origin = cfg.sys_origin AND CAST(cat.vendor_game_id AS CHAR) = ext.vendor_game_id").
Where("cfg.sys_origin = ? AND cfg.game_id = ? AND ext.vendor_type = ?", sysOrigin, gameID, baishunVendorType).
Limit(1).
Scan(&row).Error
if err != nil {
return gameListRow{}, err
}
if strings.TrimSpace(row.InternalGameID) != "" {
return row, nil
}
var catalog model.BaishunGameCatalog
if err := s.repo.DB.WithContext(ctx).
Where("sys_origin = ? AND internal_game_id = ?", sysOrigin, gameID).
First(&catalog).Error; err == nil {
return gameListRow{
SysOrigin: sysOrigin,
InternalGameID: catalog.InternalGameID,
Name: catalog.Name,
Cover: catalog.Cover,
VendorType: baishunVendorType,
VendorGameID: strconv.Itoa(catalog.VendorGameID),
LaunchMode: baishunLaunchModeRemote,
CatalogCover: catalog.Cover,
CatalogPreviewURL: catalog.PreviewURL,
CatalogDownloadURL: catalog.DownloadURL,
CatalogPackageVersion: catalog.PackageVersion,
CatalogSafeHeight: catalog.SafeHeight,
CatalogOrientation: catalog.Orientation,
CatalogStatus: catalog.Status,
GameModeRaw: formatAdminGameModeRaw(baishunFixedGameMode),
}, nil
}
return gameListRow{}, NewAppError(http.StatusNotFound, "game_not_found", "room game config not found")
}
// resolveGSP 解析行模型中的 GSP 值。
func (s *BaishunService) resolveGSP(row gameListRow) int {
if parsed, err := strconv.Atoi(strings.TrimSpace(row.ExtGSP)); err == nil && parsed > 0 {
return parsed
}
if s.cfg.Baishun.GSP > 0 {
return s.cfg.Baishun.GSP
}
return 101
}