483 lines
19 KiB
Go
483 lines
19 KiB
Go
package game
|
||
|
||
import (
|
||
"context"
|
||
"crypto/md5"
|
||
"encoding/hex"
|
||
"encoding/json"
|
||
"fmt"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
gamev1 "hyapp.local/api/proto/game/v1"
|
||
"hyapp/pkg/xerr"
|
||
gamedomain "hyapp/services/game-service/internal/domain/game"
|
||
)
|
||
|
||
const (
|
||
// BAISHUN 文档定义 code=0 成功;余额不足固定 1008,签名错误固定 1003。
|
||
baishunCodeOK = 0
|
||
baishunCodeInvalidData = 1001
|
||
baishunCodeReadDataFailed = 1002
|
||
baishunCodeSignatureInvalid = 1003
|
||
baishunCodeInsufficientBalance = 1008
|
||
baishunCodeGameMaintenance = 1019
|
||
baishunCodeAPIServerError = 1021
|
||
baishunCodeIPRestricted = 1023
|
||
|
||
baishunDefaultTokenTTL = 24 * time.Hour
|
||
baishunSignatureValidWindow = 15 * time.Second
|
||
baishunDefaultSuccessMessage = "succeed"
|
||
)
|
||
|
||
type baishunAdapterConfig struct {
|
||
// app_id/app_channel/app_key 均来自百顺后台;app_key 存在平台 callbackSecret 中。
|
||
AppID int64 `json:"app_id"`
|
||
AppIDAlias int64 `json:"appId"`
|
||
AppChannel string `json:"app_channel"`
|
||
AppChannelAlias string `json:"appChannel"`
|
||
UIDMode string `json:"uid_mode"`
|
||
DefaultLang string `json:"default_lang"`
|
||
TokenTTLSeconds int64 `json:"token_ttl_seconds"`
|
||
|
||
// 以下字段是 NativeBridge.getConfig 需要返回给 H5 的配置,同时也会拼在启动 URL query 中给壳层复用。
|
||
GameMode string `json:"game_mode"`
|
||
SceneMode int64 `json:"scene_mode"`
|
||
CurrencyIcon string `json:"currency_icon"`
|
||
GSP int64 `json:"gsp"`
|
||
|
||
// 可选用户管控字段;配置后会随 get_sstoken/get_user_info 一起返回给百顺。
|
||
UserType int `json:"user_type"`
|
||
ReleaseCond int `json:"release_cond"`
|
||
IsOldUser *bool `json:"is_old_user"`
|
||
Extend string `json:"extend"`
|
||
|
||
// game_urls 支持同一个百顺商户下不同 game_id 使用不同 download_url。
|
||
GameURLs map[string]string `json:"game_urls"`
|
||
LaunchURLTemplate string `json:"launch_url_template"`
|
||
}
|
||
|
||
func baishunConfigFromPlatform(value any) baishunAdapterConfig {
|
||
var raw string
|
||
switch typed := value.(type) {
|
||
case gamedomain.Platform:
|
||
raw = typed.AdapterConfigJSON
|
||
case gamedomain.LaunchableGame:
|
||
raw = typed.AdapterConfigJSON
|
||
}
|
||
var config baishunAdapterConfig
|
||
_ = json.Unmarshal([]byte(strings.TrimSpace(raw)), &config)
|
||
if config.AppID == 0 {
|
||
config.AppID = config.AppIDAlias
|
||
}
|
||
if strings.TrimSpace(config.AppChannel) == "" {
|
||
config.AppChannel = config.AppChannelAlias
|
||
}
|
||
config.AppChannel = strings.TrimSpace(config.AppChannel)
|
||
config.UIDMode = strings.ToLower(strings.TrimSpace(config.UIDMode))
|
||
config.DefaultLang = strings.TrimSpace(config.DefaultLang)
|
||
config.GameMode = strings.TrimSpace(config.GameMode)
|
||
config.CurrencyIcon = strings.TrimSpace(config.CurrencyIcon)
|
||
config.Extend = strings.TrimSpace(config.Extend)
|
||
config.LaunchURLTemplate = strings.TrimSpace(config.LaunchURLTemplate)
|
||
config.GameURLs = normalizeStringMap(config.GameURLs)
|
||
return config
|
||
}
|
||
|
||
func (c baishunAdapterConfig) TokenTTL() time.Duration {
|
||
if c.TokenTTLSeconds > 0 {
|
||
return time.Duration(c.TokenTTLSeconds) * time.Second
|
||
}
|
||
return baishunDefaultTokenTTL
|
||
}
|
||
|
||
func (c baishunAdapterConfig) LanguageValue() string {
|
||
if c.DefaultLang != "" {
|
||
return c.DefaultLang
|
||
}
|
||
return "2"
|
||
}
|
||
|
||
func (c baishunAdapterConfig) GameModeValue() string {
|
||
if c.GameMode != "" {
|
||
return c.GameMode
|
||
}
|
||
return "3"
|
||
}
|
||
|
||
func baishunLaunchBaseURL(game gamedomain.LaunchableGame, config baishunAdapterConfig) string {
|
||
for _, key := range []string{game.ProviderGameID, game.GameID, strings.ToLower(game.GameID)} {
|
||
if raw := strings.TrimSpace(config.GameURLs[strings.TrimSpace(key)]); raw != "" {
|
||
return raw
|
||
}
|
||
}
|
||
if config.LaunchURLTemplate == "" {
|
||
return ""
|
||
}
|
||
replacer := strings.NewReplacer(
|
||
"{provider_game_id}", strings.TrimSpace(game.ProviderGameID),
|
||
"{providerGameId}", strings.TrimSpace(game.ProviderGameID),
|
||
"{game_id}", strings.TrimSpace(game.GameID),
|
||
"{gameId}", strings.TrimSpace(game.GameID),
|
||
)
|
||
return strings.TrimSpace(replacer.Replace(config.LaunchURLTemplate))
|
||
}
|
||
|
||
func (s *Service) handleBaishunOperation(ctx context.Context, app string, platform gamedomain.Platform, req *gamev1.CallbackRequest, operation string, requestHash string) ([]byte, string, string, bool, string, error) {
|
||
if !callbackIPAllowed(req, platform.CallbackIPWhitelist) {
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeIPRestricted, "ip restricted", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(baishunCodeIPRestricted), false, "", nil
|
||
}
|
||
config := baishunConfigFromPlatform(platform)
|
||
body, signed, decoded, providerRequestID := s.decodeBaishunBody(req, platform.CallbackSecretCiphertext)
|
||
if !signed {
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeSignatureInvalid, "signature invalid", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(baishunCodeSignatureInvalid), false, providerRequestID, nil
|
||
}
|
||
if !decoded {
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid data", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(baishunCodeInvalidData), signed, providerRequestID, nil
|
||
}
|
||
if err := validateBaishunAppID(config, body.AppIDInt64()); err != nil {
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid app_id", map[string]any{})
|
||
return raw, contentType, strconv.Itoa(baishunCodeInvalidData), signed, providerRequestID, nil
|
||
}
|
||
switch operation {
|
||
case "get_sstoken", "getsstoken", "sstoken", "api/get_sstoken":
|
||
return s.handleBaishunGetSSToken(ctx, app, config, req, body)
|
||
case "get_user_info", "userinfo", "user_info", "api/get_user_info":
|
||
return s.handleBaishunUserInfo(ctx, app, config, req, body)
|
||
case "update_sstoken", "updatesstoken", "refresh_sstoken", "api/update_sstoken":
|
||
return s.handleBaishunUpdateSSToken(ctx, app, config, req, body)
|
||
case "change_balance", "changebalance", "api/change_balance":
|
||
return s.handleBaishunChangeBalance(ctx, app, config, req, body, requestHash)
|
||
default:
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeOK, baishunDefaultSuccessMessage, map[string]any{})
|
||
return raw, contentType, strconv.Itoa(baishunCodeOK), signed, providerRequestID, nil
|
||
}
|
||
}
|
||
|
||
type baishunBody struct {
|
||
AppID json.Number `json:"app_id"`
|
||
UserID string `json:"user_id"`
|
||
Code string `json:"code"`
|
||
SSToken string `json:"ss_token"`
|
||
ClientIP string `json:"client_ip"`
|
||
GameID json.Number `json:"game_id"`
|
||
CurrencyDiff int64 `json:"currency_diff"`
|
||
DiffMsg string `json:"diff_msg"`
|
||
GameRoundID string `json:"game_round_id"`
|
||
RoomID string `json:"room_id"`
|
||
ChangeTimeAt int64 `json:"change_time_at"`
|
||
OrderID string `json:"order_id"`
|
||
Extend string `json:"extend"`
|
||
MsgType string `json:"msg_type"`
|
||
CurrencyType json.Number `json:"currency_type"`
|
||
SignatureNonce string `json:"signature_nonce"`
|
||
Timestamp int64 `json:"timestamp"`
|
||
Signature string `json:"signature"`
|
||
ProviderName string `json:"provider_name"`
|
||
}
|
||
|
||
func (s *Service) decodeBaishunBody(req *gamev1.CallbackRequest, secret string) (baishunBody, bool, bool, string) {
|
||
var body baishunBody
|
||
decoder := json.NewDecoder(strings.NewReader(string(req.GetRawBody())))
|
||
decoder.UseNumber()
|
||
if err := decoder.Decode(&body); err != nil {
|
||
return baishunBody{}, false, false, ""
|
||
}
|
||
body.normalize()
|
||
providerRequestID := strings.TrimSpace(body.OrderID)
|
||
if !s.baishunSignatureValid(body, secret) {
|
||
return baishunBody{}, false, true, providerRequestID
|
||
}
|
||
return body, true, true, providerRequestID
|
||
}
|
||
|
||
func (s *Service) baishunSignatureValid(body baishunBody, secret string) bool {
|
||
secret = strings.TrimSpace(secret)
|
||
if secret == "" || body.SignatureNonce == "" || body.Timestamp <= 0 {
|
||
return false
|
||
}
|
||
requestAt := time.Unix(body.Timestamp, 0)
|
||
now := s.now()
|
||
if requestAt.After(now.Add(baishunSignatureValidWindow)) || now.Sub(requestAt) > baishunSignatureValidWindow {
|
||
return false
|
||
}
|
||
raw := fmt.Sprintf("%s%s%d", body.SignatureNonce, secret, body.Timestamp)
|
||
sum := md5.Sum([]byte(raw))
|
||
if !strings.EqualFold(body.Signature, hex.EncodeToString(sum[:])) {
|
||
return false
|
||
}
|
||
// 文档要求 signature_nonce 在 15 秒窗口内不可重复;这里做单进程防重放,多节点严格一致需接共享缓存。
|
||
if !s.rememberSignatureNonce(gamedomain.AdapterBaishunV1, body.SignatureNonce, requestAt, baishunSignatureValidWindow) {
|
||
return false
|
||
}
|
||
return true
|
||
}
|
||
|
||
func (body *baishunBody) normalize() {
|
||
body.UserID = strings.TrimSpace(body.UserID)
|
||
body.Code = strings.TrimSpace(body.Code)
|
||
body.SSToken = strings.TrimSpace(body.SSToken)
|
||
body.ClientIP = strings.TrimSpace(body.ClientIP)
|
||
body.DiffMsg = strings.ToLower(strings.TrimSpace(body.DiffMsg))
|
||
body.GameRoundID = strings.TrimSpace(body.GameRoundID)
|
||
body.RoomID = strings.TrimSpace(body.RoomID)
|
||
body.OrderID = strings.TrimSpace(body.OrderID)
|
||
body.Extend = strings.TrimSpace(body.Extend)
|
||
body.MsgType = strings.TrimSpace(body.MsgType)
|
||
body.SignatureNonce = strings.TrimSpace(body.SignatureNonce)
|
||
body.Signature = strings.TrimSpace(body.Signature)
|
||
body.ProviderName = strings.TrimSpace(body.ProviderName)
|
||
}
|
||
|
||
func (body baishunBody) AppIDInt64() int64 {
|
||
value, _ := strconv.ParseInt(strings.TrimSpace(body.AppID.String()), 10, 64)
|
||
return value
|
||
}
|
||
|
||
func (body baishunBody) GameIDInt64() int64 {
|
||
value, _ := strconv.ParseInt(strings.TrimSpace(body.GameID.String()), 10, 64)
|
||
return value
|
||
}
|
||
|
||
func validateBaishunAppID(config baishunAdapterConfig, appID int64) error {
|
||
if appID <= 0 {
|
||
return xerr.New(xerr.InvalidArgument, "app_id required")
|
||
}
|
||
if config.AppID > 0 && appID != config.AppID {
|
||
return xerr.New(xerr.InvalidArgument, "app_id mismatch")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func (s *Service) handleBaishunGetSSToken(ctx context.Context, app string, config baishunAdapterConfig, req *gamev1.CallbackRequest, body baishunBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.Code == "" {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid data", true, "")
|
||
}
|
||
session, err := s.validBaishunSession(ctx, app, body.Code)
|
||
if err != nil || !baishunSessionMatches(session, config, body.UserID, 0) {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "token invalid", true, "")
|
||
}
|
||
snapshot, err := s.yomiUserSnapshot(ctx, app, req, session.UserID)
|
||
if err != nil {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunUserSnapshotCodeFromError(err), baishunUserSnapshotMessageFromError(err), true, "")
|
||
}
|
||
response := map[string]any{
|
||
"code": baishunCodeOK,
|
||
"message": baishunDefaultSuccessMessage,
|
||
"unique_id": baishunUniqueID(req.GetMeta().GetRequestId()),
|
||
"data": map[string]any{
|
||
"ss_token": body.Code,
|
||
"expire_date": session.ExpiresAtMS,
|
||
},
|
||
"user_info": baishunUserInfoData(session, config, snapshot),
|
||
}
|
||
raw, contentType := jsonResponse(response)
|
||
return raw, contentType, strconv.Itoa(baishunCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleBaishunUserInfo(ctx context.Context, app string, config baishunAdapterConfig, req *gamev1.CallbackRequest, body baishunBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.SSToken == "" {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid data", true, "")
|
||
}
|
||
session, err := s.validBaishunSession(ctx, app, body.SSToken)
|
||
if err != nil || !baishunSessionMatches(session, config, body.UserID, body.GameIDInt64()) {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "token invalid", true, "")
|
||
}
|
||
snapshot, err := s.yomiUserSnapshot(ctx, app, req, session.UserID)
|
||
if err != nil {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunUserSnapshotCodeFromError(err), baishunUserSnapshotMessageFromError(err), true, "")
|
||
}
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeOK, baishunDefaultSuccessMessage, baishunUserInfoData(session, config, snapshot))
|
||
return raw, contentType, strconv.Itoa(baishunCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleBaishunUpdateSSToken(ctx context.Context, app string, config baishunAdapterConfig, req *gamev1.CallbackRequest, body baishunBody) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.SSToken == "" {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid data", true, "")
|
||
}
|
||
session, err := s.validBaishunSession(ctx, app, body.SSToken)
|
||
if err != nil || !baishunSessionMatches(session, config, body.UserID, body.GameIDInt64()) {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "token invalid", true, "")
|
||
}
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeOK, baishunDefaultSuccessMessage, map[string]any{
|
||
"ss_token": body.SSToken,
|
||
"expire_date": session.ExpiresAtMS,
|
||
})
|
||
return raw, contentType, strconv.Itoa(baishunCodeOK), true, "", nil
|
||
}
|
||
|
||
func (s *Service) handleBaishunChangeBalance(ctx context.Context, app string, config baishunAdapterConfig, req *gamev1.CallbackRequest, body baishunBody, requestHash string) ([]byte, string, string, bool, string, error) {
|
||
if body.UserID == "" || body.SSToken == "" || body.OrderID == "" || body.GameIDInt64() <= 0 || body.DiffMsg == "" {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "invalid data", true, body.OrderID)
|
||
}
|
||
session, err := s.validBaishunSession(ctx, app, body.SSToken)
|
||
if err != nil || !baishunSessionMatches(session, config, body.UserID, body.GameIDInt64()) {
|
||
return baishunAdapterError(req.GetMeta().GetRequestId(), baishunCodeInvalidData, "token invalid", true, body.OrderID)
|
||
}
|
||
if session.ProviderGameID == "" {
|
||
session.ProviderGameID = strconv.FormatInt(body.GameIDInt64(), 10)
|
||
}
|
||
if session.GameID == "" {
|
||
session.GameID = session.ProviderGameID
|
||
}
|
||
opType, amount := baishunOpType(body.CurrencyDiff)
|
||
result, err := s.applyYomiCoinChange(ctx, app, req, session, body.OrderID, body.GameRoundID, opType, amount, body.RoomID, requestHash)
|
||
if err != nil {
|
||
code := baishunCodeFromError(err)
|
||
balance := s.bestEffortCoinBalance(ctx, app, req.GetMeta().GetRequestId(), session.UserID)
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), code, baishunMessageFromError(err), baishunChangeBalanceData(balance, body.Extend))
|
||
return raw, contentType, strconv.Itoa(code), true, body.OrderID, nil
|
||
}
|
||
raw, contentType := baishunJSON(req.GetMeta().GetRequestId(), baishunCodeOK, baishunDefaultSuccessMessage, baishunChangeBalanceData(result.BalanceAfter, body.Extend))
|
||
return raw, contentType, strconv.Itoa(baishunCodeOK), true, body.OrderID, nil
|
||
}
|
||
|
||
func (s *Service) validBaishunSession(ctx context.Context, app string, token string) (gamedomain.LaunchSession, error) {
|
||
token = strings.TrimSpace(token)
|
||
if token == "" {
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid")
|
||
}
|
||
// 百顺的 ss_token 直接复用 App access token;同一个登录 token 可能打开多款百顺游戏。
|
||
// JWT 必须优先解析,否则会先命中旧启动会话的 hash,并被旧 provider_game_id 误判为跨游戏复用。
|
||
if strings.Count(token, ".") == 2 {
|
||
if session, err := s.appSessionFromAccessToken(app, token); err == nil {
|
||
return session, nil
|
||
}
|
||
}
|
||
if session, err := s.validYomiSession(ctx, app, token); err == nil {
|
||
return session, nil
|
||
}
|
||
if strings.Count(token, ".") == 2 {
|
||
return s.appSessionFromAccessToken(app, token)
|
||
}
|
||
return gamedomain.LaunchSession{}, xerr.New(xerr.Unauthorized, "token invalid")
|
||
}
|
||
|
||
func baishunSessionMatches(session gamedomain.LaunchSession, config baishunAdapterConfig, userID string, gameID int64) bool {
|
||
if gameID > 0 && strings.TrimSpace(session.ProviderGameID) != "" && strconv.FormatInt(gameID, 10) != strings.TrimSpace(session.ProviderGameID) {
|
||
return false
|
||
}
|
||
userID = strings.TrimSpace(userID)
|
||
return userID == externalUserID(session, config.UIDMode) ||
|
||
userID == strings.TrimSpace(session.DisplayUserID) ||
|
||
userID == strconv.FormatInt(session.UserID, 10)
|
||
}
|
||
|
||
func baishunOpType(diff int64) (string, int64) {
|
||
if diff < 0 {
|
||
return "debit", -diff
|
||
}
|
||
return "credit", diff
|
||
}
|
||
|
||
func baishunUserInfoData(session gamedomain.LaunchSession, config baishunAdapterConfig, snapshot yomiUserSnapshot) map[string]any {
|
||
data := map[string]any{
|
||
"user_id": externalUserID(session, config.UIDMode),
|
||
"user_name": snapshot.Nickname,
|
||
"user_avatar": snapshot.Avatar,
|
||
"balance": snapshot.Balance,
|
||
}
|
||
if config.UserType > 0 {
|
||
data["user_type"] = config.UserType
|
||
}
|
||
if config.ReleaseCond >= 0 && config.UserType > 0 {
|
||
data["release_cond"] = config.ReleaseCond
|
||
}
|
||
if config.IsOldUser != nil {
|
||
data["is_old_user"] = *config.IsOldUser
|
||
}
|
||
if config.Extend != "" {
|
||
data["extend"] = config.Extend
|
||
}
|
||
return data
|
||
}
|
||
|
||
func baishunChangeBalanceData(balance int64, extend string) map[string]any {
|
||
data := map[string]any{"currency_balance": balance}
|
||
if strings.TrimSpace(extend) != "" {
|
||
data["extend"] = strings.TrimSpace(extend)
|
||
}
|
||
return data
|
||
}
|
||
|
||
func baishunAdapterError(uniqueID string, code int, message string, signatureValid bool, providerRequestID string) ([]byte, string, string, bool, string, error) {
|
||
raw, contentType := baishunJSON(uniqueID, code, message, map[string]any{})
|
||
return raw, contentType, strconv.Itoa(code), signatureValid, providerRequestID, nil
|
||
}
|
||
|
||
func baishunJSON(uniqueID string, code int, message string, data any) ([]byte, string) {
|
||
if strings.TrimSpace(message) == "" && code == baishunCodeOK {
|
||
message = baishunDefaultSuccessMessage
|
||
}
|
||
if data == nil {
|
||
data = map[string]any{}
|
||
}
|
||
return jsonResponse(map[string]any{
|
||
"code": code,
|
||
"message": strings.TrimSpace(message),
|
||
"unique_id": baishunUniqueID(uniqueID),
|
||
"data": data,
|
||
})
|
||
}
|
||
|
||
func baishunUniqueID(value string) string {
|
||
value = strings.TrimSpace(value)
|
||
if value != "" {
|
||
return value
|
||
}
|
||
return strconv.FormatInt(time.Now().UnixNano(), 10)
|
||
}
|
||
|
||
func baishunCodeFromError(err error) int {
|
||
switch {
|
||
case err == nil:
|
||
return baishunCodeOK
|
||
case xerr.IsCode(err, xerr.InsufficientBalance):
|
||
return baishunCodeInsufficientBalance
|
||
case xerr.IsCode(err, xerr.SessionExpired), xerr.IsCode(err, xerr.Unauthorized), xerr.IsCode(err, xerr.InvalidArgument):
|
||
return baishunCodeInvalidData
|
||
case xerr.IsCode(err, xerr.NotFound):
|
||
return baishunCodeReadDataFailed
|
||
case xerr.IsCode(err, xerr.Conflict):
|
||
return baishunCodeGameMaintenance
|
||
default:
|
||
return baishunCodeAPIServerError
|
||
}
|
||
}
|
||
|
||
func baishunMessageFromError(err error) string {
|
||
switch baishunCodeFromError(err) {
|
||
case baishunCodeInsufficientBalance:
|
||
return "coin not enough"
|
||
case baishunCodeInvalidData:
|
||
return "invalid data"
|
||
case baishunCodeReadDataFailed:
|
||
return "read data failed"
|
||
case baishunCodeGameMaintenance:
|
||
return "game maintenance"
|
||
default:
|
||
if err != nil && strings.TrimSpace(xerr.MessageOf(err)) != "" {
|
||
return xerr.MessageOf(err)
|
||
}
|
||
return "api server error"
|
||
}
|
||
}
|
||
|
||
func baishunUserSnapshotCodeFromError(err error) int {
|
||
if xerr.IsCode(err, xerr.NotFound) {
|
||
return baishunCodeInvalidData
|
||
}
|
||
return baishunCodeFromError(err)
|
||
}
|
||
|
||
func baishunUserSnapshotMessageFromError(err error) string {
|
||
if xerr.IsCode(err, xerr.NotFound) {
|
||
return "token invalid"
|
||
}
|
||
return baishunMessageFromError(err)
|
||
}
|