package storage import ( "context" "database/sql" "strconv" "strings" ) func (r *MySQLRepository) ResolveWalletGame(ctx context.Context, sysOrigin string, eventType string, eventID string) (string, string, string, error) { if provider, gameID, fallback, ok := directGameMeta(eventType); ok { gameName, err := r.lookupGameName(ctx, sysOrigin, provider, gameID) if gameName == "" { gameName = fallback } return provider, gameID, gameName, err } if provider, gameID, gameName, ok, err := r.resolveGameOpenByWalletEvent(ctx, sysOrigin, eventType, eventID); ok || err != nil { return provider, gameID, gameName, err } if provider, gameID, gameName, ok, err := r.resolveBaishunByWalletEvent(ctx, sysOrigin, eventID); ok || err != nil { return provider, gameID, gameName, err } provider, gameID, fallback := fallbackGameMeta(eventType) gameName, err := r.lookupGameName(ctx, sysOrigin, provider, gameID) if err != nil { return provider, gameID, fallback, err } if gameName == "" { gameName = fallback } return provider, gameID, gameName, nil } func (r *MySQLRepository) resolveBaishunByWalletEvent(ctx context.Context, sysOrigin string, eventID string) (string, string, string, bool, error) { if strings.TrimSpace(eventID) == "" { return "", "", "", false, nil } var vendorID int64 err := r.db.QueryRowContext(ctx, ` SELECT vendor_game_id FROM baishun_order_idempotency WHERE sys_origin = ? AND wallet_event_id = ? LIMIT 1 `, sysOrigin, eventID).Scan(&vendorID) if err == sql.ErrNoRows { return "", "", "", false, nil } if err != nil { return "", "", "", false, err } gameID := strconv.FormatInt(vendorID, 10) gameName, err := r.lookupGameName(ctx, sysOrigin, "BAISHUN", gameID) if gameName == "" { gameName = gameID } return "BAISHUN", gameID, gameName, true, err } func (r *MySQLRepository) resolveGameOpenByWalletEvent(ctx context.Context, sysOrigin string, eventType string, eventID string) (string, string, string, bool, error) { orderID := strings.TrimPrefix(eventID, "GAME_OPEN:") if orderID == eventID && !strings.HasPrefix(eventType, "GAME_OPEN_") { return "", "", "", false, nil } if orderID != "" && orderID != eventID { var gameID string err := r.db.QueryRowContext(ctx, ` SELECT game_id FROM game_open_callback_log WHERE sys_origin = ? AND order_id = ? LIMIT 1 `, sysOrigin, orderID).Scan(&gameID) if err != nil && err != sql.ErrNoRows { return "", "", "", false, err } if err == nil && strings.TrimSpace(gameID) != "" { gameName, err := r.lookupGameName(ctx, sysOrigin, "GAME_OPEN", gameID) if gameName == "" { gameName = gameID } return "GAME_OPEN", gameID, gameName, true, err } } if strings.HasPrefix(eventType, "GAME_OPEN_") { gameID := strings.TrimPrefix(eventType, "GAME_OPEN_") gameName, err := r.lookupGameName(ctx, sysOrigin, "GAME_OPEN", gameID) if gameName == "" { gameName = gameID } return "GAME_OPEN", gameID, gameName, true, err } return "", "", "", false, nil } func (r *MySQLRepository) lookupGameName(ctx context.Context, sysOrigin string, provider string, gameID string) (string, error) { cacheKey := strings.Join([]string{sysOrigin, provider, gameID}, "|") if name, ok := r.gameNameCache.Get(cacheKey); ok { return name, nil } var name string if provider == "BAISHUN" { err := r.db.QueryRowContext(ctx, ` SELECT name FROM baishun_game_catalog WHERE sys_origin = ? AND profile = 'PROD' AND vendor_game_id = ? LIMIT 1 `, sysOrigin, gameID).Scan(&name) if err == nil || err != sql.ErrNoRows { if err == nil { r.gameNameCache.Set(cacheKey, name) } return name, err } } err := r.db.QueryRowContext(ctx, ` SELECT name FROM lingxian_game_catalog WHERE sys_origin = ? AND profile = 'PROD' AND vendor_game_id = ? LIMIT 1 `, sysOrigin, gameID).Scan(&name) if err == nil || err != sql.ErrNoRows { if err == nil { r.gameNameCache.Set(cacheKey, name) } return name, err } err = r.db.QueryRowContext(ctx, ` SELECT name FROM sys_game_list_config WHERE sys_origin = ? AND game_id = ? LIMIT 1 `, sysOrigin, gameID).Scan(&name) if err == sql.ErrNoRows { r.gameNameCache.Set(cacheKey, "") return "", nil } if err == nil { r.gameNameCache.Set(cacheKey, name) } return name, err } func directGameMeta(eventType string) (string, string, string, bool) { switch { case strings.HasPrefix(eventType, "BAISHUN_GAME_"), strings.HasPrefix(eventType, "GAME_OPEN_"), strings.HasPrefix(eventType, "YOMI_GAME_"), strings.HasPrefix(eventType, "HKYS_GAME_"), strings.HasPrefix(eventType, "HOT_GAME_"), in(eventType, "FRUIT_GAME", "DOUBLE_LAYER_FRUIT", "GAME_FRUIT_BET", "GAME_FRUIT_AWARD", "LUDO_GAME", "LUDO_VICTORY", "LUDO_REFUND", "GAME_LUDO_REFUND", "GAME_BARBECUE", "DOUBLE_LAYER_BARBECUE", "GAME_SLOT_MACHINE", "TURNTABLE_GAME", "GAME_BURST_CRYSTAL", "TEEN_PATTI", "BET_TEEN_PATTI", "LOTTERY_NUMBER_BET", "LOTTERY_REWARD"): provider, gameID, fallback := fallbackGameMeta(eventType) return provider, gameID, fallback, true default: return "", "", "", false } } func fallbackGameMeta(eventType string) (string, string, string) { switch { case strings.HasPrefix(eventType, "BAISHUN_GAME_"): return "BAISHUN", strings.TrimPrefix(eventType, "BAISHUN_GAME_"), strings.TrimPrefix(eventType, "BAISHUN_GAME_") case strings.HasPrefix(eventType, "YOMI_GAME_"): return "YOMI", strings.TrimPrefix(eventType, "YOMI_GAME_"), strings.TrimPrefix(eventType, "YOMI_GAME_") case strings.HasPrefix(eventType, "HKYS_GAME_"): return "HKYS", strings.TrimPrefix(eventType, "HKYS_GAME_"), strings.TrimPrefix(eventType, "HKYS_GAME_") case strings.HasPrefix(eventType, "HOT_GAME_"): return "HOT", strings.TrimPrefix(eventType, "HOT_GAME_"), strings.TrimPrefix(eventType, "HOT_GAME_") case in(eventType, "FRUIT_GAME", "DOUBLE_LAYER_FRUIT", "GAME_FRUIT_BET", "GAME_FRUIT_AWARD"): return "INTERNAL", "FRUIT_GAME", "Fruit Game" case in(eventType, "LUDO_GAME", "LUDO_VICTORY", "LUDO_REFUND", "GAME_LUDO_REFUND"): return "INTERNAL", "LUDO_GAME", "Ludo Game" case in(eventType, "GAME_BARBECUE", "DOUBLE_LAYER_BARBECUE"): return "INTERNAL", "BARBECUE_GAME", "Barbecue Game" case eventType == "GAME_SLOT_MACHINE": return "INTERNAL", "SLOT_MACHINE", "Slot Machine" case eventType == "TURNTABLE_GAME": return "INTERNAL", "TURNTABLE_GAME", "Turntable Game" case eventType == "GAME_BURST_CRYSTAL": return "INTERNAL", "BURST_CRYSTAL", "Burst Crystal" case in(eventType, "TEEN_PATTI", "BET_TEEN_PATTI"): return "INTERNAL", "TEEN_PATTI", "Teen Patti" case in(eventType, "LOTTERY_NUMBER_BET", "LOTTERY_REWARD"): return "INTERNAL", "LOTTERY_NUMBER", "Lottery Number" default: return "INTERNAL", eventType, eventType } } func in(value string, candidates ...string) bool { for _, candidate := range candidates { if value == candidate { return true } } return false }