feat: update baishun game mode rollout
This commit is contained in:
parent
df9a779b62
commit
0701bd0795
@ -4,20 +4,23 @@ import "time"
|
||||
|
||||
// SysGameListConfig 是系统游戏列表主表,描述平台可展示的游戏。
|
||||
type SysGameListConfig struct {
|
||||
ID int64 `gorm:"column:id;primaryKey"` // 主键
|
||||
SysOrigin string `gorm:"column:sys_origin;size:32"` // 系统标识
|
||||
GameOrigin string `gorm:"column:game_origin;size:32"` // 游戏来源
|
||||
GameID string `gorm:"column:game_id;size:64"` // 内部游戏 ID
|
||||
Name string `gorm:"column:name;size:128"` // 游戏名称
|
||||
Category string `gorm:"column:category;size:64"` // 分类
|
||||
GameCode string `gorm:"column:game_code;size:512"` // 游戏码或入口标识
|
||||
Cover string `gorm:"column:cover;size:1024"` // 封面图
|
||||
Showcase bool `gorm:"column:is_showcase"` // 是否首页推荐
|
||||
Sort int64 `gorm:"column:sort"` // 排序值
|
||||
FullScreen bool `gorm:"column:full_screen"` // 是否全屏展示
|
||||
ClientOrigin string `gorm:"column:client_origin;size:32"` // 客户端来源
|
||||
Regions string `gorm:"column:regions;size:255"` // 可投放地区
|
||||
GameMode string `gorm:"column:game_mode;size:255"` // 游戏模式配置
|
||||
ID int64 `gorm:"column:id;primaryKey"` // 主键
|
||||
SysOrigin string `gorm:"column:sys_origin;size:32"` // 系统标识
|
||||
GameOrigin string `gorm:"column:game_origin;size:32"` // 游戏来源
|
||||
GameID string `gorm:"column:game_id;size:64"` // 内部游戏 ID
|
||||
Name string `gorm:"column:name;size:128"` // 游戏名称
|
||||
Category string `gorm:"column:category;size:64"` // 分类
|
||||
GameCode string `gorm:"column:game_code;size:512"` // 游戏码或入口标识
|
||||
Cover string `gorm:"column:cover;size:1024"` // 封面图
|
||||
Amounts string `gorm:"column:amounts;size:512"` // 金额配置
|
||||
Showcase bool `gorm:"column:is_showcase"` // 是否首页推荐
|
||||
Sort int64 `gorm:"column:sort"` // 排序值
|
||||
Height float64 `gorm:"column:height"` // 高度
|
||||
Width float64 `gorm:"column:width"` // 宽度
|
||||
FullScreen bool `gorm:"column:full_screen"` // 是否全屏展示
|
||||
ClientOrigin string `gorm:"column:client_origin;size:32"` // 客户端来源
|
||||
Regions string `gorm:"column:regions;size:255"` // 可投放地区
|
||||
GameMode string `gorm:"column:game_mode;size:255"` // 游戏模式配置
|
||||
}
|
||||
|
||||
// TableName 返回系统游戏列表表名。
|
||||
|
||||
@ -109,6 +109,73 @@ func registerBaishunRoutes(
|
||||
writeOK(c, resp)
|
||||
})
|
||||
|
||||
consoleGroup := engine.Group("/operate/baishun-game")
|
||||
consoleGroup.Use(consoleAuthMiddleware(javaClient))
|
||||
consoleGroup.GET("/page", func(c *gin.Context) {
|
||||
cursor, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("cursor", "1")))
|
||||
limit, _ := strconv.Atoi(strings.TrimSpace(c.DefaultQuery("limit", "20")))
|
||||
resp, err := baishunService.PageAdminGames(
|
||||
c.Request.Context(),
|
||||
c.Query("sysOrigin"),
|
||||
c.Query("keyword"),
|
||||
parseOptionalBool(c.Query("showcase")),
|
||||
cursor,
|
||||
limit,
|
||||
)
|
||||
if err != nil {
|
||||
writeError(c, err)
|
||||
return
|
||||
}
|
||||
writeOK(c, resp)
|
||||
})
|
||||
consoleGroup.POST("/save", func(c *gin.Context) {
|
||||
var req baishun.SaveAdminBaishunGameRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
||||
return
|
||||
}
|
||||
resp, err := baishunService.SaveAdminGame(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
writeError(c, err)
|
||||
return
|
||||
}
|
||||
writeOK(c, resp)
|
||||
})
|
||||
consoleGroup.DELETE("", func(c *gin.Context) {
|
||||
id, _ := strconv.ParseInt(strings.TrimSpace(c.Query("id")), 10, 64)
|
||||
if err := baishunService.DeleteAdminGame(c.Request.Context(), c.Query("sysOrigin"), id); err != nil {
|
||||
writeError(c, err)
|
||||
return
|
||||
}
|
||||
writeOK(c, gin.H{"deleted": true})
|
||||
})
|
||||
consoleGroup.POST("/import-catalog", func(c *gin.Context) {
|
||||
var req baishun.SyncCatalogRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
||||
return
|
||||
}
|
||||
resp, err := baishunService.ImportCatalogGames(c.Request.Context(), req.SysOrigin, req.VendorGameIDs)
|
||||
if err != nil {
|
||||
writeError(c, err)
|
||||
return
|
||||
}
|
||||
writeOK(c, resp)
|
||||
})
|
||||
consoleGroup.POST("/sync", func(c *gin.Context) {
|
||||
var req baishun.SyncCatalogRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
writeError(c, baishun.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
||||
return
|
||||
}
|
||||
resp, err := baishunService.SyncAdminGames(c.Request.Context(), req)
|
||||
if err != nil {
|
||||
writeError(c, err)
|
||||
return
|
||||
}
|
||||
writeOK(c, resp)
|
||||
})
|
||||
|
||||
callbackGroup := engine.Group("/game/baishun")
|
||||
callbackGroup.POST("/token", func(c *gin.Context) {
|
||||
raw, payload := readRawPayload(c)
|
||||
@ -268,3 +335,16 @@ func asInt64(value any) int64 {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
|
||||
func parseOptionalBool(raw string) *bool {
|
||||
switch strings.ToLower(strings.TrimSpace(raw)) {
|
||||
case "0", "false":
|
||||
value := false
|
||||
return &value
|
||||
case "1", "true":
|
||||
value := true
|
||||
return &value
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
720
internal/service/baishun/admin.go
Normal file
720
internal/service/baishun/admin.go
Normal file
@ -0,0 +1,720 @@
|
||||
package baishun
|
||||
|
||||
import (
|
||||
"chatapp3-golang/internal/model"
|
||||
"chatapp3-golang/internal/utils"
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const adminTimeLayout = "2006-01-02 15:04:05"
|
||||
|
||||
type adminGameRow struct {
|
||||
ID int64
|
||||
SysOrigin string
|
||||
InternalGameID string
|
||||
Name string
|
||||
Category string
|
||||
GameCode string
|
||||
Cover string
|
||||
Amounts string
|
||||
Showcase bool
|
||||
Sort int64
|
||||
Height float64
|
||||
Width float64
|
||||
FullScreen bool
|
||||
ClientOrigin string
|
||||
Regions string
|
||||
GameModeRaw string
|
||||
CreateTime time.Time
|
||||
UpdateTime time.Time
|
||||
VendorGameID string
|
||||
LaunchMode string
|
||||
ExtPackageVersion string
|
||||
ExtPreviewURL string
|
||||
PackageURL string
|
||||
ExtOrientation *int
|
||||
ExtSafeHeight int
|
||||
ExtGSP string
|
||||
CurrencyIcon string
|
||||
CatalogStatus string
|
||||
CatalogPreviewURL string
|
||||
CatalogDownloadURL string
|
||||
CatalogPackage string
|
||||
CatalogOrientation *int
|
||||
CatalogSafeHeight int
|
||||
CatalogGameModeJSON string
|
||||
}
|
||||
|
||||
// PageAdminGames 返回后台百顺游戏管理分页数据。
|
||||
func (s *BaishunService) PageAdminGames(
|
||||
ctx context.Context,
|
||||
sysOrigin string,
|
||||
keyword string,
|
||||
showcase *bool,
|
||||
cursor int,
|
||||
limit int,
|
||||
) (*AdminBaishunGamePageResponse, error) {
|
||||
sysOrigin = normalizeAdminSysOrigin(sysOrigin)
|
||||
cursor = normalizePageCursor(cursor)
|
||||
limit = normalizePageLimit(limit)
|
||||
|
||||
countQuery := s.buildAdminGameQuery(ctx, sysOrigin, keyword, showcase)
|
||||
|
||||
var total int64
|
||||
if err := countQuery.Count(&total).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var rows []adminGameRow
|
||||
listQuery := s.buildAdminGameQuery(ctx, sysOrigin, keyword, showcase)
|
||||
if err := listQuery.
|
||||
Select(`
|
||||
cfg.id AS id,
|
||||
cfg.sys_origin AS sys_origin,
|
||||
cfg.game_id AS internal_game_id,
|
||||
cfg.name AS name,
|
||||
cfg.category AS category,
|
||||
cfg.game_code AS game_code,
|
||||
cfg.cover AS cover,
|
||||
cfg.amounts AS amounts,
|
||||
cfg.is_showcase AS showcase,
|
||||
cfg.sort AS sort,
|
||||
cfg.height AS height,
|
||||
cfg.width AS width,
|
||||
cfg.full_screen AS full_screen,
|
||||
cfg.client_origin AS client_origin,
|
||||
cfg.regions AS regions,
|
||||
cfg.game_mode AS game_mode_raw,
|
||||
cfg.create_time AS create_time,
|
||||
cfg.update_time AS update_time,
|
||||
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.gsp AS ext_gsp,
|
||||
ext.currency_icon AS currency_icon,
|
||||
cat.status AS catalog_status,
|
||||
cat.preview_url AS catalog_preview_url,
|
||||
cat.download_url AS catalog_download_url,
|
||||
cat.package_version AS catalog_package,
|
||||
cat.orientation AS catalog_orientation,
|
||||
cat.safe_height AS catalog_safe_height,
|
||||
cat.game_mode_json AS catalog_game_mode_json
|
||||
`).
|
||||
Order("cfg.is_showcase DESC").
|
||||
Order("cfg.sort DESC").
|
||||
Order("cfg.update_time DESC").
|
||||
Limit(limit).
|
||||
Offset((cursor - 1) * limit).
|
||||
Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]AdminBaishunGameItem, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
items = append(items, row.toAdminItem())
|
||||
}
|
||||
|
||||
return &AdminBaishunGamePageResponse{
|
||||
Cursor: cursor,
|
||||
Limit: limit,
|
||||
Records: items,
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SaveAdminGame 保存后台百顺游戏配置。
|
||||
func (s *BaishunService) SaveAdminGame(ctx context.Context, req SaveAdminBaishunGameRequest) (*AdminBaishunGameItem, error) {
|
||||
sysOrigin := normalizeAdminSysOrigin(req.SysOrigin)
|
||||
|
||||
catalog, err := s.findCatalogForAdmin(ctx, sysOrigin, req.VendorGameID, req.GameID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if req.VendorGameID <= 0 {
|
||||
req.VendorGameID = catalog.VendorGameID
|
||||
}
|
||||
if req.VendorGameID <= 0 {
|
||||
return nil, NewAppError(http.StatusBadRequest, "vendor_game_id_required", "vendorGameId is required")
|
||||
}
|
||||
|
||||
gameID := strings.TrimSpace(req.GameID)
|
||||
if gameID == "" {
|
||||
gameID = defaultIfBlank(catalog.InternalGameID, fmt.Sprintf("bs_%d", req.VendorGameID))
|
||||
}
|
||||
if gameID == "" {
|
||||
return nil, NewAppError(http.StatusBadRequest, "game_id_required", "gameId is required")
|
||||
}
|
||||
|
||||
name := defaultIfBlank(strings.TrimSpace(req.Name), defaultIfBlank(catalog.Name, gameID))
|
||||
if name == "" {
|
||||
return nil, NewAppError(http.StatusBadRequest, "name_required", "name is required")
|
||||
}
|
||||
category := defaultIfBlank(strings.TrimSpace(req.Category), "OTHER")
|
||||
cover := defaultIfBlank(strings.TrimSpace(req.Cover), defaultIfBlank(catalog.Cover, catalog.PreviewURL))
|
||||
clientOrigin := defaultIfBlank(strings.TrimSpace(req.ClientOrigin), "COMMON")
|
||||
gameCode := defaultIfBlank(strings.TrimSpace(req.GameCode), strconv.Itoa(req.VendorGameID))
|
||||
launchMode := defaultIfBlank(strings.TrimSpace(req.LaunchMode), baishunLaunchModeRemote)
|
||||
packageVersion := defaultIfBlank(strings.TrimSpace(req.PackageVersion), catalog.PackageVersion)
|
||||
previewURL := defaultIfBlank(strings.TrimSpace(req.PreviewURL), catalog.PreviewURL)
|
||||
packageURL := defaultIfBlank(strings.TrimSpace(req.PackageURL), catalog.DownloadURL)
|
||||
gsp := defaultIfBlank(strings.TrimSpace(req.GSP), defaultAdminGSP(catalog.GSP, s.cfg.Baishun.GSP))
|
||||
safeHeight := req.SafeHeight
|
||||
if safeHeight <= 0 {
|
||||
safeHeight = catalog.SafeHeight
|
||||
}
|
||||
orientation := req.Orientation
|
||||
if orientation == nil && catalog.Orientation != nil {
|
||||
value := *catalog.Orientation
|
||||
orientation = &value
|
||||
}
|
||||
gameMode := baishunFixedGameMode
|
||||
|
||||
tx := s.repo.DB.WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
panic(r)
|
||||
}
|
||||
}()
|
||||
|
||||
cfg, err := s.findAdminConfigForSave(tx, sysOrigin, req.ID, gameID, req.VendorGameID)
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfgValues := map[string]any{
|
||||
"sys_origin": sysOrigin,
|
||||
"game_origin": baishunVendorType,
|
||||
"game_id": gameID,
|
||||
"name": name,
|
||||
"category": category,
|
||||
"game_code": gameCode,
|
||||
"cover": cover,
|
||||
"amounts": strings.TrimSpace(req.Amounts),
|
||||
"is_showcase": req.Showcase,
|
||||
"sort": req.Sort,
|
||||
"height": req.Height,
|
||||
"width": req.Width,
|
||||
"full_screen": req.FullScreen,
|
||||
"client_origin": clientOrigin,
|
||||
"regions": strings.TrimSpace(req.Regions),
|
||||
"game_mode": formatAdminGameModeRaw(gameMode),
|
||||
}
|
||||
|
||||
if cfg.ID == 0 {
|
||||
nextID, idErr := utils.NextID()
|
||||
if idErr != nil {
|
||||
tx.Rollback()
|
||||
return nil, idErr
|
||||
}
|
||||
cfg = model.SysGameListConfig{
|
||||
ID: nextID,
|
||||
SysOrigin: sysOrigin,
|
||||
GameOrigin: baishunVendorType,
|
||||
GameID: gameID,
|
||||
Name: name,
|
||||
Category: category,
|
||||
GameCode: gameCode,
|
||||
Cover: cover,
|
||||
Amounts: strings.TrimSpace(req.Amounts),
|
||||
Showcase: req.Showcase,
|
||||
Sort: req.Sort,
|
||||
Height: req.Height,
|
||||
Width: req.Width,
|
||||
FullScreen: req.FullScreen,
|
||||
ClientOrigin: clientOrigin,
|
||||
Regions: strings.TrimSpace(req.Regions),
|
||||
GameMode: formatAdminGameModeRaw(gameMode),
|
||||
}
|
||||
if err := tx.Create(&cfg).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := tx.Model(&model.SysGameListConfig{}).
|
||||
Where("id = ?", cfg.ID).
|
||||
Updates(cfgValues).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var ext model.SysGameListVendorExt
|
||||
err = tx.Where("game_list_config_id = ? AND vendor_type = ?", cfg.ID, baishunVendorType).First(&ext).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ext.ID == 0 {
|
||||
nextID, idErr := utils.NextID()
|
||||
if idErr != nil {
|
||||
tx.Rollback()
|
||||
return nil, idErr
|
||||
}
|
||||
ext = model.SysGameListVendorExt{
|
||||
ID: nextID,
|
||||
GameListConfigID: cfg.ID,
|
||||
SysOrigin: sysOrigin,
|
||||
VendorType: baishunVendorType,
|
||||
VendorGameID: strconv.Itoa(req.VendorGameID),
|
||||
LaunchMode: launchMode,
|
||||
PackageVersion: packageVersion,
|
||||
PackageURL: packageURL,
|
||||
PreviewURL: previewURL,
|
||||
Orientation: orientation,
|
||||
SafeHeight: safeHeight,
|
||||
GSP: gsp,
|
||||
CurrencyIcon: strings.TrimSpace(req.CurrencyIcon),
|
||||
Enabled: true,
|
||||
}
|
||||
if err := tx.Create(&ext).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
if err := tx.Model(&model.SysGameListVendorExt{}).
|
||||
Where("id = ?", ext.ID).
|
||||
Updates(map[string]any{
|
||||
"sys_origin": sysOrigin,
|
||||
"vendor_game_id": strconv.Itoa(req.VendorGameID),
|
||||
"launch_mode": launchMode,
|
||||
"package_version": packageVersion,
|
||||
"package_url": packageURL,
|
||||
"preview_url": previewURL,
|
||||
"orientation": orientation,
|
||||
"safe_height": safeHeight,
|
||||
"gsp": gsp,
|
||||
"currency_icon": strings.TrimSpace(req.CurrencyIcon),
|
||||
"enabled": true,
|
||||
"bridge_schema_version": defaultIfBlank(ext.BridgeSchemaVersion, "1.0"),
|
||||
}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.getAdminGame(ctx, sysOrigin, cfg.ID)
|
||||
}
|
||||
|
||||
// DeleteAdminGame 删除后台百顺游戏配置,不影响目录表。
|
||||
func (s *BaishunService) DeleteAdminGame(ctx context.Context, sysOrigin string, id int64) error {
|
||||
if id <= 0 {
|
||||
return NewAppError(http.StatusBadRequest, "id_required", "id is required")
|
||||
}
|
||||
sysOrigin = normalizeAdminSysOrigin(sysOrigin)
|
||||
|
||||
tx := s.repo.DB.WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
return tx.Error
|
||||
}
|
||||
if err := tx.Where("game_list_config_id = ? AND vendor_type = ?", id, baishunVendorType).
|
||||
Delete(&model.SysGameListVendorExt{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
if err := tx.Where("id = ? AND sys_origin = ? AND game_origin = ?", id, sysOrigin, baishunVendorType).
|
||||
Delete(&model.SysGameListConfig{}).Error; err != nil {
|
||||
tx.Rollback()
|
||||
return err
|
||||
}
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
||||
// ImportCatalogGames 把本地目录中尚未进入后台列表的百顺游戏补齐成可配置记录。
|
||||
func (s *BaishunService) ImportCatalogGames(ctx context.Context, sysOrigin string, vendorGameIDs []int) (*ImportCatalogResponse, error) {
|
||||
tx := s.repo.DB.WithContext(ctx).Begin()
|
||||
if tx.Error != nil {
|
||||
return nil, tx.Error
|
||||
}
|
||||
resp, err := s.importCatalogGamesTx(tx, normalizeAdminSysOrigin(sysOrigin), buildVendorGameIDFilter(vendorGameIDs))
|
||||
if err != nil {
|
||||
tx.Rollback()
|
||||
return nil, err
|
||||
}
|
||||
if err := tx.Commit().Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// SyncAdminGames 先同步百顺平台目录,再把缺失配置导入后台列表。
|
||||
func (s *BaishunService) SyncAdminGames(ctx context.Context, req SyncCatalogRequest) (*SyncAdminCatalogResponse, error) {
|
||||
syncResp, err := s.SyncCatalog(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
importResp, err := s.ImportCatalogGames(ctx, req.SysOrigin, req.VendorGameIDs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SyncAdminCatalogResponse{
|
||||
Inserted: syncResp.Inserted,
|
||||
Updated: syncResp.Updated,
|
||||
Skipped: syncResp.Skipped,
|
||||
Existing: importResp.Existing,
|
||||
Imported: importResp.Imported,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *BaishunService) buildAdminGameQuery(ctx context.Context, sysOrigin, keyword string, showcase *bool) *gorm.DB {
|
||||
query := s.repo.DB.WithContext(ctx).
|
||||
Table("sys_game_list_config AS cfg").
|
||||
Joins("LEFT JOIN sys_game_list_vendor_ext ext ON ext.game_list_config_id = cfg.id AND ext.vendor_type = ?", baishunVendorType).
|
||||
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
|
||||
OR cat.internal_game_id = cfg.game_id
|
||||
)
|
||||
`).
|
||||
Where("cfg.sys_origin = ? AND cfg.game_origin = ?", sysOrigin, baishunVendorType)
|
||||
if showcase != nil {
|
||||
query = query.Where("cfg.is_showcase = ?", *showcase)
|
||||
}
|
||||
keyword = strings.TrimSpace(keyword)
|
||||
if keyword != "" {
|
||||
like := "%" + keyword + "%"
|
||||
query = query.Where(`
|
||||
cfg.name LIKE ?
|
||||
OR cfg.game_id LIKE ?
|
||||
OR cfg.game_code LIKE ?
|
||||
OR ext.vendor_game_id LIKE ?
|
||||
`, like, like, like, like)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func (s *BaishunService) getAdminGame(ctx context.Context, sysOrigin string, id int64) (*AdminBaishunGameItem, error) {
|
||||
var rows []adminGameRow
|
||||
err := s.buildAdminGameQuery(ctx, sysOrigin, "", nil).
|
||||
Where("cfg.id = ?", id).
|
||||
Select(`
|
||||
cfg.id AS id,
|
||||
cfg.sys_origin AS sys_origin,
|
||||
cfg.game_id AS internal_game_id,
|
||||
cfg.name AS name,
|
||||
cfg.category AS category,
|
||||
cfg.game_code AS game_code,
|
||||
cfg.cover AS cover,
|
||||
cfg.amounts AS amounts,
|
||||
cfg.is_showcase AS showcase,
|
||||
cfg.sort AS sort,
|
||||
cfg.height AS height,
|
||||
cfg.width AS width,
|
||||
cfg.full_screen AS full_screen,
|
||||
cfg.client_origin AS client_origin,
|
||||
cfg.regions AS regions,
|
||||
cfg.game_mode AS game_mode_raw,
|
||||
cfg.create_time AS create_time,
|
||||
cfg.update_time AS update_time,
|
||||
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.gsp AS ext_gsp,
|
||||
ext.currency_icon AS currency_icon,
|
||||
cat.status AS catalog_status,
|
||||
cat.preview_url AS catalog_preview_url,
|
||||
cat.download_url AS catalog_download_url,
|
||||
cat.package_version AS catalog_package,
|
||||
cat.orientation AS catalog_orientation,
|
||||
cat.safe_height AS catalog_safe_height,
|
||||
cat.game_mode_json AS catalog_game_mode_json
|
||||
`).
|
||||
Limit(1).
|
||||
Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(rows) == 1 {
|
||||
item := rows[0].toAdminItem()
|
||||
return &item, nil
|
||||
}
|
||||
return nil, NewAppError(http.StatusNotFound, "game_not_found", "admin baishun game not found")
|
||||
}
|
||||
|
||||
func (s *BaishunService) findCatalogForAdmin(ctx context.Context, sysOrigin string, vendorGameID int, gameID string) (model.BaishunGameCatalog, error) {
|
||||
query := s.repo.DB.WithContext(ctx).Where("sys_origin = ?", sysOrigin)
|
||||
switch {
|
||||
case vendorGameID > 0:
|
||||
query = query.Where("vendor_game_id = ?", vendorGameID)
|
||||
case strings.TrimSpace(gameID) != "":
|
||||
query = query.Where("internal_game_id = ?", strings.TrimSpace(gameID))
|
||||
default:
|
||||
return model.BaishunGameCatalog{}, nil
|
||||
}
|
||||
|
||||
var catalog model.BaishunGameCatalog
|
||||
if err := query.First(&catalog).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return model.BaishunGameCatalog{}, nil
|
||||
}
|
||||
return model.BaishunGameCatalog{}, err
|
||||
}
|
||||
return catalog, nil
|
||||
}
|
||||
|
||||
func (s *BaishunService) findAdminConfigForSave(tx *gorm.DB, sysOrigin string, id int64, gameID string, vendorGameID int) (model.SysGameListConfig, error) {
|
||||
var cfg model.SysGameListConfig
|
||||
if id > 0 {
|
||||
err := tx.Where("id = ? AND sys_origin = ? AND game_origin = ?", id, sysOrigin, baishunVendorType).First(&cfg).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return model.SysGameListConfig{}, NewAppError(http.StatusNotFound, "game_not_found", "admin baishun game not found")
|
||||
}
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
if vendorGameID > 0 {
|
||||
var ext model.SysGameListVendorExt
|
||||
err := tx.Where("sys_origin = ? AND vendor_type = ? AND vendor_game_id = ?", sysOrigin, baishunVendorType, strconv.Itoa(vendorGameID)).First(&ext).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return model.SysGameListConfig{}, err
|
||||
}
|
||||
if ext.GameListConfigID > 0 {
|
||||
err = tx.Where("id = ? AND sys_origin = ? AND game_origin = ?", ext.GameListConfigID, sysOrigin, baishunVendorType).First(&cfg).Error
|
||||
if err == nil {
|
||||
return cfg, nil
|
||||
}
|
||||
if err != gorm.ErrRecordNotFound {
|
||||
return model.SysGameListConfig{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if strings.TrimSpace(gameID) == "" {
|
||||
return model.SysGameListConfig{}, nil
|
||||
}
|
||||
err := tx.Where("sys_origin = ? AND game_origin = ? AND game_id = ?", sysOrigin, baishunVendorType, strings.TrimSpace(gameID)).First(&cfg).Error
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return model.SysGameListConfig{}, nil
|
||||
}
|
||||
return cfg, err
|
||||
}
|
||||
|
||||
func (s *BaishunService) importCatalogGamesTx(tx *gorm.DB, sysOrigin string, filter map[int]struct{}) (*ImportCatalogResponse, error) {
|
||||
query := tx.Where("sys_origin = ? AND status = ?", sysOrigin, baishunCatalogEnabled)
|
||||
if len(filter) > 0 {
|
||||
ids := make([]int, 0, len(filter))
|
||||
for id := range filter {
|
||||
ids = append(ids, id)
|
||||
}
|
||||
query = query.Where("vendor_game_id IN ?", ids)
|
||||
}
|
||||
|
||||
var catalogs []model.BaishunGameCatalog
|
||||
if err := query.Order("vendor_game_id ASC").Find(&catalogs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &ImportCatalogResponse{}
|
||||
for _, catalog := range catalogs {
|
||||
vendorGameID := strconv.Itoa(catalog.VendorGameID)
|
||||
|
||||
var ext model.SysGameListVendorExt
|
||||
err := tx.Where("sys_origin = ? AND vendor_type = ? AND vendor_game_id = ?", sysOrigin, baishunVendorType, vendorGameID).First(&ext).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
if err == nil && ext.GameListConfigID > 0 {
|
||||
resp.Existing++
|
||||
continue
|
||||
}
|
||||
|
||||
var cfg model.SysGameListConfig
|
||||
err = tx.Where("sys_origin = ? AND game_origin = ? AND game_id = ?", sysOrigin, baishunVendorType, catalog.InternalGameID).First(&cfg).Error
|
||||
if err != nil && err != gorm.ErrRecordNotFound {
|
||||
return nil, err
|
||||
}
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
nextID, idErr := utils.NextID()
|
||||
if idErr != nil {
|
||||
return nil, idErr
|
||||
}
|
||||
cfg = model.SysGameListConfig{
|
||||
ID: nextID,
|
||||
SysOrigin: sysOrigin,
|
||||
GameOrigin: baishunVendorType,
|
||||
GameID: defaultIfBlank(catalog.InternalGameID, fmt.Sprintf("bs_%d", catalog.VendorGameID)),
|
||||
Name: catalog.Name,
|
||||
Category: "OTHER",
|
||||
GameCode: vendorGameID,
|
||||
Cover: defaultIfBlank(catalog.Cover, catalog.PreviewURL),
|
||||
Showcase: false,
|
||||
Sort: int64(catalog.VendorGameID),
|
||||
FullScreen: true,
|
||||
ClientOrigin: "COMMON",
|
||||
GameMode: formatAdminGameModeRaw(baishunFixedGameMode),
|
||||
}
|
||||
if err := tx.Create(&cfg).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
nextID, idErr := utils.NextID()
|
||||
if idErr != nil {
|
||||
return nil, idErr
|
||||
}
|
||||
newExt := model.SysGameListVendorExt{
|
||||
ID: nextID,
|
||||
GameListConfigID: cfg.ID,
|
||||
SysOrigin: sysOrigin,
|
||||
VendorType: baishunVendorType,
|
||||
VendorGameID: vendorGameID,
|
||||
LaunchMode: baishunLaunchModeRemote,
|
||||
PackageVersion: catalog.PackageVersion,
|
||||
PackageURL: catalog.DownloadURL,
|
||||
PreviewURL: catalog.PreviewURL,
|
||||
Orientation: catalog.Orientation,
|
||||
SafeHeight: catalog.SafeHeight,
|
||||
GSP: defaultAdminGSP(catalog.GSP, s.cfg.Baishun.GSP),
|
||||
BridgeSchemaVersion: "1.0",
|
||||
Enabled: true,
|
||||
}
|
||||
if err := tx.Create(&newExt).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp.Imported++
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (r adminGameRow) toAdminItem() AdminBaishunGameItem {
|
||||
return AdminBaishunGameItem{
|
||||
ID: r.ID,
|
||||
SysOrigin: r.SysOrigin,
|
||||
GameID: r.InternalGameID,
|
||||
VendorGameID: parseAdminVendorGameID(r.VendorGameID, r.InternalGameID),
|
||||
Name: r.Name,
|
||||
Category: r.Category,
|
||||
GameCode: r.GameCode,
|
||||
Cover: r.Cover,
|
||||
Amounts: r.Amounts,
|
||||
Showcase: r.Showcase,
|
||||
Sort: r.Sort,
|
||||
Height: r.Height,
|
||||
Width: r.Width,
|
||||
FullScreen: r.FullScreen,
|
||||
ClientOrigin: r.ClientOrigin,
|
||||
Regions: r.Regions,
|
||||
GameMode: baishunFixedGameMode,
|
||||
LaunchMode: defaultIfBlank(r.LaunchMode, baishunLaunchModeRemote),
|
||||
PackageVersion: defaultIfBlank(r.ExtPackageVersion, r.CatalogPackage),
|
||||
PreviewURL: defaultIfBlank(r.ExtPreviewURL, r.CatalogPreviewURL),
|
||||
PackageURL: defaultIfBlank(r.PackageURL, r.CatalogDownloadURL),
|
||||
Orientation: chooseAdminOrientation(r.ExtOrientation, r.CatalogOrientation),
|
||||
SafeHeight: chooseAdminSafeHeight(r.ExtSafeHeight, r.CatalogSafeHeight),
|
||||
GSP: r.ExtGSP,
|
||||
CurrencyIcon: r.CurrencyIcon,
|
||||
CatalogStatus: r.CatalogStatus,
|
||||
CreateTime: formatAdminTime(r.CreateTime),
|
||||
UpdateTime: formatAdminTime(r.UpdateTime),
|
||||
}
|
||||
}
|
||||
|
||||
func normalizeAdminSysOrigin(sysOrigin string) string {
|
||||
sysOrigin = strings.ToUpper(strings.TrimSpace(sysOrigin))
|
||||
if sysOrigin == "" {
|
||||
return "LIKEI"
|
||||
}
|
||||
return sysOrigin
|
||||
}
|
||||
|
||||
func normalizePageCursor(cursor int) int {
|
||||
if cursor <= 0 {
|
||||
return 1
|
||||
}
|
||||
return cursor
|
||||
}
|
||||
|
||||
func normalizePageLimit(limit int) int {
|
||||
if limit <= 0 {
|
||||
return 20
|
||||
}
|
||||
if limit > 200 {
|
||||
return 200
|
||||
}
|
||||
return limit
|
||||
}
|
||||
|
||||
func defaultAdminGSP(catalogGSP string, fallback int) string {
|
||||
if strings.TrimSpace(catalogGSP) != "" {
|
||||
return strings.TrimSpace(catalogGSP)
|
||||
}
|
||||
if fallback > 0 {
|
||||
return strconv.Itoa(fallback)
|
||||
}
|
||||
return "101"
|
||||
}
|
||||
|
||||
func formatAdminGameModeRaw(gameMode int) string {
|
||||
return fmt.Sprintf("[%d]", baishunFixedGameMode)
|
||||
}
|
||||
|
||||
func parseAdminVendorGameID(raw string, gameID string) int {
|
||||
if parsed, err := strconv.Atoi(strings.TrimSpace(raw)); err == nil {
|
||||
return parsed
|
||||
}
|
||||
if strings.HasPrefix(strings.TrimSpace(gameID), "bs_") {
|
||||
if parsed, err := strconv.Atoi(strings.TrimPrefix(strings.TrimSpace(gameID), "bs_")); err == nil {
|
||||
return parsed
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func chooseAdminOrientation(extOrientation, catalogOrientation *int) int {
|
||||
if extOrientation != nil {
|
||||
return *extOrientation
|
||||
}
|
||||
if catalogOrientation != nil {
|
||||
return *catalogOrientation
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func chooseAdminSafeHeight(extSafeHeight, catalogSafeHeight int) int {
|
||||
if extSafeHeight > 0 {
|
||||
return extSafeHeight
|
||||
}
|
||||
return catalogSafeHeight
|
||||
}
|
||||
|
||||
func formatAdminTime(value time.Time) string {
|
||||
if value.IsZero() {
|
||||
return ""
|
||||
}
|
||||
return value.Format(adminTimeLayout)
|
||||
}
|
||||
|
||||
func buildVendorGameIDFilter(vendorGameIDs []int) map[int]struct{} {
|
||||
filter := make(map[int]struct{}, len(vendorGameIDs))
|
||||
for _, id := range vendorGameIDs {
|
||||
if id > 0 {
|
||||
filter[id] = struct{}{}
|
||||
}
|
||||
}
|
||||
return filter
|
||||
}
|
||||
3
internal/service/baishun/game_mode.go
Normal file
3
internal/service/baishun/game_mode.go
Normal file
@ -0,0 +1,3 @@
|
||||
package baishun
|
||||
|
||||
const baishunFixedGameMode = 2
|
||||
32
internal/service/baishun/game_mode_test.go
Normal file
32
internal/service/baishun/game_mode_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
package baishun
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGameListRowGameModeAlwaysFixed(t *testing.T) {
|
||||
testCases := []string{"", "[1]", "[2]", "[3]", "CHAT_ROOM"}
|
||||
for _, raw := range testCases {
|
||||
row := gameListRow{GameModeRaw: raw}
|
||||
if got := row.gameMode(); got != baishunFixedGameMode {
|
||||
t.Fatalf("gameMode() = %d, want %d for raw %q", got, baishunFixedGameMode, raw)
|
||||
}
|
||||
if got := row.toListItem().GameMode; got != baishunFixedGameMode {
|
||||
t.Fatalf("toListItem().GameMode = %d, want %d for raw %q", got, baishunFixedGameMode, raw)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminGameModeAlwaysFixed(t *testing.T) {
|
||||
row := adminGameRow{
|
||||
GameModeRaw: "[3]",
|
||||
CatalogGameModeJSON: "[1]",
|
||||
}
|
||||
if got := row.toAdminItem().GameMode; got != baishunFixedGameMode {
|
||||
t.Fatalf("toAdminItem().GameMode = %d, want %d", got, baishunFixedGameMode)
|
||||
}
|
||||
if got := formatAdminGameModeRaw(0); got != "[2]" {
|
||||
t.Fatalf("formatAdminGameModeRaw(0) = %q, want [2]", got)
|
||||
}
|
||||
if got := formatAdminGameModeRaw(99); got != "[2]" {
|
||||
t.Fatalf("formatAdminGameModeRaw(99) = %q, want [2]", got)
|
||||
}
|
||||
}
|
||||
@ -87,10 +87,7 @@ func (r gameListRow) launchMode() string {
|
||||
|
||||
// gameMode 返回游戏模式。
|
||||
func (r gameListRow) gameMode() int {
|
||||
if parsed := pickFirstInt(r.GameModeRaw, 3); parsed > 0 {
|
||||
return parsed
|
||||
}
|
||||
return 3
|
||||
return baishunFixedGameMode
|
||||
}
|
||||
|
||||
// vendorGameIDInt 返回厂商游戏 ID 的整数值。
|
||||
|
||||
@ -99,9 +99,6 @@ func (s *BaishunService) listRoomGames(ctx context.Context, sysOrigin, roomID, c
|
||||
if strings.TrimSpace(category) != "" && !strings.EqualFold(strings.TrimSpace(category), "CHAT_ROOM") {
|
||||
query = query.Where("cfg.category = ?", category)
|
||||
}
|
||||
if strings.TrimSpace(roomID) != "" {
|
||||
query = query.Where("cfg.game_mode LIKE ?", "%CHAT_ROOM%")
|
||||
}
|
||||
if err := query.Order("cfg.sort DESC").Scan(&rows).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -133,7 +130,7 @@ func (s *BaishunService) listRoomGames(ctx context.Context, sysOrigin, roomID, c
|
||||
Sort: int64(catalog.VendorGameID),
|
||||
LaunchMode: baishunLaunchModeRemote,
|
||||
FullScreen: true,
|
||||
GameMode: pickFirstInt(catalog.GameModeJSON, 3),
|
||||
GameMode: baishunFixedGameMode,
|
||||
SafeHeight: catalog.SafeHeight,
|
||||
Orientation: derefInt(catalog.Orientation),
|
||||
PackageVersion: catalog.PackageVersion,
|
||||
@ -214,7 +211,7 @@ func (s *BaishunService) findGameRow(ctx context.Context, sysOrigin, gameID stri
|
||||
CatalogSafeHeight: catalog.SafeHeight,
|
||||
CatalogOrientation: catalog.Orientation,
|
||||
CatalogStatus: catalog.Status,
|
||||
GameModeRaw: catalog.GameModeJSON,
|
||||
GameModeRaw: formatAdminGameModeRaw(baishunFixedGameMode),
|
||||
}, nil
|
||||
}
|
||||
return gameListRow{}, NewAppError(http.StatusNotFound, "game_not_found", "room game config not found")
|
||||
|
||||
@ -181,6 +181,90 @@ type SyncCatalogResponse struct {
|
||||
Skipped int `json:"skipped"`
|
||||
}
|
||||
|
||||
// AdminBaishunGameItem 是后台管理页展示的百顺游戏配置行。
|
||||
type AdminBaishunGameItem struct {
|
||||
ID int64 `json:"id"`
|
||||
SysOrigin string `json:"sysOrigin"`
|
||||
GameID string `json:"gameId"`
|
||||
VendorGameID int `json:"vendorGameId"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
GameCode string `json:"gameCode"`
|
||||
Cover string `json:"cover"`
|
||||
Amounts string `json:"amounts"`
|
||||
Showcase bool `json:"showcase"`
|
||||
Sort int64 `json:"sort"`
|
||||
Height float64 `json:"height"`
|
||||
Width float64 `json:"width"`
|
||||
FullScreen bool `json:"fullScreen"`
|
||||
ClientOrigin string `json:"clientOrigin"`
|
||||
Regions string `json:"regions"`
|
||||
GameMode int `json:"gameMode"`
|
||||
LaunchMode string `json:"launchMode"`
|
||||
PackageVersion string `json:"packageVersion"`
|
||||
PreviewURL string `json:"previewUrl"`
|
||||
PackageURL string `json:"packageUrl"`
|
||||
Orientation int `json:"orientation"`
|
||||
SafeHeight int `json:"safeHeight"`
|
||||
GSP string `json:"gsp"`
|
||||
CurrencyIcon string `json:"currencyIcon"`
|
||||
CatalogStatus string `json:"catalogStatus"`
|
||||
CreateTime string `json:"createTime"`
|
||||
UpdateTime string `json:"updateTime"`
|
||||
}
|
||||
|
||||
// AdminBaishunGamePageResponse 是后台页分页响应。
|
||||
type AdminBaishunGamePageResponse struct {
|
||||
Cursor int `json:"cursor"`
|
||||
Limit int `json:"limit"`
|
||||
Records []AdminBaishunGameItem `json:"records"`
|
||||
Total int64 `json:"total"`
|
||||
}
|
||||
|
||||
// SaveAdminBaishunGameRequest 是后台保存百顺游戏配置的入参。
|
||||
type SaveAdminBaishunGameRequest struct {
|
||||
ID int64 `json:"id"`
|
||||
SysOrigin string `json:"sysOrigin"`
|
||||
GameID string `json:"gameId"`
|
||||
VendorGameID int `json:"vendorGameId"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
GameCode string `json:"gameCode"`
|
||||
Cover string `json:"cover"`
|
||||
Amounts string `json:"amounts"`
|
||||
Showcase bool `json:"showcase"`
|
||||
Sort int64 `json:"sort"`
|
||||
Height float64 `json:"height"`
|
||||
Width float64 `json:"width"`
|
||||
FullScreen bool `json:"fullScreen"`
|
||||
ClientOrigin string `json:"clientOrigin"`
|
||||
Regions string `json:"regions"`
|
||||
GameMode int `json:"gameMode"`
|
||||
LaunchMode string `json:"launchMode"`
|
||||
PackageVersion string `json:"packageVersion"`
|
||||
PreviewURL string `json:"previewUrl"`
|
||||
PackageURL string `json:"packageUrl"`
|
||||
Orientation *int `json:"orientation"`
|
||||
SafeHeight int `json:"safeHeight"`
|
||||
GSP string `json:"gsp"`
|
||||
CurrencyIcon string `json:"currencyIcon"`
|
||||
}
|
||||
|
||||
// ImportCatalogResponse 描述从本地目录补齐后台配置的结果。
|
||||
type ImportCatalogResponse struct {
|
||||
Existing int `json:"existing"`
|
||||
Imported int `json:"imported"`
|
||||
}
|
||||
|
||||
// SyncAdminCatalogResponse 描述同步平台目录并导入后台配置的结果。
|
||||
type SyncAdminCatalogResponse struct {
|
||||
Inserted int `json:"inserted"`
|
||||
Updated int `json:"updated"`
|
||||
Skipped int `json:"skipped"`
|
||||
Existing int `json:"existing"`
|
||||
Imported int `json:"imported"`
|
||||
}
|
||||
|
||||
// BaishunTokenRequest 是百顺拉取 token/code 回调入参。
|
||||
type BaishunTokenRequest struct {
|
||||
AppID int64 `json:"app_id"`
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user