package baishun import ( "bytes" "chatapp3-golang/internal/model" "chatapp3-golang/internal/utils" "context" "encoding/json" "fmt" "io" "net/http" "strconv" "strings" "time" "gorm.io/gorm/clause" ) // SyncCatalog 从百顺平台拉取游戏目录并同步到本地表。 func (s *BaishunService) SyncCatalog(ctx context.Context, req SyncCatalogRequest) (*SyncCatalogResponse, error) { if strings.TrimSpace(s.cfg.Baishun.PlatformBaseURL) == "" || s.cfg.Baishun.AppID == 0 { return nil, NewAppError(http.StatusBadRequest, "baishun_config_missing", "baishun platform config is missing") } sysOrigin := defaultIfBlank(req.SysOrigin, "LIKEI") if strings.TrimSpace(sysOrigin) == "" { sysOrigin = "LIKEI" } games, err := s.fetchPlatformGames(ctx) if err != nil { return nil, err } filter := make(map[int]struct{}, len(req.VendorGameIDs)) for _, id := range req.VendorGameIDs { filter[id] = struct{}{} } resp := &SyncCatalogResponse{} for _, game := range games { if len(filter) > 0 { if _, ok := filter[game.GameID]; !ok { resp.Skipped++ continue } } id, err := utils.NextID() if err != nil { return nil, err } record := model.BaishunGameCatalog{ ID: id, SysOrigin: sysOrigin, InternalGameID: fmt.Sprintf("bs_%d", game.GameID), VendorGameID: game.GameID, Name: game.Name, Cover: defaultIfBlank(game.PreviewURL, game.DownloadURL), PreviewURL: game.PreviewURL, DownloadURL: game.DownloadURL, PackageVersion: game.GameVersion, GameModeJSON: utils.MustJSONString(game.GameMode, ""), Orientation: intPtr(game.GameOrientation), SafeHeight: game.SafeHeight, VenueLevelJSON: utils.MustJSONString(game.VenueLevel, ""), GSP: strconv.Itoa(s.cfg.Baishun.GSP), RawJSON: utils.MustJSONString(game, ""), Status: baishunCatalogEnabled, CreateTime: time.Now(), UpdateTime: time.Now(), } err = s.repo.DB.WithContext(ctx).Clauses(clause.OnConflict{ Columns: []clause.Column{{Name: "sys_origin"}, {Name: "vendor_game_id"}}, DoUpdates: clause.AssignmentColumns([]string{ "internal_game_id", "name", "cover", "preview_url", "download_url", "package_version", "game_mode_json", "orientation", "safe_height", "venue_level_json", "gsp", "raw_json", "status", "update_time", }), }).Create(&record).Error if err != nil { return nil, err } if req.Force { resp.Updated++ } else { resp.Inserted++ } } return resp, nil } // fetchPlatformGames 从百顺平台拉取游戏目录。 func (s *BaishunService) fetchPlatformGames(ctx context.Context) ([]baishunPlatformGame, error) { timestamp := time.Now().Unix() nonce := s.randomNonce() payload := map[string]any{ "game_list_type": baishunGameListTypeGame, "app_channel": s.cfg.Baishun.AppChannel, "app_id": s.cfg.Baishun.AppID, "signature": s.signBAISHUN(timestamp, nonce), "signature_nonce": nonce, "timestamp": timestamp, } if strings.TrimSpace(s.cfg.Baishun.AppName) != "" { payload["app_name"] = s.cfg.Baishun.AppName } body, _ := json.Marshal(payload) req, err := http.NewRequestWithContext(ctx, http.MethodPost, strings.TrimRight(s.cfg.Baishun.PlatformBaseURL, "/")+"/v1/api/gamelist", bytes.NewReader(body)) if err != nil { return nil, err } req.Header.Set("Content-Type", "application/json") resp, err := s.httpClient.Do(req) if err != nil { return nil, err } defer resp.Body.Close() raw, err := io.ReadAll(resp.Body) if err != nil { return nil, err } if resp.StatusCode >= http.StatusBadRequest { return nil, fmt.Errorf("baishun platform failed: status=%d body=%s", resp.StatusCode, strings.TrimSpace(string(raw))) } var result baishunPlatformResponse if err := json.Unmarshal(raw, &result); err != nil { return nil, err } if result.Code != 0 { return nil, fmt.Errorf("baishun platform failed: code=%d message=%s", result.Code, result.Message) } return result.Data, nil }