package userclient import ( "context" "time" "hyapp-admin-server/internal/appctx" userv1 "hyapp.local/api/proto/user/v1" "google.golang.org/grpc" ) type Client interface { GetUser(ctx context.Context, req GetUserRequest) (*User, error) BatchGetUsers(ctx context.Context, req BatchGetUsersRequest) (map[int64]*User, error) AdminChangeUserCountry(ctx context.Context, req AdminChangeUserCountryRequest) (*ChangeUserCountryResult, error) SetUserStatus(ctx context.Context, req SetUserStatusRequest) (*SetUserStatusResult, error) GetRegisterRiskConfig(ctx context.Context, req GetRegisterRiskConfigRequest) (*RegisterRiskConfig, error) UpdateRegisterRiskConfig(ctx context.Context, req UpdateRegisterRiskConfigRequest) (*RegisterRiskConfig, error) RefreshLoginRiskWhitelistCache(ctx context.Context, req RefreshLoginRiskWhitelistCacheRequest) (*LoginRiskWhitelistCacheSnapshot, error) ResolveDisplayUserID(ctx context.Context, req ResolveDisplayUserIDRequest) (*UserIdentity, error) ListPrettyDisplayIDPools(ctx context.Context, req ListPrettyDisplayIDPoolsRequest) ([]*PrettyDisplayIDPool, int64, error) CreatePrettyDisplayIDPool(ctx context.Context, req PrettyDisplayIDPoolRequest) (*PrettyDisplayIDPool, error) UpdatePrettyDisplayIDPool(ctx context.Context, req PrettyDisplayIDPoolRequest) (*PrettyDisplayIDPool, error) GeneratePrettyDisplayIDs(ctx context.Context, req GeneratePrettyDisplayIDsRequest) (*PrettyDisplayIDGenerationBatch, error) ListPrettyDisplayIDs(ctx context.Context, req ListPrettyDisplayIDsRequest) ([]*PrettyDisplayID, int64, error) SetPrettyDisplayIDStatus(ctx context.Context, req SetPrettyDisplayIDStatusRequest) (*PrettyDisplayID, error) RecyclePrettyDisplayID(ctx context.Context, req RecyclePrettyDisplayIDRequest) (*PrettyDisplayID, error) AdminGrantPrettyDisplayID(ctx context.Context, req AdminGrantPrettyDisplayIDRequest) (*AdminGrantPrettyDisplayIDResult, error) ListCountries(ctx context.Context, req ListCountriesRequest) ([]*Country, error) UpdateCountry(ctx context.Context, req UpdateCountryRequest) (*Country, error) ListRegions(ctx context.Context, req ListRegionsRequest) ([]*Region, error) GetRegion(ctx context.Context, req GetRegionRequest) (*Region, error) UpdateRegion(ctx context.Context, req UpdateRegionRequest) (*Region, error) ReplaceRegionCountries(ctx context.Context, req ReplaceRegionCountriesRequest) (*Region, error) CreateBDLeader(ctx context.Context, req CreateBDLeaderRequest) (*BDProfile, error) CreateBD(ctx context.Context, req CreateBDRequest) (*BDProfile, error) SetBDStatus(ctx context.Context, req SetBDStatusRequest) (*BDProfile, error) CreateCoinSeller(ctx context.Context, req CreateCoinSellerRequest) (*CoinSellerProfile, error) SetCoinSellerStatus(ctx context.Context, req SetCoinSellerStatusRequest) (*CoinSellerProfile, error) ReviewCoinSellerSubApplication(ctx context.Context, req ReviewCoinSellerSubApplicationRequest) (*CoinSellerSubApplicationReviewResult, error) GetCoinSellerProfile(ctx context.Context, req GetCoinSellerProfileRequest) (*CoinSellerProfile, error) GetUserRoleSummary(ctx context.Context, req GetUserRoleSummaryRequest) (*UserRoleSummary, error) CreateAgency(ctx context.Context, req CreateAgencyRequest) (*CreateAgencyResult, error) AdminAddAgencyHost(ctx context.Context, req AdminAddAgencyHostRequest) (*CreateAgencyResult, error) CloseAgency(ctx context.Context, req CloseAgencyRequest) (*Agency, error) DeleteAgency(ctx context.Context, req DeleteAgencyRequest) (*Agency, error) SetAgencyStatus(ctx context.Context, req SetAgencyStatusRequest) (*Agency, error) SetAgencyJoinEnabled(ctx context.Context, req SetAgencyJoinEnabledRequest) (*Agency, error) QuickCreateAccount(ctx context.Context, req QuickCreateAccountRequest) (*QuickCreateAccountResult, error) } type GetUserRequest struct { RequestID string Caller string UserID int64 } // BatchGetUsersRequest 只承载后台展示需要的一组真实 user_id,不在 admin-server 里回查用户库。 type BatchGetUsersRequest struct { RequestID string Caller string UserIDs []int64 } type QuickCreateAccountRequest struct { RequestID string Caller string Password string Username string Avatar string Gender string Country string DeviceID string Source string InstallChannel string Platform string Language string Timezone string } type QuickCreateAccountResult struct { UserID int64 DisplayUserID string AccessToken string } type SetUserStatusRequest struct { RequestID string Caller string TargetUserID int64 Status string OperatorType string OperatorUserID int64 Reason string } type AdminChangeUserCountryRequest struct { RequestID string Caller string TargetUserID int64 Country string AdminUserID int64 Reason string } type ChangeUserCountryResult struct { User *User `json:"user"` OldRegionID int64 `json:"oldRegionId"` NewRegionID int64 `json:"newRegionId"` RegionChanged bool `json:"regionChanged"` } type SetUserStatusResult struct { User *User `json:"user"` RevokedSessionCount int64 `json:"revokedSessionCount"` AccessTokenRevoked bool `json:"accessTokenRevoked"` AccessTokenError string `json:"accessTokenRevokeError,omitempty"` IMKicked bool `json:"imKicked"` IMKickError string `json:"imKickError,omitempty"` RoomEvicted bool `json:"roomEvicted"` RoomID string `json:"roomId,omitempty"` RTCKicked bool `json:"rtcKicked"` RTCKickError string `json:"rtcKickError,omitempty"` RoomEvictError string `json:"roomEvictError,omitempty"` } type GetRegisterRiskConfigRequest struct { RequestID string Caller string } type UpdateRegisterRiskConfigRequest struct { RequestID string Caller string MaxAccountsPerDevice int32 OperatorAdminID int64 } type RegisterRiskConfig struct { AppCode string `json:"appCode"` MaxAccountsPerDevice int32 `json:"maxAccountsPerDevice"` UpdatedAtMs int64 `json:"updatedAtMs"` UpdatedByAdminID int64 `json:"updatedByAdminId"` } type RefreshLoginRiskWhitelistCacheRequest struct { RequestID string Caller string } type LoginRiskWhitelistCacheSnapshot struct { Refreshed bool `json:"refreshed"` IPWhitelistCount int32 `json:"ipWhitelistCount"` UserWhitelistCount int32 `json:"userWhitelistCount"` UserCacheExpiresAtMs int64 `json:"userCacheExpiresAtMs"` } type ResolveDisplayUserIDRequest struct { RequestID string Caller string DisplayUserID string } type UserIdentity struct { UserID int64 `json:"userId,string"` DisplayUserID string `json:"displayUserId"` Status string `json:"status"` DefaultDisplayUserID string `json:"defaultDisplayUserId"` DisplayUserIDKind string `json:"displayUserIdKind"` DisplayUserIDExpiresAtMs int64 `json:"displayUserIdExpiresAtMs"` PrettyID string `json:"prettyId,omitempty"` PrettyDisplayUserID string `json:"prettyDisplayUserId,omitempty"` AppCode string `json:"appCode"` } type User struct { UserID int64 `json:"userId,string"` Status string `json:"status"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` DisplayUserID string `json:"displayUserId"` DefaultDisplayUserID string `json:"defaultDisplayUserId"` DisplayUserIDKind string `json:"displayUserIdKind"` DisplayUserIDExpiresAtMs int64 `json:"displayUserIdExpiresAtMs"` PrettyID string `json:"prettyId,omitempty"` PrettyDisplayUserID string `json:"prettyDisplayUserId,omitempty"` Username string `json:"username"` Gender string `json:"gender"` Country string `json:"country"` Avatar string `json:"avatar"` Birth string `json:"birth"` Language string `json:"language"` CountryID int64 `json:"countryId"` CountryName string `json:"countryName"` CountryDisplayName string `json:"countryDisplayName"` RegionID int64 `json:"regionId"` RegionCode string `json:"regionCode"` RegionName string `json:"regionName"` ProfileCompleted bool `json:"profileCompleted"` ProfileCompletedAtMs int64 `json:"profileCompletedAtMs"` OnboardingStatus string `json:"onboardingStatus"` ISONumeric string `json:"isoNumeric"` PhoneCountryCode string `json:"phoneCountryCode"` CountryEnabled bool `json:"countryEnabled"` ContactInfo string `json:"contactInfo"` } type GRPCClient struct { client userv1.UserServiceClient authClient userv1.AuthServiceClient identityClient userv1.UserIdentityServiceClient countryClient userv1.CountryAdminServiceClient regionClient userv1.RegionAdminServiceClient prettyClient userv1.UserPrettyDisplayIDAdminServiceClient hostClient userv1.UserHostServiceClient hostAdminClient userv1.UserHostAdminServiceClient } func NewGRPC(conn grpc.ClientConnInterface) *GRPCClient { return &GRPCClient{ client: userv1.NewUserServiceClient(conn), authClient: userv1.NewAuthServiceClient(conn), identityClient: userv1.NewUserIdentityServiceClient(conn), countryClient: userv1.NewCountryAdminServiceClient(conn), regionClient: userv1.NewRegionAdminServiceClient(conn), prettyClient: userv1.NewUserPrettyDisplayIDAdminServiceClient(conn), hostClient: userv1.NewUserHostServiceClient(conn), hostAdminClient: userv1.NewUserHostAdminServiceClient(conn), } } func (c *GRPCClient) QuickCreateAccount(ctx context.Context, req QuickCreateAccountRequest) (*QuickCreateAccountResult, error) { resp, err := c.authClient.QuickCreateAccount(ctx, &userv1.QuickCreateAccountRequest{ Meta: &userv1.RequestMeta{ RequestId: req.RequestID, Caller: req.Caller, SentAtMs: time.Now().UnixMilli(), AppCode: appctx.FromContext(ctx), Platform: req.Platform, Language: req.Language, Timezone: req.Timezone, }, Password: req.Password, Username: req.Username, Avatar: req.Avatar, Gender: req.Gender, Country: req.Country, DeviceId: req.DeviceID, Source: req.Source, InstallChannel: req.InstallChannel, Platform: req.Platform, Language: req.Language, Timezone: req.Timezone, }) if err != nil { return nil, err } token := resp.GetToken() return &QuickCreateAccountResult{ UserID: token.GetUserId(), DisplayUserID: token.GetDisplayUserId(), AccessToken: token.GetAccessToken(), }, nil } func (c *GRPCClient) GetUser(ctx context.Context, req GetUserRequest) (*User, error) { resp, err := c.client.GetUser(ctx, &userv1.GetUserRequest{ Meta: &userv1.RequestMeta{ RequestId: req.RequestID, Caller: req.Caller, SentAtMs: time.Now().UnixMilli(), AppCode: appctx.FromContext(ctx), }, UserId: req.UserID, }) if err != nil { return nil, err } return fromProtoUser(resp.GetUser()), nil } func (c *GRPCClient) GetRegisterRiskConfig(ctx context.Context, req GetRegisterRiskConfigRequest) (*RegisterRiskConfig, error) { resp, err := c.authClient.GetRegisterRiskConfig(ctx, &userv1.GetRegisterRiskConfigRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), }) if err != nil { return nil, err } return fromProtoRegisterRiskConfig(resp.GetConfig()), nil } func (c *GRPCClient) UpdateRegisterRiskConfig(ctx context.Context, req UpdateRegisterRiskConfigRequest) (*RegisterRiskConfig, error) { resp, err := c.authClient.UpdateRegisterRiskConfig(ctx, &userv1.UpdateRegisterRiskConfigRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), MaxAccountsPerDevice: req.MaxAccountsPerDevice, OperatorAdminId: req.OperatorAdminID, }) if err != nil { return nil, err } return fromProtoRegisterRiskConfig(resp.GetConfig()), nil } func (c *GRPCClient) RefreshLoginRiskWhitelistCache(ctx context.Context, req RefreshLoginRiskWhitelistCacheRequest) (*LoginRiskWhitelistCacheSnapshot, error) { resp, err := c.authClient.RefreshLoginRiskWhitelistCache(ctx, &userv1.RefreshLoginRiskWhitelistCacheRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), }) if err != nil { return nil, err } return &LoginRiskWhitelistCacheSnapshot{ Refreshed: resp.GetRefreshed(), IPWhitelistCount: resp.GetIpWhitelistCount(), UserWhitelistCount: resp.GetUserWhitelistCount(), UserCacheExpiresAtMs: resp.GetUserCacheExpiresAtMs(), }, nil } // BatchGetUsers 复用 user-service 主数据 RPC,让后台列表只做展示聚合,不复制用户资料到游戏库。 func (c *GRPCClient) BatchGetUsers(ctx context.Context, req BatchGetUsersRequest) (map[int64]*User, error) { resp, err := c.client.BatchGetUsers(ctx, &userv1.BatchGetUsersRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), UserIds: req.UserIDs, }) if err != nil { return nil, err } users := make(map[int64]*User, len(resp.GetUsers())) for userID, user := range resp.GetUsers() { users[userID] = fromProtoUser(user) } return users, nil } func (c *GRPCClient) AdminChangeUserCountry(ctx context.Context, req AdminChangeUserCountryRequest) (*ChangeUserCountryResult, error) { resp, err := c.client.AdminChangeUserCountry(ctx, &userv1.AdminChangeUserCountryRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), TargetUserId: req.TargetUserID, Country: req.Country, AdminUserId: req.AdminUserID, Reason: req.Reason, }) if err != nil { return nil, err } return &ChangeUserCountryResult{ User: fromProtoUser(resp.GetUser()), OldRegionID: resp.GetOldRegionId(), NewRegionID: resp.GetNewRegionId(), RegionChanged: resp.GetRegionChanged(), }, nil } func (c *GRPCClient) SetUserStatus(ctx context.Context, req SetUserStatusRequest) (*SetUserStatusResult, error) { resp, err := c.client.SetUserStatus(ctx, &userv1.SetUserStatusRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), TargetUserId: req.TargetUserID, Status: userStatusToProto(req.Status), OperatorType: req.OperatorType, OperatorUserId: req.OperatorUserID, Reason: req.Reason, }) if err != nil { return nil, err } return &SetUserStatusResult{ User: fromProtoUser(resp.GetUser()), RevokedSessionCount: resp.GetRevokedSessionCount(), AccessTokenRevoked: resp.GetAccessTokenRevoked(), AccessTokenError: resp.GetAccessTokenRevokeError(), IMKicked: resp.GetImKicked(), IMKickError: resp.GetImKickError(), RoomEvicted: resp.GetRoomEvicted(), RoomID: resp.GetRoomId(), RTCKicked: resp.GetRtcKicked(), RTCKickError: resp.GetRtcKickError(), RoomEvictError: resp.GetRoomEvictError(), }, nil } func userStatusToProto(status string) userv1.UserStatus { switch status { case "active": return userv1.UserStatus_USER_STATUS_ACTIVE case "disabled": return userv1.UserStatus_USER_STATUS_DISABLED case "banned": return userv1.UserStatus_USER_STATUS_BANNED default: return userv1.UserStatus_USER_STATUS_UNSPECIFIED } } func (c *GRPCClient) ResolveDisplayUserID(ctx context.Context, req ResolveDisplayUserIDRequest) (*UserIdentity, error) { resp, err := c.identityClient.ResolveDisplayUserID(ctx, &userv1.ResolveDisplayUserIDRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), DisplayUserId: req.DisplayUserID, }) if err != nil { return nil, err } return fromProtoUserIdentity(resp.GetIdentity()), nil } func fromProtoRegisterRiskConfig(config *userv1.RegisterRiskConfig) *RegisterRiskConfig { if config == nil { return nil } return &RegisterRiskConfig{ AppCode: config.GetAppCode(), MaxAccountsPerDevice: config.GetMaxAccountsPerDevice(), UpdatedAtMs: config.GetUpdatedAtMs(), UpdatedByAdminID: config.GetUpdatedByAdminId(), } } func fromProtoUser(user *userv1.User) *User { if user == nil { return nil } return &User{ UserID: user.GetUserId(), Status: fromProtoStatus(user.GetStatus()), CreatedAtMs: user.GetCreatedAtMs(), UpdatedAtMs: user.GetUpdatedAtMs(), DisplayUserID: user.GetDisplayUserId(), DefaultDisplayUserID: user.GetDefaultDisplayUserId(), DisplayUserIDKind: user.GetDisplayUserIdKind(), DisplayUserIDExpiresAtMs: user.GetDisplayUserIdExpiresAtMs(), PrettyID: user.GetPrettyId(), PrettyDisplayUserID: user.GetPrettyDisplayUserId(), Username: user.GetUsername(), Gender: user.GetGender(), Country: user.GetCountry(), Avatar: user.GetAvatar(), Birth: user.GetBirth(), Language: user.GetLanguage(), CountryID: user.GetCountryId(), CountryName: user.GetCountryName(), CountryDisplayName: user.GetCountryDisplayName(), RegionID: user.GetRegionId(), RegionCode: user.GetRegionCode(), RegionName: user.GetRegionName(), ProfileCompleted: user.GetProfileCompleted(), ProfileCompletedAtMs: user.GetProfileCompletedAtMs(), OnboardingStatus: user.GetOnboardingStatus(), ISONumeric: user.GetIsoNumeric(), PhoneCountryCode: user.GetPhoneCountryCode(), CountryEnabled: user.GetCountryEnabled(), ContactInfo: user.GetContactInfo(), } } func fromProtoStatus(status userv1.UserStatus) string { switch status { case userv1.UserStatus_USER_STATUS_ACTIVE: return "active" case userv1.UserStatus_USER_STATUS_DISABLED: return "disabled" case userv1.UserStatus_USER_STATUS_BANNED: return "banned" default: return "" } } func fromProtoUserIdentity(identity *userv1.UserIdentity) *UserIdentity { if identity == nil { return nil } return &UserIdentity{ UserID: identity.GetUserId(), DisplayUserID: identity.GetDisplayUserId(), Status: identity.GetStatus(), DefaultDisplayUserID: identity.GetDefaultDisplayUserId(), DisplayUserIDKind: identity.GetDisplayUserIdKind(), DisplayUserIDExpiresAtMs: identity.GetDisplayUserIdExpiresAtMs(), PrettyID: identity.GetPrettyId(), PrettyDisplayUserID: identity.GetPrettyDisplayUserId(), AppCode: identity.GetAppCode(), } } type CreateBDLeaderRequest struct { RequestID string Caller string CommandID string AdminUserID int64 TargetUserID int64 Reason string } type CreateBDRequest struct { RequestID string Caller string CommandID string AdminUserID int64 TargetUserID int64 ParentLeaderUserID int64 Reason string } type SetBDStatusRequest struct { RequestID string Caller string CommandID string AdminUserID int64 TargetUserID int64 Role string Status string Reason string } type CreateCoinSellerRequest struct { RequestID string Caller string CommandID string AdminUserID int64 TargetUserID int64 Reason string CanManageSubCoinSellers *bool } type SetCoinSellerStatusRequest struct { RequestID string Caller string CommandID string AdminUserID int64 TargetUserID int64 Status string Reason string CanManageSubCoinSellers *bool } type ReviewCoinSellerSubApplicationRequest struct { RequestID string Caller string CommandID string AdminUserID int64 ApplicationID int64 Decision string Reason string } type GetCoinSellerProfileRequest struct { RequestID string Caller string UserID int64 } type GetUserRoleSummaryRequest struct { RequestID string Caller string UserID int64 } type CreateAgencyRequest struct { RequestID string Caller string CommandID string AdminUserID int64 OwnerUserID int64 ParentBDUserID int64 Name string JoinEnabled bool Reason string } type AdminAddAgencyHostRequest struct { RequestID string Caller string CommandID string AdminUserID int64 AgencyID int64 TargetUserID int64 Reason string } type CloseAgencyRequest struct { RequestID string Caller string CommandID string AdminUserID int64 AgencyID int64 Reason string } type DeleteAgencyRequest struct { RequestID string Caller string CommandID string AdminUserID int64 AgencyID int64 Reason string } type SetAgencyStatusRequest struct { RequestID string Caller string CommandID string AdminUserID int64 AgencyID int64 Status string Reason string } type SetAgencyJoinEnabledRequest struct { RequestID string Caller string CommandID string AdminUserID int64 AgencyID int64 JoinEnabled bool Reason string } type BDProfile struct { UserID int64 `json:"userId,string"` Role string `json:"role"` RegionID int64 `json:"regionId"` ParentLeaderUserID int64 `json:"parentLeaderUserId,string"` PositionAlias string `json:"positionAlias"` Status string `json:"status"` CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` DisplayUserID string `json:"displayUserId"` Username string `json:"username"` Avatar string `json:"avatar"` Contact string `json:"contact"` RegionName string `json:"regionName"` ParentLeaderDisplayID string `json:"parentLeaderDisplayUserId"` ParentLeaderUsername string `json:"parentLeaderUsername"` ParentLeaderAvatar string `json:"parentLeaderAvatar"` CreatedByDisplayUserID string `json:"createdByDisplayUserId"` CreatedByUsername string `json:"createdByUsername"` CreatedByAvatar string `json:"createdByAvatar"` AgencyCount int64 `json:"agencyCount"` SubBDCount int64 `json:"subBdCount"` SalaryWallet SalaryWallet `json:"salaryWallet"` } type Agency struct { AgencyID int64 `json:"agencyId,string"` OwnerUserID int64 `json:"ownerUserId,string"` RegionID int64 `json:"regionId"` ParentBDUserID int64 `json:"parentBdUserId,string"` Name string `json:"name"` Avatar string `json:"avatar"` Status string `json:"status"` JoinEnabled bool `json:"joinEnabled"` MaxHosts int32 `json:"maxHosts"` CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` OwnerDisplayUserID string `json:"ownerDisplayUserId"` OwnerUsername string `json:"ownerUsername"` OwnerAvatar string `json:"ownerAvatar"` Contact string `json:"contact"` ParentBDDisplayUserID string `json:"parentBdDisplayUserId"` ParentBDUsername string `json:"parentBdUsername"` ParentBDAvatar string `json:"parentBdAvatar"` RegionName string `json:"regionName"` ActiveHostCount int64 `json:"activeHostCount"` SalaryWallet SalaryWallet `json:"salaryWallet"` } type HostProfile struct { UserID int64 `json:"userId,string"` Status string `json:"status"` RegionID int64 `json:"regionId"` CurrentAgencyID int64 `json:"currentAgencyId,string"` CurrentMembershipID int64 `json:"currentMembershipId,string"` Source string `json:"source"` FirstBecameHostAtMs int64 `json:"firstBecameHostAtMs"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` DisplayUserID string `json:"displayUserId"` Username string `json:"username"` Avatar string `json:"avatar"` Contact string `json:"contact"` RegionName string `json:"regionName"` CurrentAgencyName string `json:"currentAgencyName"` Diamond int64 `json:"diamond"` CurrentAgencyOwnerUserID int64 `json:"currentAgencyOwnerUserId,string"` CurrentAgencyOwnerDisplayUserID string `json:"currentAgencyOwnerDisplayUserId"` CurrentAgencyOwnerUsername string `json:"currentAgencyOwnerUsername"` CurrentAgencyOwnerAvatar string `json:"currentAgencyOwnerAvatar"` SalaryWallet SalaryWallet `json:"salaryWallet"` } type SalaryWallet struct { Role string `json:"role"` UserID int64 `json:"userId,string"` AssetType string `json:"assetType"` AvailableAmount int64 `json:"availableAmount"` FrozenAmount int64 `json:"frozenAmount"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerProfile struct { UserID int64 `json:"userId,string"` Status string `json:"status"` MerchantAssetType string `json:"merchantAssetType"` CanManageSubCoinSellers bool `json:"canManageSubCoinSellers"` CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerListItem struct { UserID int64 `json:"userId,string"` DisplayUserID string `json:"displayUserId"` Username string `json:"username"` Avatar string `json:"avatar"` RegionID int64 `json:"regionId"` RegionName string `json:"regionName"` Status string `json:"status"` MerchantAssetType string `json:"merchantAssetType"` ContactInfo string `json:"contactInfo"` CanManageSubCoinSellers bool `json:"canManageSubCoinSellers"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerSubApplication struct { ApplicationID int64 `json:"applicationId,string"` CommandID string `json:"commandId"` ParentUserID int64 `json:"parentUserId,string"` TargetUserID int64 `json:"targetUserId,string"` Status string `json:"status"` RelationID int64 `json:"relationId,string"` ReviewedByAdminID int64 `json:"reviewedByAdminId,string"` ReviewReason string `json:"reviewReason"` ReviewedAtMs int64 `json:"reviewedAtMs"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerSubRelation struct { RelationID int64 `json:"relationId,string"` ParentUserID int64 `json:"parentUserId,string"` ChildUserID int64 `json:"childUserId,string"` Status string `json:"status"` CreatedByUserID int64 `json:"createdByUserId,string"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CoinSellerSubApplicationReviewResult struct { Application *CoinSellerSubApplication `json:"application"` Relation *CoinSellerSubRelation `json:"relation,omitempty"` Child *CoinSellerListItem `json:"child,omitempty"` } type UserRoleSummary struct { UserID int64 `json:"userId,string"` IsHost bool `json:"isHost"` IsAgency bool `json:"isAgency"` IsBD bool `json:"isBd"` IsBDLeader bool `json:"isBdLeader"` IsCoinSeller bool `json:"isCoinSeller"` HostStatus string `json:"hostStatus"` AgencyID int64 `json:"agencyId,string"` BDID int64 `json:"bdId,string"` BDStatus string `json:"bdStatus"` CoinSellerStatus string `json:"coinSellerStatus"` PendingRoleInvitations int64 `json:"pendingRoleInvitations"` IsManager bool `json:"isManager"` } type AgencyMembership struct { MembershipID int64 `json:"membershipId,string"` AgencyID int64 `json:"agencyId,string"` HostUserID int64 `json:"hostUserId,string"` RegionID int64 `json:"regionId"` MembershipType string `json:"membershipType"` Status string `json:"status"` JoinedAtMs int64 `json:"joinedAtMs"` EndedAtMs int64 `json:"endedAtMs"` EndedByUserID int64 `json:"endedByUserId,string"` EndedReason string `json:"endedReason"` CreatedAtMs int64 `json:"createdAtMs"` UpdatedAtMs int64 `json:"updatedAtMs"` } type CreateAgencyResult struct { Agency *Agency `json:"agency"` HostProfile *HostProfile `json:"hostProfile"` Membership *AgencyMembership `json:"membership"` } func (c *GRPCClient) CreateBDLeader(ctx context.Context, req CreateBDLeaderRequest) (*BDProfile, error) { resp, err := c.hostAdminClient.CreateBDLeader(ctx, &userv1.CreateBDLeaderRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, TargetUserId: req.TargetUserID, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoBDProfile(resp.GetBdProfile()), nil } func (c *GRPCClient) CreateBD(ctx context.Context, req CreateBDRequest) (*BDProfile, error) { resp, err := c.hostAdminClient.CreateBD(ctx, &userv1.CreateBDRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, TargetUserId: req.TargetUserID, ParentLeaderUserId: req.ParentLeaderUserID, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoBDProfile(resp.GetBdProfile()), nil } func (c *GRPCClient) SetBDStatus(ctx context.Context, req SetBDStatusRequest) (*BDProfile, error) { resp, err := c.hostAdminClient.SetBDStatus(ctx, &userv1.SetBDStatusRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, TargetUserId: req.TargetUserID, Role: req.Role, Status: req.Status, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoBDProfile(resp.GetBdProfile()), nil } func (c *GRPCClient) CreateCoinSeller(ctx context.Context, req CreateCoinSellerRequest) (*CoinSellerProfile, error) { protoReq := &userv1.CreateCoinSellerRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, TargetUserId: req.TargetUserID, Reason: req.Reason, } if req.CanManageSubCoinSellers != nil { // optional bool 只在后台显式提交时设置;nil 表示沿用 user-service 默认值。 protoReq.CanManageSubCoinSellers = req.CanManageSubCoinSellers } resp, err := c.hostAdminClient.CreateCoinSeller(ctx, protoReq) if err != nil { return nil, err } return fromProtoCoinSellerProfile(resp.GetCoinSellerProfile()), nil } func (c *GRPCClient) SetCoinSellerStatus(ctx context.Context, req SetCoinSellerStatusRequest) (*CoinSellerProfile, error) { protoReq := &userv1.SetCoinSellerStatusRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, TargetUserId: req.TargetUserID, Status: req.Status, Reason: req.Reason, } if req.CanManageSubCoinSellers != nil { // nil 表示“只改状态”,false 才表示关闭子币商管理权限。 protoReq.CanManageSubCoinSellers = req.CanManageSubCoinSellers } resp, err := c.hostAdminClient.SetCoinSellerStatus(ctx, protoReq) if err != nil { return nil, err } return fromProtoCoinSellerProfile(resp.GetCoinSellerProfile()), nil } func (c *GRPCClient) ReviewCoinSellerSubApplication(ctx context.Context, req ReviewCoinSellerSubApplicationRequest) (*CoinSellerSubApplicationReviewResult, error) { resp, err := c.hostAdminClient.ReviewSubCoinSellerApplication(ctx, &userv1.ReviewSubCoinSellerApplicationRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, ApplicationId: req.ApplicationID, Decision: req.Decision, Reason: req.Reason, }) if err != nil { return nil, err } return &CoinSellerSubApplicationReviewResult{ Application: fromProtoCoinSellerSubApplication(resp.GetApplication()), Relation: fromProtoCoinSellerSubRelation(resp.GetRelation()), Child: fromProtoCoinSellerListItem(resp.GetChild()), }, nil } func (c *GRPCClient) GetCoinSellerProfile(ctx context.Context, req GetCoinSellerProfileRequest) (*CoinSellerProfile, error) { resp, err := c.hostClient.GetCoinSellerProfile(ctx, &userv1.GetCoinSellerProfileRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), UserId: req.UserID, }) if err != nil { return nil, err } return fromProtoCoinSellerProfile(resp.GetCoinSellerProfile()), nil } func (c *GRPCClient) GetUserRoleSummary(ctx context.Context, req GetUserRoleSummaryRequest) (*UserRoleSummary, error) { resp, err := c.hostClient.GetUserRoleSummary(ctx, &userv1.GetUserRoleSummaryRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), UserId: req.UserID, }) if err != nil { return nil, err } return fromProtoUserRoleSummary(resp.GetSummary()), nil } func (c *GRPCClient) CreateAgency(ctx context.Context, req CreateAgencyRequest) (*CreateAgencyResult, error) { resp, err := c.hostAdminClient.CreateAgency(ctx, &userv1.CreateAgencyRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, OwnerUserId: req.OwnerUserID, ParentBdUserId: req.ParentBDUserID, Name: req.Name, JoinEnabled: req.JoinEnabled, Reason: req.Reason, }) if err != nil { return nil, err } return &CreateAgencyResult{ Agency: fromProtoAgency(resp.GetAgency()), HostProfile: fromProtoHostProfile(resp.GetHostProfile()), Membership: fromProtoAgencyMembership(resp.GetMembership()), }, nil } func (c *GRPCClient) AdminAddAgencyHost(ctx context.Context, req AdminAddAgencyHostRequest) (*CreateAgencyResult, error) { resp, err := c.hostAdminClient.AdminAddAgencyHost(ctx, &userv1.AdminAddAgencyHostRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, AgencyId: req.AgencyID, TargetUserId: req.TargetUserID, Reason: req.Reason, }) if err != nil { return nil, err } return &CreateAgencyResult{ Agency: fromProtoAgency(resp.GetAgency()), HostProfile: fromProtoHostProfile(resp.GetHostProfile()), Membership: fromProtoAgencyMembership(resp.GetMembership()), }, nil } func (c *GRPCClient) CloseAgency(ctx context.Context, req CloseAgencyRequest) (*Agency, error) { resp, err := c.hostAdminClient.CloseAgency(ctx, &userv1.CloseAgencyRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, AgencyId: req.AgencyID, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoAgency(resp.GetAgency()), nil } func (c *GRPCClient) DeleteAgency(ctx context.Context, req DeleteAgencyRequest) (*Agency, error) { resp, err := c.hostAdminClient.DeleteAgency(ctx, &userv1.DeleteAgencyRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, AgencyId: req.AgencyID, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoAgency(resp.GetAgency()), nil } func (c *GRPCClient) SetAgencyStatus(ctx context.Context, req SetAgencyStatusRequest) (*Agency, error) { resp, err := c.hostAdminClient.SetAgencyStatus(ctx, &userv1.SetAgencyStatusRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, AgencyId: req.AgencyID, Status: req.Status, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoAgency(resp.GetAgency()), nil } func (c *GRPCClient) SetAgencyJoinEnabled(ctx context.Context, req SetAgencyJoinEnabledRequest) (*Agency, error) { resp, err := c.hostAdminClient.SetAgencyJoinEnabled(ctx, &userv1.SetAgencyJoinEnabledRequest{ Meta: requestMeta(ctx, req.RequestID, req.Caller), CommandId: req.CommandID, AdminUserId: req.AdminUserID, AgencyId: req.AgencyID, JoinEnabled: req.JoinEnabled, Reason: req.Reason, }) if err != nil { return nil, err } return fromProtoAgency(resp.GetAgency()), nil } func requestMeta(ctx context.Context, requestID string, caller string) *userv1.RequestMeta { return &userv1.RequestMeta{ RequestId: requestID, Caller: caller, SentAtMs: time.Now().UnixMilli(), AppCode: appctx.FromContext(ctx), } } func fromProtoBDProfile(profile *userv1.BDProfile) *BDProfile { if profile == nil { return nil } return &BDProfile{ UserID: profile.GetUserId(), Role: profile.GetRole(), RegionID: profile.GetRegionId(), ParentLeaderUserID: profile.GetParentLeaderUserId(), Status: profile.GetStatus(), CreatedByUserID: profile.GetCreatedByUserId(), CreatedAtMs: profile.GetCreatedAtMs(), UpdatedAtMs: profile.GetUpdatedAtMs(), } } func fromProtoAgency(agency *userv1.Agency) *Agency { if agency == nil { return nil } return &Agency{ AgencyID: agency.GetAgencyId(), OwnerUserID: agency.GetOwnerUserId(), RegionID: agency.GetRegionId(), ParentBDUserID: agency.GetParentBdUserId(), Name: agency.GetName(), Avatar: agency.GetAvatar(), Status: agency.GetStatus(), JoinEnabled: agency.GetJoinEnabled(), MaxHosts: agency.GetMaxHosts(), CreatedByUserID: agency.GetCreatedByUserId(), CreatedAtMs: agency.GetCreatedAtMs(), UpdatedAtMs: agency.GetUpdatedAtMs(), ActiveHostCount: int64(agency.GetHostCount()), } } func fromProtoHostProfile(profile *userv1.HostProfile) *HostProfile { if profile == nil { return nil } return &HostProfile{ UserID: profile.GetUserId(), Status: profile.GetStatus(), RegionID: profile.GetRegionId(), CurrentAgencyID: profile.GetCurrentAgencyId(), CurrentMembershipID: profile.GetCurrentMembershipId(), Source: profile.GetSource(), FirstBecameHostAtMs: profile.GetFirstBecameHostAtMs(), CreatedAtMs: profile.GetCreatedAtMs(), UpdatedAtMs: profile.GetUpdatedAtMs(), } } func fromProtoCoinSellerProfile(profile *userv1.CoinSellerProfile) *CoinSellerProfile { if profile == nil { return nil } return &CoinSellerProfile{ UserID: profile.GetUserId(), Status: profile.GetStatus(), MerchantAssetType: profile.GetMerchantAssetType(), CanManageSubCoinSellers: profile.GetCanManageSubCoinSellers(), CreatedByUserID: profile.GetCreatedByUserId(), CreatedAtMs: profile.GetCreatedAtMs(), UpdatedAtMs: profile.GetUpdatedAtMs(), } } func fromProtoCoinSellerSubApplication(application *userv1.CoinSellerSubApplication) *CoinSellerSubApplication { if application == nil { return nil } return &CoinSellerSubApplication{ ApplicationID: application.GetApplicationId(), CommandID: application.GetCommandId(), ParentUserID: application.GetParentUserId(), TargetUserID: application.GetTargetUserId(), Status: application.GetStatus(), RelationID: application.GetRelationId(), ReviewedByAdminID: application.GetReviewedByAdminId(), ReviewReason: application.GetReviewReason(), ReviewedAtMs: application.GetReviewedAtMs(), CreatedAtMs: application.GetCreatedAtMs(), UpdatedAtMs: application.GetUpdatedAtMs(), } } func fromProtoCoinSellerSubRelation(relation *userv1.CoinSellerSubRelation) *CoinSellerSubRelation { if relation == nil { return nil } return &CoinSellerSubRelation{ RelationID: relation.GetRelationId(), ParentUserID: relation.GetParentUserId(), ChildUserID: relation.GetChildUserId(), Status: relation.GetStatus(), CreatedByUserID: relation.GetCreatedByUserId(), CreatedAtMs: relation.GetCreatedAtMs(), UpdatedAtMs: relation.GetUpdatedAtMs(), } } func fromProtoCoinSellerListItem(item *userv1.CoinSellerListItem) *CoinSellerListItem { if item == nil { return nil } return &CoinSellerListItem{ UserID: item.GetUserId(), DisplayUserID: item.GetDisplayUserId(), Username: item.GetUsername(), Avatar: item.GetAvatar(), RegionID: item.GetRegionId(), RegionName: item.GetRegionName(), Status: item.GetStatus(), MerchantAssetType: item.GetMerchantAssetType(), ContactInfo: item.GetContactInfo(), CanManageSubCoinSellers: item.GetCanManageSubCoinSellers(), UpdatedAtMs: item.GetUpdatedAtMs(), } } func fromProtoUserRoleSummary(summary *userv1.UserRoleSummary) *UserRoleSummary { if summary == nil { return nil } return &UserRoleSummary{ UserID: summary.GetUserId(), IsHost: summary.GetIsHost(), IsAgency: summary.GetIsAgency(), IsBD: summary.GetIsBd(), IsBDLeader: summary.GetIsBdLeader(), IsCoinSeller: summary.GetIsCoinSeller(), HostStatus: summary.GetHostStatus(), AgencyID: summary.GetAgencyId(), BDID: summary.GetBdId(), BDStatus: summary.GetBdStatus(), CoinSellerStatus: summary.GetCoinSellerStatus(), PendingRoleInvitations: summary.GetPendingRoleInvitations(), IsManager: summary.GetIsManager(), } } func fromProtoAgencyMembership(membership *userv1.AgencyMembership) *AgencyMembership { if membership == nil { return nil } return &AgencyMembership{ MembershipID: membership.GetMembershipId(), AgencyID: membership.GetAgencyId(), HostUserID: membership.GetHostUserId(), RegionID: membership.GetRegionId(), MembershipType: membership.GetMembershipType(), Status: membership.GetStatus(), JoinedAtMs: membership.GetJoinedAtMs(), EndedAtMs: membership.GetEndedAtMs(), EndedByUserID: membership.GetEndedByUserId(), EndedReason: membership.GetEndedReason(), CreatedAtMs: membership.GetCreatedAtMs(), UpdatedAtMs: membership.GetUpdatedAtMs(), } }