2026-07-09 18:15:57 +08:00

1187 lines
43 KiB
Go
Raw Permalink 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 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(),
}
}