灵仙gateway

This commit is contained in:
hy001 2026-04-23 16:03:13 +08:00
parent 3e2719f682
commit 1d2bc6eafd
5 changed files with 252 additions and 8 deletions

View File

@ -88,6 +88,16 @@ func (g *Gateways) GetUserProfile(ctx context.Context, userID int64) (UserProfil
return g.Profile.GetUserProfile(ctx, userID)
}
// GetUserProfileByAccount 透传到资料网关。
func (g *Gateways) GetUserProfileByAccount(ctx context.Context, sysOrigin, account string) (UserProfile, error) {
return g.Profile.GetUserProfileByAccount(ctx, sysOrigin, account)
}
// GetUserProfileByAccountOne 透传到资料网关。
func (g *Gateways) GetUserProfileByAccountOne(ctx context.Context, account string) (UserProfile, error) {
return g.Profile.GetUserProfileByAccountOne(ctx, account)
}
// GetUserLanguage 透传到资料网关。
func (g *Gateways) GetUserLanguage(ctx context.Context, userID int64) (string, error) {
return g.Profile.GetUserLanguage(ctx, userID)
@ -198,6 +208,16 @@ func (g *ProfileGateway) GetUserProfile(ctx context.Context, userID int64) (User
return g.client.GetUserProfile(ctx, userID)
}
// GetUserProfileByAccount 根据账号查询用户资料。
func (g *ProfileGateway) GetUserProfileByAccount(ctx context.Context, sysOrigin, account string) (UserProfile, error) {
return g.client.GetUserProfileByAccount(ctx, sysOrigin, account)
}
// GetUserProfileByAccountOne 根据账号查询单个用户资料。
func (g *ProfileGateway) GetUserProfileByAccountOne(ctx context.Context, account string) (UserProfile, error) {
return g.client.GetUserProfileByAccountOne(ctx, account)
}
// GetUserLanguage 查询用户语言。
func (g *ProfileGateway) GetUserLanguage(ctx context.Context, userID int64) (string, error) {
return g.client.GetUserLanguage(ctx, userID)

View File

@ -38,6 +38,7 @@ type ConsoleAccount struct {
type UserProfile struct {
ID Int64Value `json:"id"`
Account string `json:"account"`
UserAvatar string `json:"userAvatar"`
UserNickname string `json:"userNickname"`
}
@ -349,6 +350,32 @@ func (c *Client) GetUserProfile(ctx context.Context, userID int64) (UserProfile,
return resp.Body, nil
}
func (c *Client) GetUserProfileByAccount(ctx context.Context, sysOrigin, account string) (UserProfile, error) {
endpoint := c.cfg.Java.OtherBaseURL + "/user-profile/client/getByAccount?sysOrigin=" +
url.QueryEscape(strings.TrimSpace(sysOrigin)) + "&account=" + url.QueryEscape(strings.TrimSpace(account))
var resp resultResponse[UserProfile]
if err := c.getJSON(ctx, endpoint, nil, &resp); err != nil {
return UserProfile{}, err
}
if int64(resp.Body.ID) == 0 {
return UserProfile{}, fmt.Errorf("empty user profile response")
}
return resp.Body, nil
}
func (c *Client) GetUserProfileByAccountOne(ctx context.Context, account string) (UserProfile, error) {
endpoint := c.cfg.Java.OtherBaseURL + "/user-profile/client/getByAccountOne?account=" +
url.QueryEscape(strings.TrimSpace(account))
var resp resultResponse[UserProfile]
if err := c.getJSON(ctx, endpoint, nil, &resp); err != nil {
return UserProfile{}, err
}
if int64(resp.Body.ID) == 0 {
return UserProfile{}, fmt.Errorf("empty user profile response")
}
return resp.Body, nil
}
func (c *Client) rewardBridgeBaseURL() string {
if baseURL := strings.TrimRight(strings.TrimSpace(c.cfg.Java.ConsoleBaseURL), "/"); baseURL != "" {
return baseURL

View File

@ -31,14 +31,19 @@ func (s *GameOpenService) HandleQueryUser(ctx context.Context, req QueryUserRequ
s.saveCallbackLog(ctx, "query-user", rawJSON, resp, "", req, nil, gameOpenLogStatusFailed)
return resp
}
userID := int64(credential.UserID)
if req.UID != strconv.FormatInt(userID, 10) {
resolved, err := s.resolveUserWithToken(ctx, credential, req.UID)
if err != nil {
resp := failResponse(gameOpenCodeTokenInvalid, "uid token mismatch")
userID := int64(credential.UserID)
s.saveCallbackLog(ctx, "query-user", rawJSON, resp, credential.SysOrigin, req, &userID, gameOpenLogStatusFailed)
return resp
}
userID := resolved.userID
profile, err := s.java.GetUserProfile(ctx, userID)
profile := resolved.profile
if int64(profile.ID) == 0 {
profile, err = s.java.GetUserProfile(ctx, userID)
}
if err != nil {
resp := failResponse(gameOpenCodeServerError, "profile load failed")
s.saveCallbackLog(ctx, "query-user", rawJSON, resp, credential.SysOrigin, req, &userID, gameOpenLogStatusFailed)
@ -80,12 +85,14 @@ func (s *GameOpenService) HandleUpdateCoin(ctx context.Context, req UpdateCoinRe
s.saveCallbackLog(ctx, "update-coin", rawJSON, resp, "", req, nil, gameOpenLogStatusFailed)
return resp
}
userID := int64(credential.UserID)
if req.UID != strconv.FormatInt(userID, 10) {
resolved, err := s.resolveUserWithToken(ctx, credential, req.UID)
if err != nil {
resp := failResponse(gameOpenCodeTokenInvalid, "uid token mismatch")
userID := int64(credential.UserID)
s.saveCallbackLog(ctx, "update-coin", rawJSON, resp, credential.SysOrigin, req, &userID, gameOpenLogStatusFailed)
return resp
}
userID := resolved.userID
eventID := "GAME_OPEN:" + strings.TrimSpace(req.OrderID)
exists, err := s.java.ExistsGoldEvent(ctx, eventID)
@ -138,7 +145,12 @@ func (s *GameOpenService) HandleSupplement(ctx context.Context, req SupplementRe
return resp
}
userID := parseInt64Default(req.UID)
userID, err := s.resolveUserID(ctx, req.UID)
if err != nil {
resp := failResponse(gameOpenCodeTokenInvalid, "uid invalid")
s.saveCallbackLog(ctx, "supplement", rawJSON, resp, "", req, nil, gameOpenLogStatusFailed)
return resp
}
balance, err := s.readUserBalance(ctx, userID)
if err != nil {
resp := failResponse(gameOpenCodeServerError, "balance load failed")
@ -187,6 +199,49 @@ func (s *GameOpenService) readUserBalance(ctx context.Context, userID int64) (in
return balances[userID], nil
}
type resolvedGameOpenUser struct {
userID int64
profile integration.UserProfile
}
func (s *GameOpenService) resolveUserWithToken(ctx context.Context, credential integration.UserCredential, uid string) (resolvedGameOpenUser, error) {
userID := int64(credential.UserID)
uid = strings.TrimSpace(uid)
if uid == strconv.FormatInt(userID, 10) {
return resolvedGameOpenUser{userID: userID}, nil
}
profile, err := s.java.GetUserProfileByAccount(ctx, credential.SysOrigin, uid)
if err == nil {
if int64(profile.ID) == userID {
return resolvedGameOpenUser{userID: userID, profile: profile}, nil
}
return resolvedGameOpenUser{}, fmt.Errorf("uid token mismatch")
}
profile, err = s.java.GetUserProfile(ctx, userID)
if err != nil {
return resolvedGameOpenUser{}, err
}
if strings.EqualFold(strings.TrimSpace(profile.Account), uid) {
return resolvedGameOpenUser{userID: userID, profile: profile}, nil
}
return resolvedGameOpenUser{}, fmt.Errorf("uid token mismatch")
}
func (s *GameOpenService) resolveUserID(ctx context.Context, uid string) (int64, error) {
uid = strings.TrimSpace(uid)
profile, err := s.java.GetUserProfileByAccountOne(ctx, uid)
if err == nil && int64(profile.ID) > 0 {
return int64(profile.ID), nil
}
if userID := parseInt64Default(uid); userID > 0 {
return userID, nil
}
return 0, fmt.Errorf("uid invalid")
}
func (s *GameOpenService) saveCallbackLog(ctx context.Context, requestType, rawJSON string, resp CallbackResponse, sysOrigin string, req any, userID *int64, status string) {
if s == nil || s.repo.DB == nil {
return

View File

@ -11,6 +11,7 @@ type stubGateway struct {
credential integration.UserCredential
token string
profile integration.UserProfile
accountMap map[string]integration.UserProfile
balance int64
exists bool
cmd integration.GoldReceiptCommand
@ -28,8 +29,34 @@ func (s *stubGateway) GetUserProfile(context.Context, int64) (integration.UserPr
return s.profile, nil
}
func (s *stubGateway) GetUserProfileByAccount(_ context.Context, _ string, account string) (integration.UserProfile, error) {
if profile, ok := s.accountMap[account]; ok {
return profile, nil
}
return integration.UserProfile{}, context.Canceled
}
func (s *stubGateway) GetUserProfileByAccountOne(_ context.Context, account string) (integration.UserProfile, error) {
if profile, ok := s.accountMap[account]; ok {
return profile, nil
}
return integration.UserProfile{}, context.Canceled
}
func (s *stubGateway) MapGoldBalance(context.Context, []int64) (map[int64]int64, error) {
return map[int64]int64{int64(s.credential.UserID): s.balance}, nil
result := map[int64]int64{}
if int64(s.credential.UserID) > 0 {
result[int64(s.credential.UserID)] = s.balance
}
if int64(s.profile.ID) > 0 {
result[int64(s.profile.ID)] = s.balance
}
for _, profile := range s.accountMap {
if int64(profile.ID) > 0 {
result[int64(profile.ID)] = s.balance
}
}
return result, nil
}
func (s *stubGateway) ExistsGoldEvent(context.Context, string) (bool, error) {
@ -46,6 +73,7 @@ func TestHandleQueryUser(t *testing.T) {
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
profile: integration.UserProfile{
ID: 1234567,
Account: "1234567",
UserNickname: "tester",
UserAvatar: "https://example.com/avatar.png",
},
@ -78,7 +106,11 @@ func TestHandleQueryUser(t *testing.T) {
func TestHandleUpdateCoin(t *testing.T) {
gateway := &stubGateway{
credential: integration.UserCredential{UserID: 1234567, SysOrigin: "LIKEI"},
balance: 99,
profile: integration.UserProfile{
ID: 1234567,
Account: "1234567",
},
balance: 99,
}
service := NewGameOpenService(config.Config{
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
@ -109,6 +141,114 @@ func TestHandleUpdateCoin(t *testing.T) {
}
}
func TestHandleQueryUserAcceptsAccountUID(t *testing.T) {
gateway := &stubGateway{
credential: integration.UserCredential{UserID: 4569421795454615552, SysOrigin: "LIKEI"},
profile: integration.UserProfile{
ID: 4569421795454615552,
Account: "1234567",
UserNickname: "tester",
UserAvatar: "https://example.com/avatar.png",
},
accountMap: map[string]integration.UserProfile{
"1234567": {
ID: 4569421795454615552,
Account: "1234567",
UserNickname: "tester",
UserAvatar: "https://example.com/avatar.png",
},
},
balance: 88,
}
service := NewGameOpenService(config.Config{
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
}, nil, gateway)
req := QueryUserRequest{
GameID: "101",
UID: "1234567",
Token: "token",
RoomID: "room-1",
}
req.Sign = buildQueryUserSign(req, "game-open-test-key")
resp := service.HandleQueryUser(context.Background(), req, "{}")
if resp.ErrorCode != 0 {
t.Fatalf("HandleQueryUser() error = %+v", resp)
}
}
func TestHandleUpdateCoinAcceptsAccountUID(t *testing.T) {
gateway := &stubGateway{
credential: integration.UserCredential{UserID: 4569421795454615552, SysOrigin: "LIKEI"},
accountMap: map[string]integration.UserProfile{
"1234567": {
ID: 4569421795454615552,
Account: "1234567",
},
},
balance: 99,
}
service := NewGameOpenService(config.Config{
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
}, nil, gateway)
req := UpdateCoinRequest{
OrderID: "order-1",
GameID: "101",
RoundID: "round-1",
UID: "1234567",
Coin: 15,
Type: 2,
RewardType: 2,
Token: "token",
WinID: "win-1",
RoomID: "room-1",
}
req.Sign = buildUpdateCoinSign(req, "game-open-test-key")
resp := service.HandleUpdateCoin(context.Background(), req, "{}")
if resp.ErrorCode != 0 {
t.Fatalf("HandleUpdateCoin() error = %+v", resp)
}
}
func TestHandleSupplementAcceptsAccountUID(t *testing.T) {
gateway := &stubGateway{
accountMap: map[string]integration.UserProfile{
"1234567": {
ID: 4569421795454615552,
Account: "1234567",
},
},
balance: 66,
}
service := NewGameOpenService(config.Config{
GameOpen: config.GameOpenConfig{AppKey: "game-open-test-key"},
}, nil, gateway)
req := SupplementRequest{
OrderID: "order-1",
GameID: "101",
RoundID: "round-1",
UID: "1234567",
Coin: 1,
RewardType: 1,
WinID: "win-1",
RoomID: "room-1",
}
req.Sign = buildSupplementSign(req, "game-open-test-key")
resp := service.HandleSupplement(context.Background(), req, "{}")
if resp.ErrorCode != 0 {
t.Fatalf("HandleSupplement() error = %+v", resp)
}
data, ok := resp.Data.(map[string]any)
if !ok {
t.Fatalf("HandleSupplement() data type = %T", resp.Data)
}
if got := data["coin"]; got != int64(66) {
t.Fatalf("HandleSupplement() coin = %v, want 66", got)
}
}
func TestGetIntegrationInfo(t *testing.T) {
gateway := &stubGateway{token: "token-123"}
service := NewGameOpenService(config.Config{

View File

@ -29,6 +29,8 @@ type gameOpenGateway interface {
AuthenticateToken(ctx context.Context, token string) (integration.UserCredential, error)
CreateIfAbsentUserCredentialToken(ctx context.Context, userID int64) (string, error)
GetUserProfile(ctx context.Context, userID int64) (integration.UserProfile, error)
GetUserProfileByAccount(ctx context.Context, sysOrigin, account string) (integration.UserProfile, error)
GetUserProfileByAccountOne(ctx context.Context, account string) (integration.UserProfile, error)
MapGoldBalance(ctx context.Context, userIDs []int64) (map[int64]int64, error)
ExistsGoldEvent(ctx context.Context, eventID string) (bool, error)
ChangeGoldBalance(ctx context.Context, cmd integration.GoldReceiptCommand) error