package baishun import ( "chatapp3-golang/internal/model" "context" "fmt" "net/http" "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) { sysOrigin = normalizeAdminSysOrigin(sysOrigin) profile, err := s.activeProfile(ctx, sysOrigin) if err != nil { return nil, err } 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, ext.profile AS profile, 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, ext.extra_json AS ext_extra_json, 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 AND ext.profile = ?", profile). Joins("LEFT JOIN baishun_game_catalog cat ON cat.sys_origin = cfg.sys_origin AND cat.profile = ext.profile 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()) } return items, nil } // findGameRow 按内部游戏 ID 查询游戏聚合信息。 func (s *BaishunService) findGameRow(ctx context.Context, sysOrigin, gameID string) (gameListRow, error) { sysOrigin = normalizeAdminSysOrigin(sysOrigin) profile, err := s.activeProfile(ctx, sysOrigin) if err != nil { return gameListRow{}, err } 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, ext.profile AS profile, 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, ext.extra_json AS ext_extra_json, 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 AND ext.profile = ?", profile). Joins("LEFT JOIN baishun_game_catalog cat ON cat.sys_origin = cfg.sys_origin AND cat.profile = ext.profile 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 profile = ? AND internal_game_id = ?", sysOrigin, profile, gameID). First(&catalog).Error; err == nil { return gameListRow{ SysOrigin: sysOrigin, Profile: profile, InternalGameID: defaultIfBlank(catalog.InternalGameID, fmt.Sprintf("bs_%d", catalog.VendorGameID)), Name: catalog.Name, Cover: catalog.Cover, VendorType: baishunVendorType, VendorGameID: strconv.Itoa(catalog.VendorGameID), LaunchMode: baishunLaunchModeRemote, ExtGSP: catalog.GSP, 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 } else if err != gorm.ErrRecordNotFound { return gameListRow{}, err } return gameListRow{}, NewAppError(http.StatusNotFound, "game_not_found", "room game config not found") } // findGameRowByConfigID 按系统游戏配置主键查询游戏聚合信息。 func (s *BaishunService) findGameRowByConfigID(ctx context.Context, sysOrigin string, configID int64) (gameListRow, error) { sysOrigin = normalizeAdminSysOrigin(sysOrigin) profile, err := s.activeProfile(ctx, sysOrigin) if err != nil { return gameListRow{}, err } 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, ext.profile AS profile, 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, ext.extra_json AS ext_extra_json, 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 AND ext.profile = ?", profile). Joins("LEFT JOIN baishun_game_catalog cat ON cat.sys_origin = cfg.sys_origin AND cat.profile = ext.profile AND CAST(cat.vendor_game_id AS CHAR) = ext.vendor_game_id"). Where("cfg.sys_origin = ? AND cfg.id = ? AND ext.vendor_type = ?", sysOrigin, configID, baishunVendorType). Limit(1). Scan(&row).Error if err != nil { return gameListRow{}, err } if row.ConfigID > 0 { return row, nil } return gameListRow{}, NewAppError(http.StatusNotFound, "game_not_found", "room game config not found") } // resolveGSP 解析行模型中的 GSP 值。 func (s *BaishunService) resolveGSP(row gameListRow, runtimeCfg baishunRuntimeConfig) int { if parsed, err := strconv.Atoi(strings.TrimSpace(row.ExtGSP)); err == nil && parsed > 0 { return parsed } return runtimeCfg.gsp() }