经理中心配置
This commit is contained in:
parent
e370c3ca32
commit
53c6f361f9
@ -37,6 +37,15 @@ func registerManagerCenterRoutes(engine *gin.Engine, javaClient authGateway, ser
|
|||||||
writeOK(c, resp)
|
writeOK(c, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
group.GET("/countries", func(c *gin.Context) {
|
||||||
|
resp, err := service.ListCountries(c.Request.Context(), mustAuthUser(c))
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeOK(c, resp)
|
||||||
|
})
|
||||||
|
|
||||||
group.GET("/props", func(c *gin.Context) {
|
group.GET("/props", func(c *gin.Context) {
|
||||||
resp, err := service.ListProps(c.Request.Context(), mustAuthUser(c), c.Query("type"))
|
resp, err := service.ListProps(c.Request.Context(), mustAuthUser(c), c.Query("type"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -110,4 +119,18 @@ func registerManagerCenterRoutes(engine *gin.Engine, javaClient authGateway, ser
|
|||||||
}
|
}
|
||||||
writeOK(c, resp)
|
writeOK(c, resp)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
group.POST("/users/country", func(c *gin.Context) {
|
||||||
|
var req managercenter.ChangeUserCountryRequest
|
||||||
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
|
writeError(c, managercenter.NewAppError(http.StatusBadRequest, "bad_request", err.Error()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resp, err := service.ChangeUserCountry(c.Request.Context(), mustAuthUser(c), req)
|
||||||
|
if err != nil {
|
||||||
|
writeError(c, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
writeOK(c, resp)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"chatapp3-golang/internal/integration"
|
"chatapp3-golang/internal/integration"
|
||||||
"chatapp3-golang/internal/model"
|
"chatapp3-golang/internal/model"
|
||||||
@ -32,21 +33,38 @@ func (teamManagerInfoRow) TableName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type userBaseInfoRow struct {
|
type userBaseInfoRow struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
Account string `gorm:"column:account"`
|
Account string `gorm:"column:account"`
|
||||||
UserAvatar string `gorm:"column:user_avatar"`
|
UserAvatar string `gorm:"column:user_avatar"`
|
||||||
UserNickname string `gorm:"column:user_nickname"`
|
UserNickname string `gorm:"column:user_nickname"`
|
||||||
CountryCode string `gorm:"column:country_code"`
|
CountryID int64 `gorm:"column:country_id"`
|
||||||
CountryName string `gorm:"column:country_name"`
|
CountryCode string `gorm:"column:country_code"`
|
||||||
OriginSys string `gorm:"column:origin_sys"`
|
CountryName string `gorm:"column:country_name"`
|
||||||
AccountStatus string `gorm:"column:account_status"`
|
OriginSys string `gorm:"column:origin_sys"`
|
||||||
IsDel bool `gorm:"column:is_del"`
|
AccountStatus string `gorm:"column:account_status"`
|
||||||
|
IsDel bool `gorm:"column:is_del"`
|
||||||
|
UpdateTime time.Time `gorm:"column:update_time"`
|
||||||
|
UpdateUser int64 `gorm:"column:update_user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (userBaseInfoRow) TableName() string {
|
func (userBaseInfoRow) TableName() string {
|
||||||
return "user_base_info"
|
return "user_base_info"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type sysCountryCodeRow struct {
|
||||||
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
|
AlphaTwo string `gorm:"column:alpha_two"`
|
||||||
|
CountryName string `gorm:"column:country_name"`
|
||||||
|
NationalFlag string `gorm:"column:national_flag"`
|
||||||
|
PhonePrefix int `gorm:"column:phone_prefix"`
|
||||||
|
Open bool `gorm:"column:is_open"`
|
||||||
|
Sort int `gorm:"column:sort"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sysCountryCodeRow) TableName() string {
|
||||||
|
return "sys_country_code"
|
||||||
|
}
|
||||||
|
|
||||||
type propsSourceRecordRow struct {
|
type propsSourceRecordRow struct {
|
||||||
ID int64 `gorm:"column:id;primaryKey"`
|
ID int64 `gorm:"column:id;primaryKey"`
|
||||||
Type string `gorm:"column:type"`
|
Type string `gorm:"column:type"`
|
||||||
@ -106,6 +124,34 @@ func (s *Service) SearchUser(ctx context.Context, user AuthUser, account string)
|
|||||||
return &UserSearchResponse{User: view}, nil
|
return &UserSearchResponse{User: view}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListCountries 给经理中心返回当前开放国家,H5 只展示可绑定目标,避免经理手输无效国家码。
|
||||||
|
func (s *Service) ListCountries(ctx context.Context, user AuthUser) (*CountryListResponse, error) {
|
||||||
|
if err := s.ensureManager(ctx, user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if s.db == nil {
|
||||||
|
return nil, serviceUnavailable("database_unavailable", "database is unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows []sysCountryCodeRow
|
||||||
|
if err := s.db.WithContext(ctx).
|
||||||
|
Table("sys_country_code").
|
||||||
|
Select("id, alpha_two, country_name, national_flag, phone_prefix, is_open, sort").
|
||||||
|
Where("is_open = ?", true).
|
||||||
|
Order("sort ASC, country_name ASC, id ASC").
|
||||||
|
Find(&rows).Error; err != nil {
|
||||||
|
return nil, serverError("country_query_failed", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
records := make([]CountryView, 0, len(rows))
|
||||||
|
for _, row := range rows {
|
||||||
|
if view, ok := countryRowToView(row); ok {
|
||||||
|
records = append(records, view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &CountryListResponse{Records: records}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ListProps(ctx context.Context, user AuthUser, propsType string) (*PropsListResponse, error) {
|
func (s *Service) ListProps(ctx context.Context, user AuthUser, propsType string) (*PropsListResponse, error) {
|
||||||
if err := s.ensureManager(ctx, user); err != nil {
|
if err := s.ensureManager(ctx, user); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -332,6 +378,66 @@ func (s *Service) UnbanUser(ctx context.Context, user AuthUser, req UnbanUserReq
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChangeUserCountry 只允许经理移动普通用户国家;用户已有业务身份时拒绝,避免跨国家后团队、币商和主播归属数据不一致。
|
||||||
|
func (s *Service) ChangeUserCountry(ctx context.Context, user AuthUser, req ChangeUserCountryRequest) (*ChangeUserCountryResponse, error) {
|
||||||
|
if err := s.ensureManager(ctx, user); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if req.UserID.Int64() <= 0 {
|
||||||
|
return nil, badRequest("user_required", "userId is required")
|
||||||
|
}
|
||||||
|
if req.CountryID.Int64() <= 0 {
|
||||||
|
return nil, badRequest("country_required", "countryId is required")
|
||||||
|
}
|
||||||
|
if s.db == nil {
|
||||||
|
return nil, serviceUnavailable("database_unavailable", "database is unavailable")
|
||||||
|
}
|
||||||
|
|
||||||
|
target, err := s.loadUserRowByID(ctx, req.UserID.Int64())
|
||||||
|
if err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil, notFound("user_not_found", "user not found")
|
||||||
|
}
|
||||||
|
return nil, serverError("user_query_failed", err.Error())
|
||||||
|
}
|
||||||
|
country, err := s.loadOpenCountryByID(ctx, req.CountryID.Int64())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := s.ensureUserCountryMovable(ctx, target.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只改国家三元组,不触碰昵称、头像等资料审核字段;清理 Java 资料缓存后 App/H5 会重新读到新国家。
|
||||||
|
updates := map[string]any{
|
||||||
|
"country_id": country.ID,
|
||||||
|
"country_code": strings.ToUpper(strings.TrimSpace(country.AlphaTwo)),
|
||||||
|
"country_name": strings.TrimSpace(country.CountryName),
|
||||||
|
"update_time": time.Now(),
|
||||||
|
"update_user": user.UserID,
|
||||||
|
}
|
||||||
|
if err := s.db.WithContext(ctx).
|
||||||
|
Table("user_base_info").
|
||||||
|
Where("id = ? AND is_del = ?", target.ID, false).
|
||||||
|
Updates(updates).Error; err != nil {
|
||||||
|
return nil, serverError("user_country_update_failed", err.Error())
|
||||||
|
}
|
||||||
|
if s.java != nil {
|
||||||
|
if err := s.java.RemoveUserProfileCacheAll(ctx, target.ID); err != nil {
|
||||||
|
return nil, serverError("remove_profile_cache_failed", err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
target.CountryID = country.ID
|
||||||
|
target.CountryCode = strings.ToUpper(strings.TrimSpace(country.AlphaTwo))
|
||||||
|
target.CountryName = strings.TrimSpace(country.CountryName)
|
||||||
|
view, err := s.userRowToView(ctx, target)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &ChangeUserCountryResponse{Success: true, User: view}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ensureManager(ctx context.Context, user AuthUser) error {
|
func (s *Service) ensureManager(ctx context.Context, user AuthUser) error {
|
||||||
_, err := s.requireManagerInfo(ctx, user)
|
_, err := s.requireManagerInfo(ctx, user)
|
||||||
return err
|
return err
|
||||||
@ -444,6 +550,65 @@ func (s *Service) loadUserRowByAccount(ctx context.Context, account string) (use
|
|||||||
return row, err
|
return row, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) loadOpenCountryByID(ctx context.Context, countryID int64) (sysCountryCodeRow, error) {
|
||||||
|
var row sysCountryCodeRow
|
||||||
|
err := s.db.WithContext(ctx).
|
||||||
|
Table("sys_country_code").
|
||||||
|
Select("id, alpha_two, country_name, national_flag, phone_prefix, is_open, sort").
|
||||||
|
Where("id = ? AND is_open = ?", countryID, true).
|
||||||
|
First(&row).Error
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return sysCountryCodeRow{}, notFound("country_not_found", "country not found")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return sysCountryCodeRow{}, serverError("country_query_failed", err.Error())
|
||||||
|
}
|
||||||
|
if strings.TrimSpace(row.AlphaTwo) == "" {
|
||||||
|
return sysCountryCodeRow{}, badRequest("country_code_required", "country code is required")
|
||||||
|
}
|
||||||
|
return row, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureUserCountryMovable 串行检查所有会绑定归属关系的身份;任一命中都返回同一个业务错误,前端显示统一提示。
|
||||||
|
func (s *Service) ensureUserCountryMovable(ctx context.Context, userID int64) error {
|
||||||
|
locked, err := s.userHasLockedHistoryIdentity(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if locked {
|
||||||
|
return badRequest("user_identity_locked", "user has host, agency, BD, BD leader or coin seller identity and cannot be moved")
|
||||||
|
}
|
||||||
|
locked, err = s.userIsBDLeader(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if locked {
|
||||||
|
return badRequest("user_identity_locked", "user has host, agency, BD, BD leader or coin seller identity and cannot be moved")
|
||||||
|
}
|
||||||
|
if s.java == nil {
|
||||||
|
return serviceUnavailable("java_gateway_unavailable", "java gateway is unavailable")
|
||||||
|
}
|
||||||
|
locked, err = s.java.ExistsFreightSeller(ctx, userID)
|
||||||
|
if err != nil {
|
||||||
|
return serverError("coin_seller_query_failed", err.Error())
|
||||||
|
}
|
||||||
|
if locked {
|
||||||
|
return badRequest("user_identity_locked", "user has host, agency, BD, BD leader or coin seller identity and cannot be moved")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) userHasLockedHistoryIdentity(ctx context.Context, userID int64) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
if err := s.db.WithContext(ctx).
|
||||||
|
Table("user_history_identity").
|
||||||
|
Where("user_id = ? AND (is_host = ? OR is_agent = ? OR is_bd = ?)", userID, true, true, true).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return false, serverError("history_identity_query_failed", err.Error())
|
||||||
|
}
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) userRowToView(ctx context.Context, row userBaseInfoRow) (UserView, error) {
|
func (s *Service) userRowToView(ctx context.Context, row userBaseInfoRow) (UserView, error) {
|
||||||
hasRecharge, canBan := s.userRechargeEligibility(ctx, row.ID)
|
hasRecharge, canBan := s.userRechargeEligibility(ctx, row.ID)
|
||||||
canBan = canBan && !isUnbannableAccountStatus(row.AccountStatus)
|
canBan = canBan && !isUnbannableAccountStatus(row.AccountStatus)
|
||||||
@ -452,6 +617,7 @@ func (s *Service) userRowToView(ctx context.Context, row userBaseInfoRow) (UserV
|
|||||||
Account: row.Account,
|
Account: row.Account,
|
||||||
UserAvatar: row.UserAvatar,
|
UserAvatar: row.UserAvatar,
|
||||||
UserNickname: row.UserNickname,
|
UserNickname: row.UserNickname,
|
||||||
|
CountryID: ID(row.CountryID),
|
||||||
CountryCode: row.CountryCode,
|
CountryCode: row.CountryCode,
|
||||||
CountryName: row.CountryName,
|
CountryName: row.CountryName,
|
||||||
OriginSys: row.OriginSys,
|
OriginSys: row.OriginSys,
|
||||||
@ -469,6 +635,7 @@ func (s *Service) javaProfileToView(ctx context.Context, profile integration.Use
|
|||||||
Account: profile.Account,
|
Account: profile.Account,
|
||||||
UserAvatar: profile.UserAvatar,
|
UserAvatar: profile.UserAvatar,
|
||||||
UserNickname: profile.UserNickname,
|
UserNickname: profile.UserNickname,
|
||||||
|
CountryID: ID(profile.CountryID),
|
||||||
CountryCode: profile.CountryCode,
|
CountryCode: profile.CountryCode,
|
||||||
CountryName: profile.CountryName,
|
CountryName: profile.CountryName,
|
||||||
OriginSys: profile.OriginSys,
|
OriginSys: profile.OriginSys,
|
||||||
@ -494,6 +661,9 @@ func overlayJavaProfile(view *UserView, profile integration.UserProfile) {
|
|||||||
if profile.CountryName != "" {
|
if profile.CountryName != "" {
|
||||||
view.CountryName = profile.CountryName
|
view.CountryName = profile.CountryName
|
||||||
}
|
}
|
||||||
|
if int64(profile.CountryID) > 0 {
|
||||||
|
view.CountryID = ID(profile.CountryID)
|
||||||
|
}
|
||||||
if profile.OriginSys != "" {
|
if profile.OriginSys != "" {
|
||||||
view.OriginSys = profile.OriginSys
|
view.OriginSys = profile.OriginSys
|
||||||
}
|
}
|
||||||
@ -505,6 +675,21 @@ func overlayJavaProfile(view *UserView, profile integration.UserProfile) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func countryRowToView(row sysCountryCodeRow) (CountryView, bool) {
|
||||||
|
code := strings.ToUpper(strings.TrimSpace(row.AlphaTwo))
|
||||||
|
name := strings.TrimSpace(row.CountryName)
|
||||||
|
if row.ID <= 0 || code == "" || name == "" {
|
||||||
|
return CountryView{}, false
|
||||||
|
}
|
||||||
|
return CountryView{
|
||||||
|
ID: ID(row.ID),
|
||||||
|
CountryCode: code,
|
||||||
|
CountryName: name,
|
||||||
|
NationalFlag: strings.TrimSpace(row.NationalFlag),
|
||||||
|
PhonePrefix: row.PhonePrefix,
|
||||||
|
}, true
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) userHasRecharge(ctx context.Context, userID int64) (bool, error) {
|
func (s *Service) userHasRecharge(ctx context.Context, userID int64) (bool, error) {
|
||||||
if userID <= 0 {
|
if userID <= 0 {
|
||||||
return false, nil
|
return false, nil
|
||||||
|
|||||||
@ -17,6 +17,8 @@ import (
|
|||||||
type fakeManagerGateway struct {
|
type fakeManagerGateway struct {
|
||||||
recharges map[int64][]integration.UserTotalRecharge
|
recharges map[int64][]integration.UserTotalRecharge
|
||||||
rechargeErr error
|
rechargeErr error
|
||||||
|
freightSellers map[int64]bool
|
||||||
|
freightSellerErr error
|
||||||
abilities map[int64]integration.PropsNobleVIPAbility
|
abilities map[int64]integration.PropsNobleVIPAbility
|
||||||
maxAbility integration.PropsNobleVIPAbility
|
maxAbility integration.PropsNobleVIPAbility
|
||||||
giveRequests []integration.GivePropsBackpackRequest
|
giveRequests []integration.GivePropsBackpackRequest
|
||||||
@ -52,6 +54,13 @@ func (f *fakeManagerGateway) MapUserTotalRecharge(_ context.Context, userIDs []i
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeManagerGateway) ExistsFreightSeller(_ context.Context, userID int64) (bool, error) {
|
||||||
|
if f.freightSellerErr != nil {
|
||||||
|
return false, f.freightSellerErr
|
||||||
|
}
|
||||||
|
return f.freightSellers[userID], nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fakeManagerGateway) GivePropsBackpack(_ context.Context, req integration.GivePropsBackpackRequest) error {
|
func (f *fakeManagerGateway) GivePropsBackpack(_ context.Context, req integration.GivePropsBackpackRequest) error {
|
||||||
f.giveRequests = append(f.giveRequests, req)
|
f.giveRequests = append(f.giveRequests, req)
|
||||||
return nil
|
return nil
|
||||||
@ -732,6 +741,98 @@ func TestUnbanUserAllowsFrozenUser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListCountriesReturnsOpenCountries(t *testing.T) {
|
||||||
|
service, _ := newManagerCenterTestService(t)
|
||||||
|
db := service.db.(*gorm.DB)
|
||||||
|
seedManager(t, db, 1)
|
||||||
|
seedCountries(t, db,
|
||||||
|
sysCountryCodeRow{ID: 10, AlphaTwo: "SA", CountryName: "Saudi Arabia", Open: true, Sort: 2},
|
||||||
|
sysCountryCodeRow{ID: 11, AlphaTwo: "AE", CountryName: "United Arab Emirates", Open: true, Sort: 1},
|
||||||
|
sysCountryCodeRow{ID: 12, AlphaTwo: "XX", CountryName: "Closed", Open: false, Sort: 3},
|
||||||
|
)
|
||||||
|
|
||||||
|
resp, err := service.ListCountries(context.Background(), AuthUser{UserID: 1})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListCountries() error = %v", err)
|
||||||
|
}
|
||||||
|
if len(resp.Records) != 2 {
|
||||||
|
t.Fatalf("countries = %+v, want two open rows", resp.Records)
|
||||||
|
}
|
||||||
|
if resp.Records[0].CountryCode != "AE" || resp.Records[1].CountryCode != "SA" {
|
||||||
|
t.Fatalf("countries = %+v, want sort order AE then SA", resp.Records)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangeUserCountryUpdatesCountryAndClearsCache(t *testing.T) {
|
||||||
|
service, fake := newManagerCenterTestService(t)
|
||||||
|
db := service.db.(*gorm.DB)
|
||||||
|
seedManager(t, db, 1)
|
||||||
|
seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2", CountryID: 10, CountryCode: "SA", CountryName: "Saudi Arabia"})
|
||||||
|
seedCountries(t, db, sysCountryCodeRow{ID: 11, AlphaTwo: "AE", CountryName: "United Arab Emirates", Open: true})
|
||||||
|
|
||||||
|
resp, err := service.ChangeUserCountry(context.Background(), AuthUser{UserID: 1}, ChangeUserCountryRequest{
|
||||||
|
UserID: ID(2),
|
||||||
|
CountryID: ID(11),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ChangeUserCountry() error = %v", err)
|
||||||
|
}
|
||||||
|
if !resp.Success || resp.User.CountryCode != "AE" || resp.User.CountryName != "United Arab Emirates" {
|
||||||
|
t.Fatalf("resp = %+v, want changed country", resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
var row userBaseInfoRow
|
||||||
|
if err := db.Where("id = ?", int64(2)).First(&row).Error; err != nil {
|
||||||
|
t.Fatalf("load user: %v", err)
|
||||||
|
}
|
||||||
|
if row.CountryID != 11 || row.CountryCode != "AE" || row.CountryName != "United Arab Emirates" || row.UpdateUser != 1 {
|
||||||
|
t.Fatalf("user row = %+v, want AE country and manager updater", row)
|
||||||
|
}
|
||||||
|
if !equalInt64Slice(fake.cacheCleared, []int64{2}) {
|
||||||
|
t.Fatalf("cacheCleared = %+v, want [2]", fake.cacheCleared)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangeUserCountryRejectsHistoryIdentity(t *testing.T) {
|
||||||
|
service, _ := newManagerCenterTestService(t)
|
||||||
|
db := service.db.(*gorm.DB)
|
||||||
|
seedManager(t, db, 1)
|
||||||
|
seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2", CountryID: 10, CountryCode: "SA"})
|
||||||
|
seedCountries(t, db, sysCountryCodeRow{ID: 11, AlphaTwo: "AE", CountryName: "United Arab Emirates", Open: true})
|
||||||
|
if err := db.Create(&userHistoryIdentityRow{UserID: 2, IsHost: true}).Error; err != nil {
|
||||||
|
t.Fatalf("seed identity: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := service.ChangeUserCountry(context.Background(), AuthUser{UserID: 1}, ChangeUserCountryRequest{UserID: ID(2), CountryID: ID(11)})
|
||||||
|
assertAppErrorCode(t, err, "user_identity_locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangeUserCountryRejectsBDLeader(t *testing.T) {
|
||||||
|
service, _ := newManagerCenterTestService(t)
|
||||||
|
db := service.db.(*gorm.DB)
|
||||||
|
seedManager(t, db, 1)
|
||||||
|
seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2", CountryID: 10, CountryCode: "SA"})
|
||||||
|
seedCountries(t, db, sysCountryCodeRow{ID: 11, AlphaTwo: "AE", CountryName: "United Arab Emirates", Open: true})
|
||||||
|
if err := db.Create(&bdLeaderRow{ID: 20, UserID: 2, Origin: "LIKEI", RegionID: "SA"}).Error; err != nil {
|
||||||
|
t.Fatalf("seed bd leader: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := service.ChangeUserCountry(context.Background(), AuthUser{UserID: 1}, ChangeUserCountryRequest{UserID: ID(2), CountryID: ID(11)})
|
||||||
|
assertAppErrorCode(t, err, "user_identity_locked")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestChangeUserCountryRejectsCoinSeller(t *testing.T) {
|
||||||
|
service, fake := newManagerCenterTestService(t)
|
||||||
|
db := service.db.(*gorm.DB)
|
||||||
|
seedManager(t, db, 1)
|
||||||
|
seedUsers(t, db, userBaseInfoRow{ID: 2, Account: "2", CountryID: 10, CountryCode: "SA"})
|
||||||
|
seedCountries(t, db, sysCountryCodeRow{ID: 11, AlphaTwo: "AE", CountryName: "United Arab Emirates", Open: true})
|
||||||
|
fake.freightSellers[2] = true
|
||||||
|
|
||||||
|
_, err := service.ChangeUserCountry(context.Background(), AuthUser{UserID: 1}, ChangeUserCountryRequest{UserID: ID(2), CountryID: ID(11)})
|
||||||
|
assertAppErrorCode(t, err, "user_identity_locked")
|
||||||
|
}
|
||||||
|
|
||||||
func newManagerCenterTestService(t *testing.T) (*Service, *fakeManagerGateway) {
|
func newManagerCenterTestService(t *testing.T) (*Service, *fakeManagerGateway) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=shared"), &gorm.Config{})
|
||||||
@ -741,6 +842,7 @@ func newManagerCenterTestService(t *testing.T) (*Service, *fakeManagerGateway) {
|
|||||||
if err := db.AutoMigrate(
|
if err := db.AutoMigrate(
|
||||||
&teamManagerInfoRow{},
|
&teamManagerInfoRow{},
|
||||||
&userBaseInfoRow{},
|
&userBaseInfoRow{},
|
||||||
|
&sysCountryCodeRow{},
|
||||||
&bdLeaderRow{},
|
&bdLeaderRow{},
|
||||||
&businessDevelopmentBaseInfoRow{},
|
&businessDevelopmentBaseInfoRow{},
|
||||||
&userHistoryIdentityRow{},
|
&userHistoryIdentityRow{},
|
||||||
@ -755,8 +857,9 @@ func newManagerCenterTestService(t *testing.T) (*Service, *fakeManagerGateway) {
|
|||||||
t.Fatalf("auto migrate: %v", err)
|
t.Fatalf("auto migrate: %v", err)
|
||||||
}
|
}
|
||||||
fake := &fakeManagerGateway{
|
fake := &fakeManagerGateway{
|
||||||
recharges: map[int64][]integration.UserTotalRecharge{},
|
recharges: map[int64][]integration.UserTotalRecharge{},
|
||||||
abilities: map[int64]integration.PropsNobleVIPAbility{},
|
freightSellers: map[int64]bool{},
|
||||||
|
abilities: map[int64]integration.PropsNobleVIPAbility{},
|
||||||
}
|
}
|
||||||
return NewService(db, fake), fake
|
return NewService(db, fake), fake
|
||||||
}
|
}
|
||||||
@ -803,6 +906,15 @@ func seedUsers(t *testing.T, db *gorm.DB, rows ...userBaseInfoRow) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func seedCountries(t *testing.T, db *gorm.DB, rows ...sysCountryCodeRow) {
|
||||||
|
t.Helper()
|
||||||
|
for _, row := range rows {
|
||||||
|
if err := db.Create(&row).Error; err != nil {
|
||||||
|
t.Fatalf("seed country: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func seedProps(t *testing.T, db *gorm.DB, rows ...propsSourceRecordRow) {
|
func seedProps(t *testing.T, db *gorm.DB, rows ...propsSourceRecordRow) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
for _, row := range rows {
|
for _, row := range rows {
|
||||||
|
|||||||
@ -60,6 +60,7 @@ type managerGateway interface {
|
|||||||
GetUserProfile(ctx context.Context, userID int64) (integration.UserProfile, error)
|
GetUserProfile(ctx context.Context, userID int64) (integration.UserProfile, error)
|
||||||
GetUserProfileByAccountOne(ctx context.Context, account string) (integration.UserProfile, error)
|
GetUserProfileByAccountOne(ctx context.Context, account string) (integration.UserProfile, error)
|
||||||
MapUserTotalRecharge(ctx context.Context, userIDs []int64) (map[int64][]integration.UserTotalRecharge, error)
|
MapUserTotalRecharge(ctx context.Context, userIDs []int64) (map[int64][]integration.UserTotalRecharge, error)
|
||||||
|
ExistsFreightSeller(ctx context.Context, userID int64) (bool, error)
|
||||||
GivePropsBackpack(ctx context.Context, req integration.GivePropsBackpackRequest) error
|
GivePropsBackpack(ctx context.Context, req integration.GivePropsBackpackRequest) error
|
||||||
GetNobleVIPAbility(ctx context.Context, sourceID int64) (integration.PropsNobleVIPAbility, error)
|
GetNobleVIPAbility(ctx context.Context, sourceID int64) (integration.PropsNobleVIPAbility, error)
|
||||||
GetUserMaxNobleVIPAbility(ctx context.Context, userID int64) (integration.PropsNobleVIPAbility, error)
|
GetUserMaxNobleVIPAbility(ctx context.Context, userID int64) (integration.PropsNobleVIPAbility, error)
|
||||||
@ -130,11 +131,24 @@ type UserSearchResponse struct {
|
|||||||
User UserView `json:"user"`
|
User UserView `json:"user"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CountryListResponse struct {
|
||||||
|
Records []CountryView `json:"records"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CountryView struct {
|
||||||
|
ID ID `json:"id"`
|
||||||
|
CountryCode string `json:"countryCode"`
|
||||||
|
CountryName string `json:"countryName"`
|
||||||
|
NationalFlag string `json:"nationalFlag,omitempty"`
|
||||||
|
PhonePrefix int `json:"phonePrefix,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type UserView struct {
|
type UserView struct {
|
||||||
UserID ID `json:"userId"`
|
UserID ID `json:"userId"`
|
||||||
Account string `json:"account"`
|
Account string `json:"account"`
|
||||||
UserAvatar string `json:"userAvatar"`
|
UserAvatar string `json:"userAvatar"`
|
||||||
UserNickname string `json:"userNickname"`
|
UserNickname string `json:"userNickname"`
|
||||||
|
CountryID ID `json:"countryId,omitempty"`
|
||||||
CountryCode string `json:"countryCode,omitempty"`
|
CountryCode string `json:"countryCode,omitempty"`
|
||||||
CountryName string `json:"countryName,omitempty"`
|
CountryName string `json:"countryName,omitempty"`
|
||||||
OriginSys string `json:"originSys,omitempty"`
|
OriginSys string `json:"originSys,omitempty"`
|
||||||
@ -196,6 +210,16 @@ type UnbanUserResponse struct {
|
|||||||
AccountStatus string `json:"accountStatus"`
|
AccountStatus string `json:"accountStatus"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChangeUserCountryRequest struct {
|
||||||
|
UserID ID `json:"userId"`
|
||||||
|
CountryID ID `json:"countryId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChangeUserCountryResponse struct {
|
||||||
|
Success bool `json:"success"`
|
||||||
|
User UserView `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
type BDLeaderListResponse struct {
|
type BDLeaderListResponse struct {
|
||||||
Count int `json:"count"`
|
Count int `json:"count"`
|
||||||
Records []BDLeaderView `json:"records"`
|
Records []BDLeaderView `json:"records"`
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user