百顺游戏测试页
+https://api-test.global-interaction.com;本地代理打开时 API Base URL 留空即可diff --git a/server/admin/internal/modules/gamemanagement/sync_games.go b/server/admin/internal/modules/gamemanagement/sync_games.go index b6a071a7..f87f7c02 100644 --- a/server/admin/internal/modules/gamemanagement/sync_games.go +++ b/server/admin/internal/modules/gamemanagement/sync_games.go @@ -26,7 +26,7 @@ const ( adapterBaishunV1 = "baishun_v1" defaultGameStatus = "disabled" defaultGameCategory = "casino" - defaultLaunchMode = "h5_popup" + defaultLaunchMode = "full_screen" defaultOrientation = "portrait" ) @@ -172,8 +172,8 @@ func fetchBaishunGameSyncPlan(ctx context.Context, platform *gamev1.GamePlatform appChannel := config.appChannel() appKey := strings.TrimSpace(platform.GetCallbackSecret()) endpoint := baishunGameListEndpoint(platform.GetApiBaseUrl(), config) - if appID == 0 || appChannel == "" || appKey == "" || endpoint == "" { - return providerGameSyncPlan{}, fmt.Errorf("百顺列表同步需要 app_id、app_channel、回调密钥和 provider_api_base_url/game_list_url") + if missing := missingBaishunSyncFields(appID, appChannel, appKey, endpoint); len(missing) > 0 { + return providerGameSyncPlan{}, fmt.Errorf("百顺列表同步缺少配置: %s", strings.Join(missing, "、")) } body := baishunSignedListRequest(appID, appChannel, baishunGameListType(config, req), appKey) raw, err := json.Marshal(body) @@ -225,6 +225,23 @@ func decodeBaishunSyncConfig(raw string) (baishunSyncConfig, error) { return config, nil } +func missingBaishunSyncFields(appID int64, appChannel string, appKey string, endpoint string) []string { + missing := make([]string, 0, 4) + if appID == 0 { + missing = append(missing, "adapterConfigJson.app_id") + } + if strings.TrimSpace(appChannel) == "" { + missing = append(missing, "adapterConfigJson.app_channel") + } + if strings.TrimSpace(appKey) == "" { + missing = append(missing, "回调密钥") + } + if strings.TrimSpace(endpoint) == "" { + missing = append(missing, "adapterConfigJson.game_list_url 或 provider_api_base_url") + } + return missing +} + func (c baishunSyncConfig) appID() int64 { if c.AppID != 0 { return c.AppID diff --git a/server/admin/internal/modules/gamemanagement/sync_games_test.go b/server/admin/internal/modules/gamemanagement/sync_games_test.go index 0aeb5456..ba0942a0 100644 --- a/server/admin/internal/modules/gamemanagement/sync_games_test.go +++ b/server/admin/internal/modules/gamemanagement/sync_games_test.go @@ -55,7 +55,7 @@ func TestFetchBaishunGameSyncPlanFetchesSignsAndFormatsCatalog(t *testing.T) { if game.GameID != "baishun_1006" || game.ProviderGameID != "1006" || game.GameName != "Gold of Test" { t.Fatalf("catalog identity mismatch: %+v", game) } - if game.IconURL != "https://cdn.test/1006.png" || game.CoverURL != "https://cdn.test/1006.png" || game.Orientation != "landscape" || game.Status != "disabled" { + if game.IconURL != "https://cdn.test/1006.png" || game.CoverURL != "https://cdn.test/1006.png" || game.Orientation != "landscape" || game.Status != "disabled" || game.LaunchMode != "full_screen" { t.Fatalf("catalog presentation mismatch: %+v", game) } var merged struct { @@ -81,3 +81,21 @@ func TestSelectProviderGamesKeepsOnlySelectedIDs(t *testing.T) { t.Fatalf("selected games mismatch: %+v", selected) } } + +func TestMissingBaishunSyncFieldsNamesExactInputs(t *testing.T) { + missing := missingBaishunSyncFields(0, "", "", "") + want := []string{ + "adapterConfigJson.app_id", + "adapterConfigJson.app_channel", + "回调密钥", + "adapterConfigJson.game_list_url 或 provider_api_base_url", + } + if len(missing) != len(want) { + t.Fatalf("missing len = %d, want %d: %+v", len(missing), len(want), missing) + } + for index := range want { + if missing[index] != want[index] { + t.Fatalf("missing[%d] = %q, want %q", index, missing[index], want[index]) + } + } +} diff --git a/services/game-service/internal/domain/game/game.go b/services/game-service/internal/domain/game/game.go index b428656c..f1639eb6 100644 --- a/services/game-service/internal/domain/game/game.go +++ b/services/game-service/internal/domain/game/game.go @@ -7,8 +7,11 @@ const ( SceneVoiceRoom = "voice_room" - LaunchModeH5Popup = "h5_popup" - SessionActive = "active" + LaunchModeFullScreen = "full_screen" + LaunchModeHalfScreen = "half_screen" + LaunchModeThreeQuarterScreen = "three_quarter_screen" + LaunchModeH5Popup = "h5_popup" + SessionActive = "active" AdapterDemo = "demo" AdapterYomiV4 = "yomi_v4" diff --git a/services/game-service/internal/transport/grpc/server.go b/services/game-service/internal/transport/grpc/server.go index 16cfd967..9c911281 100644 --- a/services/game-service/internal/transport/grpc/server.go +++ b/services/game-service/internal/transport/grpc/server.go @@ -367,7 +367,7 @@ func normalizeCatalog(item gamedomain.CatalogItem) (gamedomain.CatalogItem, erro item.GameName = strings.TrimSpace(item.GameName) item.Status = normalizeStatus(item.Status) if item.LaunchMode == "" { - item.LaunchMode = gamedomain.LaunchModeH5Popup + item.LaunchMode = gamedomain.LaunchModeFullScreen } if item.Orientation == "" { item.Orientation = "portrait" diff --git a/tools/baishun-game-test-server.mjs b/tools/baishun-game-test-server.mjs new file mode 100644 index 00000000..5db3e6be --- /dev/null +++ b/tools/baishun-game-test-server.mjs @@ -0,0 +1,79 @@ +import http from "node:http"; +import { readFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const htmlPath = join(__dirname, "baishun-game-test.html"); +const port = Number(process.env.PORT || 8787); +const upstreamBase = (process.env.BAISHUN_API_BASE || "https://api-test.global-interaction.com").replace(/\/+$/, ""); + +function readBody(req) { + return new Promise((resolve, reject) => { + const chunks = []; + req.on("data", (chunk) => chunks.push(chunk)); + req.on("end", () => resolve(Buffer.concat(chunks))); + req.on("error", reject); + }); +} + +function copyProxyHeaders(req) { + const headers = new Headers(); + for (const [key, value] of Object.entries(req.headers)) { + if (!value) continue; + const lower = key.toLowerCase(); + if (["host", "origin", "referer", "connection", "content-length"].includes(lower)) continue; + headers.set(key, Array.isArray(value) ? value.join(", ") : value); + } + return headers; +} + +async function proxyApi(req, res) { + const targetUrl = `${upstreamBase}${req.url}`; + const body = req.method === "GET" || req.method === "HEAD" ? undefined : await readBody(req); + const upstream = await fetch(targetUrl, { + method: req.method, + headers: copyProxyHeaders(req), + body, + redirect: "manual", + }); + const responseBody = Buffer.from(await upstream.arrayBuffer()); + res.writeHead(upstream.status, { + "Content-Type": upstream.headers.get("content-type") || "application/octet-stream", + "Cache-Control": "no-store", + }); + res.end(responseBody); +} + +const server = http.createServer(async (req, res) => { + try { + if (req.method === "OPTIONS") { + res.writeHead(204); + res.end(); + return; + } + if (req.url === "/" || req.url === "/baishun-game-test.html") { + const html = await readFile(htmlPath); + res.writeHead(200, { + "Content-Type": "text/html; charset=utf-8", + "Cache-Control": "no-store", + }); + res.end(html); + return; + } + if (req.url?.startsWith("/api/")) { + await proxyApi(req, res); + return; + } + res.writeHead(404, { "Content-Type": "text/plain; charset=utf-8" }); + res.end("not found"); + } catch (err) { + res.writeHead(502, { "Content-Type": "text/plain; charset=utf-8" }); + res.end(err instanceof Error ? err.message : String(err)); + } +}); + +server.listen(port, "127.0.0.1", () => { + console.log(`Baishun game test page: http://127.0.0.1:${port}`); + console.log(`Proxy upstream: ${upstreamBase}`); +}); diff --git a/tools/baishun-game-test.html b/tools/baishun-game-test.html new file mode 100644 index 00000000..d5295855 --- /dev/null +++ b/tools/baishun-game-test.html @@ -0,0 +1,370 @@ + + +
+ + +https://api-test.global-interaction.com;本地代理打开时 API Base URL 留空即可