2026-06-11 18:14:04 +08:00

517 lines
20 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package gameapi
import (
"fmt"
gamev1 "hyapp.local/api/proto/game/v1"
)
type gameListData struct {
Games []gameItemData `json:"games"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type gameItemData struct {
GameID string `json:"game_id"`
PlatformCode string `json:"platform_code"`
NameKey string `json:"name_key"`
Name string `json:"name"`
IconURL string `json:"icon_url"`
CoverURL string `json:"cover_url"`
Category string `json:"category"`
LaunchMode string `json:"launch_mode"`
Orientation string `json:"orientation"`
SafeHeight int32 `json:"safe_height"`
SafeHeightV2 int32 `json:"safeHeight"`
MinCoin int64 `json:"min_coin"`
Enabled bool `json:"enabled"`
Maintenance bool `json:"maintenance"`
SortOrder int32 `json:"sort_order"`
}
type exploreWinnerListData struct {
Items []exploreWinnerItemData `json:"items"`
Total int `json:"total"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type exploreWinnerItemData struct {
WinID string `json:"win_id"`
GameCode string `json:"game_code"`
GameID string `json:"game_id"`
UserID string `json:"user_id"`
UserIDNumber int64 `json:"user_id_number"`
DisplayName string `json:"display_name"`
AvatarURL string `json:"avatar_url"`
CoinAmount int64 `json:"coin_amount"`
WonAtMS int64 `json:"won_at_ms"`
}
type gameLaunchData struct {
SessionID string `json:"session_id"`
LaunchURL string `json:"launch_url"`
ExpiresAtMS int64 `json:"expires_at_ms"`
Orientation string `json:"orientation"`
SafeHeight int32 `json:"safe_height"`
SafeHeightV2 int32 `json:"safeHeight"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type gameBridgeScriptData struct {
BridgeScriptURL string `json:"bridge_script_url"`
BridgeScriptURLV2 string `json:"bridgeScriptUrl"`
BridgeScriptVersion string `json:"bridge_script_version"`
BridgeScriptVersionV2 string `json:"bridgeScriptVersion"`
BridgeScriptSHA256 string `json:"bridge_script_sha256"`
BridgeScriptSHA256V2 string `json:"bridgeScriptSha256"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type diceMatchData struct {
Match diceMatchItemData `json:"match"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type diceConfigData struct {
Config diceConfigItemData `json:"config"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomRPSConfigData struct {
Config roomRPSConfigItemData `json:"config"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomRPSChallengeData struct {
Challenge roomRPSChallengeItemData `json:"challenge"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type roomRPSChallengeListData struct {
Challenges []*roomRPSChallengeItemData `json:"challenges"`
NextCursor string `json:"next_cursor"`
PageSize int32 `json:"page_size"`
ServerTimeMS int64 `json:"server_time_ms"`
}
type diceConfigItemData struct {
AppCode string `json:"app_code"`
GameID string `json:"game_id"`
Status string `json:"status"`
StakeOptions []diceStakeOptionData `json:"stake_options"`
FeeBPS int32 `json:"fee_bps"`
PoolBPS int32 `json:"pool_bps"`
MinPlayers int32 `json:"min_players"`
MaxPlayers int32 `json:"max_players"`
RobotEnabled bool `json:"robot_enabled"`
RobotMatchWaitMS int64 `json:"robot_match_wait_ms"`
PoolBalanceCoin int64 `json:"pool_balance_coin"`
}
type diceStakeOptionData struct {
StakeCoin int64 `json:"stake_coin"`
Enabled bool `json:"enabled"`
SortOrder int32 `json:"sort_order"`
}
type roomRPSConfigItemData struct {
AppCode string `json:"app_code"`
GameID string `json:"game_id"`
Status string `json:"status"`
ChallengeTimeoutMS int64 `json:"challenge_timeout_ms"`
RevealCountdownMS int64 `json:"reveal_countdown_ms"`
StakeGifts []roomRPSStakeGiftData `json:"stake_gifts"`
}
type roomRPSStakeGiftData struct {
GiftID string `json:"gift_id"`
GiftIDNumber int64 `json:"gift_id_number"`
GiftName string `json:"gift_name"`
GiftIconURL string `json:"gift_icon_url"`
GiftPriceCoin int64 `json:"gift_price_coin"`
Enabled bool `json:"enabled"`
SortOrder int32 `json:"sort_order"`
}
type diceMatchItemData struct {
AppCode string `json:"app_code"`
MatchID string `json:"match_id"`
GameID string `json:"game_id"`
RoomID string `json:"room_id"`
RegionID int64 `json:"region_id"`
MinPlayers int32 `json:"min_players"`
MaxPlayers int32 `json:"max_players"`
CurrentPlayers int32 `json:"current_players"`
StakeCoin int64 `json:"stake_coin"`
RoundNo int32 `json:"round_no"`
Status string `json:"status"`
Result string `json:"result"`
Participants []diceParticipantItemData `json:"participants"`
JoinDeadlineMS int64 `json:"join_deadline_ms"`
ReadyAtMS int64 `json:"ready_at_ms"`
CreatedAtMS int64 `json:"created_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
SettledAtMS int64 `json:"settled_at_ms"`
CanceledAtMS int64 `json:"canceled_at_ms"`
Phase string `json:"phase"`
PhaseDeadlineMS int64 `json:"phase_deadline_ms"`
FeeBPS int32 `json:"fee_bps"`
PoolBPS int32 `json:"pool_bps"`
MatchMode string `json:"match_mode"`
ForcedResult string `json:"forced_result"`
PoolDeltaCoin int64 `json:"pool_delta_coin"`
}
type diceParticipantItemData struct {
UserID string `json:"user_id"`
UserIDNumber int64 `json:"user_id_number"`
SeatNo int32 `json:"seat_no"`
Status string `json:"status"`
StakeCoin int64 `json:"stake_coin"`
DicePoints []int32 `json:"dice_points"`
RPSGesture string `json:"rps_gesture"`
Gesture string `json:"gesture"`
Result string `json:"result"`
PayoutCoin int64 `json:"payout_coin"`
BalanceAfter int64 `json:"balance_after"`
JoinedAtMS int64 `json:"joined_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
ParticipantType string `json:"participant_type"`
IsRobot bool `json:"is_robot"`
User diceUserData `json:"user"`
AvatarFrame map[string]any `json:"avatar_frame"`
}
type diceUserData struct {
UserID string `json:"user_id"`
DisplayUserID string `json:"display_user_id"`
Nickname string `json:"nickname"`
Avatar string `json:"avatar"`
}
type roomRPSChallengeItemData struct {
AppCode string `json:"app_code"`
ChallengeID string `json:"challenge_id"`
RoomID string `json:"room_id"`
RegionID int64 `json:"region_id"`
Status string `json:"status"`
StakeGiftID string `json:"stake_gift_id"`
StakeGiftIDNumber int64 `json:"stake_gift_id_number"`
StakeCoin int64 `json:"stake_coin"`
Initiator roomRPSPlayerItemData `json:"initiator"`
Challenger roomRPSPlayerItemData `json:"challenger"`
WinnerUserID string `json:"winner_user_id"`
WinnerUserIDNumber int64 `json:"winner_user_id_number"`
SettlementStatus string `json:"settlement_status"`
FailureReason string `json:"failure_reason"`
TimeoutAtMS int64 `json:"timeout_at_ms"`
RevealAtMS int64 `json:"reveal_at_ms"`
CreatedAtMS int64 `json:"created_at_ms"`
MatchedAtMS int64 `json:"matched_at_ms"`
SettledAtMS int64 `json:"settled_at_ms"`
UpdatedAtMS int64 `json:"updated_at_ms"`
}
type roomRPSPlayerItemData struct {
UserID string `json:"user_id"`
UserIDNumber int64 `json:"user_id_number"`
Gesture string `json:"gesture"`
Result string `json:"result"`
BalanceAfter int64 `json:"balance_after"`
JoinedAtMS int64 `json:"joined_at_ms"`
User diceUserData `json:"user"`
}
func gameListDataFromProto(resp *gamev1.ListGamesResponse) gameListData {
if resp == nil {
return gameListData{}
}
items := make([]gameItemData, 0, len(resp.GetGames()))
for _, item := range resp.GetGames() {
items = append(items, gameItemData{
GameID: item.GetGameId(),
PlatformCode: item.GetPlatformCode(),
NameKey: item.GetNameKey(),
Name: item.GetName(),
IconURL: item.GetIconUrl(),
CoverURL: item.GetCoverUrl(),
Category: item.GetCategory(),
LaunchMode: item.GetLaunchMode(),
Orientation: item.GetOrientation(),
SafeHeight: item.GetSafeHeight(),
SafeHeightV2: item.GetSafeHeight(),
MinCoin: item.GetMinCoin(),
Enabled: item.GetEnabled(),
Maintenance: item.GetMaintenance(),
SortOrder: item.GetSortOrder(),
})
}
return gameListData{Games: items, ServerTimeMS: resp.GetServerTimeMs()}
}
func exploreWinnerListDataFromProto(resp *gamev1.ListExploreWinnersResponse) exploreWinnerListData {
if resp == nil {
return exploreWinnerListData{}
}
items := make([]exploreWinnerItemData, 0, len(resp.GetWinners()))
for _, item := range resp.GetWinners() {
items = append(items, exploreWinnerItemData{
WinID: item.GetWinId(),
GameCode: item.GetGameCode(),
GameID: item.GetGameId(),
UserID: int64String(item.GetUserId()),
UserIDNumber: item.GetUserId(),
DisplayName: item.GetDisplayName(),
AvatarURL: item.GetAvatarUrl(),
CoinAmount: item.GetCoinAmount(),
WonAtMS: item.GetWonAtMs(),
})
}
return exploreWinnerListData{Items: items, Total: len(items), ServerTimeMS: resp.GetServerTimeMs()}
}
func gameLaunchDataFromProto(resp *gamev1.LaunchGameResponse) gameLaunchData {
if resp == nil {
return gameLaunchData{}
}
return gameLaunchData{
SessionID: resp.GetSessionId(),
LaunchURL: resp.GetLaunchUrl(),
ExpiresAtMS: resp.GetExpiresAtMs(),
Orientation: resp.GetOrientation(),
SafeHeight: resp.GetSafeHeight(),
SafeHeightV2: resp.GetSafeHeight(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func diceConfigDataFromProto(resp *gamev1.DiceConfigResponse) diceConfigData {
if resp == nil {
return diceConfigData{}
}
config := resp.GetConfig()
options := make([]diceStakeOptionData, 0, len(config.GetStakeOptions()))
for _, option := range config.GetStakeOptions() {
options = append(options, diceStakeOptionData{
StakeCoin: option.GetStakeCoin(),
Enabled: option.GetEnabled(),
SortOrder: option.GetSortOrder(),
})
}
return diceConfigData{
Config: diceConfigItemData{
AppCode: config.GetAppCode(),
GameID: config.GetGameId(),
Status: config.GetStatus(),
StakeOptions: options,
FeeBPS: config.GetFeeBps(),
PoolBPS: config.GetPoolBps(),
MinPlayers: config.GetMinPlayers(),
MaxPlayers: config.GetMaxPlayers(),
RobotEnabled: config.GetRobotEnabled(),
RobotMatchWaitMS: config.GetRobotMatchWaitMs(),
PoolBalanceCoin: config.GetPoolBalanceCoin(),
},
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomRPSConfigDataFromProto(resp *gamev1.RoomRPSConfigResponse) roomRPSConfigData {
if resp == nil {
return roomRPSConfigData{}
}
// 配置响应保持一层 config 包装,方便 Flutter 在同一个模型里同时读取配置和 server_time_ms 做倒计时校准。
return roomRPSConfigData{Config: roomRPSConfigItemDataFromProto(resp.GetConfig()), ServerTimeMS: resp.GetServerTimeMs()}
}
func roomRPSConfigItemDataFromProto(config *gamev1.RoomRPSConfig) roomRPSConfigItemData {
if config == nil {
return roomRPSConfigItemData{}
}
gifts := make([]roomRPSStakeGiftData, 0, len(config.GetStakeGifts()))
for _, gift := range config.GetStakeGifts() {
// 礼物 ID 同时返回字符串和数字Flutter 展示、缓存和路由参数优先用字符串,提交 body 时仍可用数字字段。
gifts = append(gifts, roomRPSStakeGiftData{
GiftID: int64String(gift.GetGiftId()),
GiftIDNumber: gift.GetGiftId(),
GiftName: gift.GetGiftName(),
GiftIconURL: gift.GetGiftIconUrl(),
GiftPriceCoin: gift.GetGiftPriceCoin(),
Enabled: gift.GetEnabled(),
SortOrder: gift.GetSortOrder(),
})
}
return roomRPSConfigItemData{
AppCode: config.GetAppCode(),
GameID: config.GetGameId(),
Status: config.GetStatus(),
ChallengeTimeoutMS: config.GetChallengeTimeoutMs(),
RevealCountdownMS: config.GetRevealCountdownMs(),
StakeGifts: gifts,
}
}
func diceMatchDataFromProto(resp *gamev1.DiceMatchResponse) diceMatchData {
if resp == nil {
return diceMatchData{}
}
return diceMatchData{
Match: diceMatchItemDataFromProto(resp.GetMatch()),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func diceMatchItemDataFromProto(match *gamev1.DiceMatch) diceMatchItemData {
if match == nil {
return diceMatchItemData{}
}
participants := make([]diceParticipantItemData, 0, len(match.GetParticipants()))
for _, participant := range match.GetParticipants() {
participants = append(participants, diceParticipantItemDataFromProto(participant))
}
return diceMatchItemData{
AppCode: match.GetAppCode(),
MatchID: match.GetMatchId(),
GameID: match.GetGameId(),
RoomID: match.GetRoomId(),
RegionID: match.GetRegionId(),
MinPlayers: match.GetMinPlayers(),
MaxPlayers: match.GetMaxPlayers(),
CurrentPlayers: match.GetCurrentPlayers(),
StakeCoin: match.GetStakeCoin(),
RoundNo: match.GetRoundNo(),
Status: match.GetStatus(),
Result: match.GetResult(),
Participants: participants,
JoinDeadlineMS: match.GetJoinDeadlineMs(),
ReadyAtMS: match.GetReadyAtMs(),
CreatedAtMS: match.GetCreatedAtMs(),
UpdatedAtMS: match.GetUpdatedAtMs(),
SettledAtMS: match.GetSettledAtMs(),
CanceledAtMS: match.GetCanceledAtMs(),
Phase: match.GetPhase(),
PhaseDeadlineMS: match.GetPhaseDeadlineMs(),
FeeBPS: match.GetFeeBps(),
PoolBPS: match.GetPoolBps(),
MatchMode: match.GetMatchMode(),
ForcedResult: match.GetForcedResult(),
PoolDeltaCoin: match.GetPoolDeltaCoin(),
}
}
func diceParticipantItemDataFromProto(participant *gamev1.DiceParticipant) diceParticipantItemData {
if participant == nil {
return diceParticipantItemData{}
}
return diceParticipantItemData{
UserID: int64String(participant.GetUserId()),
UserIDNumber: participant.GetUserId(),
SeatNo: participant.GetSeatNo(),
Status: participant.GetStatus(),
StakeCoin: participant.GetStakeCoin(),
DicePoints: participant.GetDicePoints(),
RPSGesture: participant.GetRpsGesture(),
Gesture: participant.GetRpsGesture(),
Result: participant.GetResult(),
PayoutCoin: participant.GetPayoutCoin(),
BalanceAfter: participant.GetBalanceAfter(),
JoinedAtMS: participant.GetJoinedAtMs(),
UpdatedAtMS: participant.GetUpdatedAtMs(),
ParticipantType: participant.GetParticipantType(),
IsRobot: participant.GetIsRobot(),
AvatarFrame: map[string]any{},
}
}
func gameBridgeScriptDataFromProto(resp *gamev1.GetBridgeScriptResponse) gameBridgeScriptData {
if resp == nil {
return gameBridgeScriptData{}
}
config := resp.GetConfig()
return gameBridgeScriptData{
BridgeScriptURL: config.GetBridgeScriptUrl(),
BridgeScriptURLV2: config.GetBridgeScriptUrl(),
BridgeScriptVersion: config.GetBridgeScriptVersion(),
BridgeScriptVersionV2: config.GetBridgeScriptVersion(),
BridgeScriptSHA256: config.GetBridgeScriptSha256(),
BridgeScriptSHA256V2: config.GetBridgeScriptSha256(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func int64String(value int64) string {
return fmt.Sprintf("%d", value)
}
func roomRPSChallengeDataFromProto(resp *gamev1.RoomRPSChallengeResponse) roomRPSChallengeData {
if resp == nil {
return roomRPSChallengeData{}
}
// 单挑战响应和列表响应使用同一个 challenge 结构,避免 Flutter 为发起、应战、详情维护多套字段映射。
return roomRPSChallengeData{Challenge: roomRPSChallengeItemDataFromProto(resp.GetChallenge()), ServerTimeMS: resp.GetServerTimeMs()}
}
func roomRPSChallengeListDataFromProto(resp *gamev1.ListRoomRPSChallengesResponse) roomRPSChallengeListData {
if resp == nil {
return roomRPSChallengeListData{}
}
items := make([]*roomRPSChallengeItemData, 0, len(resp.GetChallenges()))
for _, challenge := range resp.GetChallenges() {
// 使用指针切片是为了后续展示资料补全可以原地写入 initiator/challenger.user不再复制整份挑战单。
item := roomRPSChallengeItemDataFromProto(challenge)
items = append(items, &item)
}
return roomRPSChallengeListData{
Challenges: items,
NextCursor: resp.GetNextCursor(),
PageSize: resp.GetPageSize(),
ServerTimeMS: resp.GetServerTimeMs(),
}
}
func roomRPSChallengeItemDataFromProto(challenge *gamev1.RoomRPSChallenge) roomRPSChallengeItemData {
if challenge == nil {
return roomRPSChallengeItemData{}
}
// game-service 返回的是订单事实gateway 这里只做 JSON 形态转换;状态、胜负、结算失败原因不能在这里二次推导。
// 所有可能超过 JS 安全整数范围的 ID 都保留 string 和 number 双字段Flutter 优先使用 string后台排查仍可读 number。
return roomRPSChallengeItemData{
AppCode: challenge.GetAppCode(),
ChallengeID: challenge.GetChallengeId(),
RoomID: challenge.GetRoomId(),
RegionID: challenge.GetRegionId(),
Status: challenge.GetStatus(),
StakeGiftID: int64String(challenge.GetStakeGiftId()),
StakeGiftIDNumber: challenge.GetStakeGiftId(),
StakeCoin: challenge.GetStakeCoin(),
Initiator: roomRPSPlayerItemDataFromProto(challenge.GetInitiator()),
Challenger: roomRPSPlayerItemDataFromProto(challenge.GetChallenger()),
WinnerUserID: int64String(challenge.GetWinnerUserId()),
WinnerUserIDNumber: challenge.GetWinnerUserId(),
SettlementStatus: challenge.GetSettlementStatus(),
FailureReason: challenge.GetFailureReason(),
TimeoutAtMS: challenge.GetTimeoutAtMs(),
RevealAtMS: challenge.GetRevealAtMs(),
CreatedAtMS: challenge.GetCreatedAtMs(),
MatchedAtMS: challenge.GetMatchedAtMs(),
SettledAtMS: challenge.GetSettledAtMs(),
UpdatedAtMS: challenge.GetUpdatedAtMs(),
}
}
func roomRPSPlayerItemDataFromProto(player *gamev1.RoomRPSPlayer) roomRPSPlayerItemData {
if player == nil {
return roomRPSPlayerItemData{}
}
// user 先放 user_id 兜底;真正的 nickname/avatar 在 enrichRoomRPSChallenges 里批量补齐,资料失败不影响订单返回。
return roomRPSPlayerItemData{
UserID: int64String(player.GetUserId()),
UserIDNumber: player.GetUserId(),
Gesture: player.GetGesture(),
Result: player.GetResult(),
BalanceAfter: player.GetBalanceAfter(),
JoinedAtMS: player.GetJoinedAtMs(),
User: diceUserData{UserID: int64String(player.GetUserId())},
}
}