经理权限和身份自动赠送
This commit is contained in:
parent
b97e5f0011
commit
9a19267c6a
@ -9,11 +9,13 @@ CREATE TABLE IF NOT EXISTS manager_profiles (
|
|||||||
contact_info VARCHAR(128) NOT NULL DEFAULT '' COMMENT '联系信息',
|
contact_info VARCHAR(128) NOT NULL DEFAULT '' COMMENT '联系信息',
|
||||||
can_grant_avatar_frame TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送头像框',
|
can_grant_avatar_frame TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送头像框',
|
||||||
can_grant_vehicle TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送座驾',
|
can_grant_vehicle TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送座驾',
|
||||||
|
can_grant_badge TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送徽章',
|
||||||
can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心升级用户等级',
|
can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心升级用户等级',
|
||||||
can_add_bd_leader TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 BD Leader',
|
can_add_bd_leader TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 BD Leader',
|
||||||
can_add_admin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Admin',
|
can_add_admin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Admin',
|
||||||
can_add_superadmin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Superadmin',
|
can_add_superadmin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Superadmin',
|
||||||
can_block_user TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心封禁用户',
|
can_block_user TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心封禁用户',
|
||||||
|
can_transfer_user_country TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心转移用户国家',
|
||||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台管理员 ID',
|
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台管理员 ID',
|
||||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||||
@ -48,9 +50,18 @@ PREPARE stmt FROM @ddl;
|
|||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
DEALLOCATE PREPARE stmt;
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
SET @ddl := IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'manager_profiles' AND COLUMN_NAME = 'can_grant_badge') = 0,
|
||||||
|
'ALTER TABLE manager_profiles ADD COLUMN can_grant_badge TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否允许在经理中心赠送徽章'' AFTER can_grant_vehicle',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @ddl;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
SET @ddl := IF(
|
SET @ddl := IF(
|
||||||
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'manager_profiles' AND COLUMN_NAME = 'can_update_user_level') = 0,
|
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'manager_profiles' AND COLUMN_NAME = 'can_update_user_level') = 0,
|
||||||
'ALTER TABLE manager_profiles ADD COLUMN can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否允许在经理中心升级用户等级'' AFTER can_grant_vehicle',
|
'ALTER TABLE manager_profiles ADD COLUMN can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否允许在经理中心升级用户等级'' AFTER can_grant_badge',
|
||||||
'SELECT 1'
|
'SELECT 1'
|
||||||
);
|
);
|
||||||
PREPARE stmt FROM @ddl;
|
PREPARE stmt FROM @ddl;
|
||||||
@ -93,9 +104,18 @@ PREPARE stmt FROM @ddl;
|
|||||||
EXECUTE stmt;
|
EXECUTE stmt;
|
||||||
DEALLOCATE PREPARE stmt;
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
|
SET @ddl := IF(
|
||||||
|
(SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'manager_profiles' AND COLUMN_NAME = 'can_transfer_user_country') = 0,
|
||||||
|
'ALTER TABLE manager_profiles ADD COLUMN can_transfer_user_country TINYINT(1) NOT NULL DEFAULT 1 COMMENT ''是否允许在经理中心转移用户国家'' AFTER can_block_user',
|
||||||
|
'SELECT 1'
|
||||||
|
);
|
||||||
|
PREPARE stmt FROM @ddl;
|
||||||
|
EXECUTE stmt;
|
||||||
|
DEALLOCATE PREPARE stmt;
|
||||||
|
|
||||||
INSERT INTO manager_profiles (
|
INSERT INTO manager_profiles (
|
||||||
app_code, user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle,
|
app_code, user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle, can_grant_badge,
|
||||||
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user,
|
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user, can_transfer_user_country,
|
||||||
created_by_admin_id, created_at_ms, updated_at_ms
|
created_by_admin_id, created_at_ms, updated_at_ms
|
||||||
)
|
)
|
||||||
SELECT
|
SELECT
|
||||||
@ -110,6 +130,8 @@ SELECT
|
|||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
1,
|
||||||
|
1,
|
||||||
0,
|
0,
|
||||||
MIN(bl.created_at_ms),
|
MIN(bl.created_at_ms),
|
||||||
MAX(bl.updated_at_ms)
|
MAX(bl.updated_at_ms)
|
||||||
|
|||||||
@ -53,11 +53,13 @@ type ManagerListItem struct {
|
|||||||
Contact string `json:"contact"`
|
Contact string `json:"contact"`
|
||||||
CanGrantAvatarFrame bool `json:"canGrantAvatarFrame"`
|
CanGrantAvatarFrame bool `json:"canGrantAvatarFrame"`
|
||||||
CanGrantVehicle bool `json:"canGrantVehicle"`
|
CanGrantVehicle bool `json:"canGrantVehicle"`
|
||||||
|
CanGrantBadge bool `json:"canGrantBadge"`
|
||||||
CanUpdateUserLevel bool `json:"canUpdateUserLevel"`
|
CanUpdateUserLevel bool `json:"canUpdateUserLevel"`
|
||||||
CanAddBDLeader bool `json:"canAddBdLeader"`
|
CanAddBDLeader bool `json:"canAddBdLeader"`
|
||||||
CanAddAdmin bool `json:"canAddAdmin"`
|
CanAddAdmin bool `json:"canAddAdmin"`
|
||||||
CanAddSuperadmin bool `json:"canAddSuperadmin"`
|
CanAddSuperadmin bool `json:"canAddSuperadmin"`
|
||||||
CanBlockUser bool `json:"canBlockUser"`
|
CanBlockUser bool `json:"canBlockUser"`
|
||||||
|
CanTransferCountry bool `json:"canTransferUserCountry"`
|
||||||
BDLeaderCount int64 `json:"bdLeaderCount"`
|
BDLeaderCount int64 `json:"bdLeaderCount"`
|
||||||
LastInvitedAtMs int64 `json:"lastInvitedAtMs"`
|
LastInvitedAtMs int64 `json:"lastInvitedAtMs"`
|
||||||
CreatedAtMs int64 `json:"createdAtMs"`
|
CreatedAtMs int64 `json:"createdAtMs"`
|
||||||
@ -600,24 +602,26 @@ func (r *Reader) CreateManagerProfile(ctx context.Context, userID int64, actorID
|
|||||||
// 不新增第二条身份,也不触碰该经理已经邀请出的 BD Leader/Admin 归属统计。
|
// 不新增第二条身份,也不触碰该经理已经邀请出的 BD Leader/Admin 归属统计。
|
||||||
if _, err := r.db.ExecContext(ctx, `
|
if _, err := r.db.ExecContext(ctx, `
|
||||||
INSERT INTO manager_profiles (
|
INSERT INTO manager_profiles (
|
||||||
app_code, user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle,
|
app_code, user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle, can_grant_badge,
|
||||||
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user,
|
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user, can_transfer_user_country,
|
||||||
created_by_admin_id, created_at_ms, updated_at_ms
|
created_by_admin_id, created_at_ms, updated_at_ms
|
||||||
) VALUES (?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
) VALUES (?, ?, 'active', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
status = 'active',
|
status = 'active',
|
||||||
contact_info = VALUES(contact_info),
|
contact_info = VALUES(contact_info),
|
||||||
can_grant_avatar_frame = VALUES(can_grant_avatar_frame),
|
can_grant_avatar_frame = VALUES(can_grant_avatar_frame),
|
||||||
can_grant_vehicle = VALUES(can_grant_vehicle),
|
can_grant_vehicle = VALUES(can_grant_vehicle),
|
||||||
|
can_grant_badge = VALUES(can_grant_badge),
|
||||||
can_update_user_level = VALUES(can_update_user_level),
|
can_update_user_level = VALUES(can_update_user_level),
|
||||||
can_add_bd_leader = VALUES(can_add_bd_leader),
|
can_add_bd_leader = VALUES(can_add_bd_leader),
|
||||||
can_add_admin = VALUES(can_add_admin),
|
can_add_admin = VALUES(can_add_admin),
|
||||||
can_add_superadmin = VALUES(can_add_superadmin),
|
can_add_superadmin = VALUES(can_add_superadmin),
|
||||||
can_block_user = VALUES(can_block_user),
|
can_block_user = VALUES(can_block_user),
|
||||||
|
can_transfer_user_country = VALUES(can_transfer_user_country),
|
||||||
updated_at_ms = VALUES(updated_at_ms)
|
updated_at_ms = VALUES(updated_at_ms)
|
||||||
`, appCode, userID, contact, permissions.CanGrantAvatarFrame, permissions.CanGrantVehicle,
|
`, appCode, userID, contact, permissions.CanGrantAvatarFrame, permissions.CanGrantVehicle,
|
||||||
permissions.CanUpdateUserLevel, permissions.CanAddBDLeader, permissions.CanAddAdmin,
|
permissions.CanGrantBadge, permissions.CanUpdateUserLevel, permissions.CanAddBDLeader, permissions.CanAddAdmin,
|
||||||
permissions.CanAddSuperadmin, permissions.CanBlockUser, actorID, nowMs, nowMs); err != nil {
|
permissions.CanAddSuperadmin, permissions.CanBlockUser, permissions.CanTransferCountry, actorID, nowMs, nowMs); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
items, _, err := r.ListManagers(ctx, listQuery{Keyword: strconv.FormatInt(userID, 10), Page: 1, PageSize: 1})
|
items, _, err := r.ListManagers(ctx, listQuery{Keyword: strconv.FormatInt(userID, 10), Page: 1, PageSize: 1})
|
||||||
@ -716,15 +720,15 @@ func (r *Reader) ListManagers(ctx context.Context, query listQuery) ([]*ManagerL
|
|||||||
SELECT mp.user_id, COALESCE(manager.current_display_user_id, ''), COALESCE(manager.username, ''),
|
SELECT mp.user_id, COALESCE(manager.current_display_user_id, ''), COALESCE(manager.username, ''),
|
||||||
COALESCE(manager.avatar, ''), `+userCountryRegionIDSQL("manager_region")+`, `+userCountryRegionNameSQL("manager_region")+`,
|
COALESCE(manager.avatar, ''), `+userCountryRegionIDSQL("manager_region")+`, `+userCountryRegionNameSQL("manager_region")+`,
|
||||||
mp.status, COALESCE(mp.contact_info, ''),
|
mp.status, COALESCE(mp.contact_info, ''),
|
||||||
mp.can_grant_avatar_frame, mp.can_grant_vehicle, mp.can_update_user_level,
|
mp.can_grant_avatar_frame, mp.can_grant_vehicle, mp.can_grant_badge, mp.can_update_user_level,
|
||||||
mp.can_add_bd_leader, mp.can_add_admin, mp.can_add_superadmin, mp.can_block_user,
|
mp.can_add_bd_leader, mp.can_add_admin, mp.can_add_superadmin, mp.can_block_user, mp.can_transfer_user_country,
|
||||||
COUNT(DISTINCT bl.user_id), COALESCE(MAX(bl.created_at_ms), 0),
|
COUNT(DISTINCT bl.user_id), COALESCE(MAX(bl.created_at_ms), 0),
|
||||||
mp.created_at_ms, mp.updated_at_ms
|
mp.created_at_ms, mp.updated_at_ms
|
||||||
%s
|
%s
|
||||||
GROUP BY mp.user_id, manager.current_display_user_id, manager.username, manager.avatar,
|
GROUP BY mp.user_id, manager.current_display_user_id, manager.username, manager.avatar,
|
||||||
manager_region.region_id, manager_region.name, mp.status, mp.contact_info,
|
manager_region.region_id, manager_region.name, mp.status, mp.contact_info,
|
||||||
mp.can_grant_avatar_frame, mp.can_grant_vehicle, mp.can_update_user_level,
|
mp.can_grant_avatar_frame, mp.can_grant_vehicle, mp.can_grant_badge, mp.can_update_user_level,
|
||||||
mp.can_add_bd_leader, mp.can_add_admin, mp.can_add_superadmin, mp.can_block_user,
|
mp.can_add_bd_leader, mp.can_add_admin, mp.can_add_superadmin, mp.can_block_user, mp.can_transfer_user_country,
|
||||||
mp.created_at_ms, mp.updated_at_ms
|
mp.created_at_ms, mp.updated_at_ms
|
||||||
ORDER BY mp.updated_at_ms DESC, mp.user_id DESC
|
ORDER BY mp.updated_at_ms DESC, mp.user_id DESC
|
||||||
LIMIT ? OFFSET ?
|
LIMIT ? OFFSET ?
|
||||||
@ -748,11 +752,13 @@ func (r *Reader) ListManagers(ctx context.Context, query listQuery) ([]*ManagerL
|
|||||||
&item.Contact,
|
&item.Contact,
|
||||||
&item.CanGrantAvatarFrame,
|
&item.CanGrantAvatarFrame,
|
||||||
&item.CanGrantVehicle,
|
&item.CanGrantVehicle,
|
||||||
|
&item.CanGrantBadge,
|
||||||
&item.CanUpdateUserLevel,
|
&item.CanUpdateUserLevel,
|
||||||
&item.CanAddBDLeader,
|
&item.CanAddBDLeader,
|
||||||
&item.CanAddAdmin,
|
&item.CanAddAdmin,
|
||||||
&item.CanAddSuperadmin,
|
&item.CanAddSuperadmin,
|
||||||
&item.CanBlockUser,
|
&item.CanBlockUser,
|
||||||
|
&item.CanTransferCountry,
|
||||||
&item.BDLeaderCount,
|
&item.BDLeaderCount,
|
||||||
&item.LastInvitedAtMs,
|
&item.LastInvitedAtMs,
|
||||||
&item.CreatedAtMs,
|
&item.CreatedAtMs,
|
||||||
|
|||||||
@ -65,54 +65,64 @@ type createManagerRequest struct {
|
|||||||
TargetUserID flexibleInt64 `json:"targetUserId" binding:"required"`
|
TargetUserID flexibleInt64 `json:"targetUserId" binding:"required"`
|
||||||
CanGrantAvatarFrame *bool `json:"canGrantAvatarFrame"`
|
CanGrantAvatarFrame *bool `json:"canGrantAvatarFrame"`
|
||||||
CanGrantVehicle *bool `json:"canGrantVehicle"`
|
CanGrantVehicle *bool `json:"canGrantVehicle"`
|
||||||
|
CanGrantBadge *bool `json:"canGrantBadge"`
|
||||||
CanUpdateUserLevel *bool `json:"canUpdateUserLevel"`
|
CanUpdateUserLevel *bool `json:"canUpdateUserLevel"`
|
||||||
CanAddBDLeader *bool `json:"canAddBdLeader"`
|
CanAddBDLeader *bool `json:"canAddBdLeader"`
|
||||||
CanAddAdmin *bool `json:"canAddAdmin"`
|
CanAddAdmin *bool `json:"canAddAdmin"`
|
||||||
CanAddSuperadmin *bool `json:"canAddSuperadmin"`
|
CanAddSuperadmin *bool `json:"canAddSuperadmin"`
|
||||||
CanBlockUser *bool `json:"canBlockUser"`
|
CanBlockUser *bool `json:"canBlockUser"`
|
||||||
|
CanTransferCountry *bool `json:"canTransferUserCountry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type updateManagerRequest struct {
|
type updateManagerRequest struct {
|
||||||
Contact *string `json:"contact"`
|
Contact *string `json:"contact"`
|
||||||
CanGrantAvatarFrame *bool `json:"canGrantAvatarFrame"`
|
CanGrantAvatarFrame *bool `json:"canGrantAvatarFrame"`
|
||||||
CanGrantVehicle *bool `json:"canGrantVehicle"`
|
CanGrantVehicle *bool `json:"canGrantVehicle"`
|
||||||
|
CanGrantBadge *bool `json:"canGrantBadge"`
|
||||||
CanUpdateUserLevel *bool `json:"canUpdateUserLevel"`
|
CanUpdateUserLevel *bool `json:"canUpdateUserLevel"`
|
||||||
CanAddBDLeader *bool `json:"canAddBdLeader"`
|
CanAddBDLeader *bool `json:"canAddBdLeader"`
|
||||||
CanAddAdmin *bool `json:"canAddAdmin"`
|
CanAddAdmin *bool `json:"canAddAdmin"`
|
||||||
CanAddSuperadmin *bool `json:"canAddSuperadmin"`
|
CanAddSuperadmin *bool `json:"canAddSuperadmin"`
|
||||||
CanBlockUser *bool `json:"canBlockUser"`
|
CanBlockUser *bool `json:"canBlockUser"`
|
||||||
|
CanTransferCountry *bool `json:"canTransferUserCountry"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type managerPermissions struct {
|
type managerPermissions struct {
|
||||||
CanGrantAvatarFrame bool
|
CanGrantAvatarFrame bool
|
||||||
CanGrantVehicle bool
|
CanGrantVehicle bool
|
||||||
|
CanGrantBadge bool
|
||||||
CanUpdateUserLevel bool
|
CanUpdateUserLevel bool
|
||||||
CanAddBDLeader bool
|
CanAddBDLeader bool
|
||||||
CanAddAdmin bool
|
CanAddAdmin bool
|
||||||
CanAddSuperadmin bool
|
CanAddSuperadmin bool
|
||||||
CanBlockUser bool
|
CanBlockUser bool
|
||||||
|
CanTransferCountry bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r createManagerRequest) permissionsWithDefault() managerPermissions {
|
func (r createManagerRequest) permissionsWithDefault() managerPermissions {
|
||||||
return managerPermissions{
|
return managerPermissions{
|
||||||
CanGrantAvatarFrame: boolDefaultTrue(r.CanGrantAvatarFrame),
|
CanGrantAvatarFrame: boolDefaultTrue(r.CanGrantAvatarFrame),
|
||||||
CanGrantVehicle: boolDefaultTrue(r.CanGrantVehicle),
|
CanGrantVehicle: boolDefaultTrue(r.CanGrantVehicle),
|
||||||
|
CanGrantBadge: boolDefaultTrue(r.CanGrantBadge),
|
||||||
CanUpdateUserLevel: boolDefaultTrue(r.CanUpdateUserLevel),
|
CanUpdateUserLevel: boolDefaultTrue(r.CanUpdateUserLevel),
|
||||||
CanAddBDLeader: boolDefaultTrue(r.CanAddBDLeader),
|
CanAddBDLeader: boolDefaultTrue(r.CanAddBDLeader),
|
||||||
CanAddAdmin: boolDefaultTrue(r.CanAddAdmin),
|
CanAddAdmin: boolDefaultTrue(r.CanAddAdmin),
|
||||||
CanAddSuperadmin: boolDefaultTrue(r.CanAddSuperadmin),
|
CanAddSuperadmin: boolDefaultTrue(r.CanAddSuperadmin),
|
||||||
CanBlockUser: boolDefaultTrue(r.CanBlockUser),
|
CanBlockUser: boolDefaultTrue(r.CanBlockUser),
|
||||||
|
CanTransferCountry: boolDefaultTrue(r.CanTransferCountry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r updateManagerRequest) appendPermissionAssignments(assignments *[]string, args *[]any) {
|
func (r updateManagerRequest) appendPermissionAssignments(assignments *[]string, args *[]any) {
|
||||||
appendBoolAssignment(assignments, args, "can_grant_avatar_frame", r.CanGrantAvatarFrame)
|
appendBoolAssignment(assignments, args, "can_grant_avatar_frame", r.CanGrantAvatarFrame)
|
||||||
appendBoolAssignment(assignments, args, "can_grant_vehicle", r.CanGrantVehicle)
|
appendBoolAssignment(assignments, args, "can_grant_vehicle", r.CanGrantVehicle)
|
||||||
|
appendBoolAssignment(assignments, args, "can_grant_badge", r.CanGrantBadge)
|
||||||
appendBoolAssignment(assignments, args, "can_update_user_level", r.CanUpdateUserLevel)
|
appendBoolAssignment(assignments, args, "can_update_user_level", r.CanUpdateUserLevel)
|
||||||
appendBoolAssignment(assignments, args, "can_add_bd_leader", r.CanAddBDLeader)
|
appendBoolAssignment(assignments, args, "can_add_bd_leader", r.CanAddBDLeader)
|
||||||
appendBoolAssignment(assignments, args, "can_add_admin", r.CanAddAdmin)
|
appendBoolAssignment(assignments, args, "can_add_admin", r.CanAddAdmin)
|
||||||
appendBoolAssignment(assignments, args, "can_add_superadmin", r.CanAddSuperadmin)
|
appendBoolAssignment(assignments, args, "can_add_superadmin", r.CanAddSuperadmin)
|
||||||
appendBoolAssignment(assignments, args, "can_block_user", r.CanBlockUser)
|
appendBoolAssignment(assignments, args, "can_block_user", r.CanBlockUser)
|
||||||
|
appendBoolAssignment(assignments, args, "can_transfer_user_country", r.CanTransferCountry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendBoolAssignment(assignments *[]string, args *[]any, column string, value *bool) {
|
func appendBoolAssignment(assignments *[]string, args *[]any, column string, value *bool) {
|
||||||
|
|||||||
@ -41,6 +41,7 @@ type UserProfileClient interface {
|
|||||||
UpdateUserProfile(ctx context.Context, req *userv1.UpdateUserProfileRequest) (*userv1.UpdateUserProfileResponse, error)
|
UpdateUserProfile(ctx context.Context, req *userv1.UpdateUserProfileRequest) (*userv1.UpdateUserProfileResponse, error)
|
||||||
UpdateUserProfileBackground(ctx context.Context, req *userv1.UpdateUserProfileBackgroundRequest) (*userv1.UpdateUserProfileBackgroundResponse, error)
|
UpdateUserProfileBackground(ctx context.Context, req *userv1.UpdateUserProfileBackgroundRequest) (*userv1.UpdateUserProfileBackgroundResponse, error)
|
||||||
ChangeUserCountry(ctx context.Context, req *userv1.ChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error)
|
ChangeUserCountry(ctx context.Context, req *userv1.ChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error)
|
||||||
|
AdminChangeUserCountry(ctx context.Context, req *userv1.AdminChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error)
|
||||||
CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error)
|
CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error)
|
||||||
ListManagerUserBlocks(ctx context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error)
|
ListManagerUserBlocks(ctx context.Context, req *userv1.ListManagerUserBlocksRequest) (*userv1.ListManagerUserBlocksResponse, error)
|
||||||
UnblockManagerUser(ctx context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error)
|
UnblockManagerUser(ctx context.Context, req *userv1.UnblockManagerUserRequest) (*userv1.UnblockManagerUserResponse, error)
|
||||||
@ -338,6 +339,10 @@ func (c *grpcUserProfileClient) ChangeUserCountry(ctx context.Context, req *user
|
|||||||
return c.client.ChangeUserCountry(ctx, req)
|
return c.client.ChangeUserCountry(ctx, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *grpcUserProfileClient) AdminChangeUserCountry(ctx context.Context, req *userv1.AdminChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error) {
|
||||||
|
return c.client.AdminChangeUserCountry(ctx, req)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *grpcUserProfileClient) CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
func (c *grpcUserProfileClient) CreateManagerUserBlock(ctx context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
||||||
return c.client.CreateManagerUserBlock(ctx, req)
|
return c.client.CreateManagerUserBlock(ctx, req)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -141,6 +141,10 @@ func (f *fakeInviteActivityUserProfileClient) ChangeUserCountry(context.Context,
|
|||||||
return &userv1.ChangeUserCountryResponse{}, nil
|
return &userv1.ChangeUserCountryResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeInviteActivityUserProfileClient) AdminChangeUserCountry(context.Context, *userv1.AdminChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error) {
|
||||||
|
return &userv1.ChangeUserCountryResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fakeInviteActivityUserProfileClient) CreateManagerUserBlock(context.Context, *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
func (f *fakeInviteActivityUserProfileClient) CreateManagerUserBlock(context.Context, *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
||||||
return &userv1.CreateManagerUserBlockResponse{}, nil
|
return &userv1.CreateManagerUserBlockResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -124,18 +124,19 @@ type UserHandlers struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ManagerHandlers struct {
|
type ManagerHandlers struct {
|
||||||
ListManagerGrantResources http.HandlerFunc
|
ListManagerGrantResources http.HandlerFunc
|
||||||
GrantManagerResource http.HandlerFunc
|
GrantManagerResource http.HandlerFunc
|
||||||
LookupBusinessUsers http.HandlerFunc
|
LookupBusinessUsers http.HandlerFunc
|
||||||
GetManagerOverview http.HandlerFunc
|
GetManagerOverview http.HandlerFunc
|
||||||
SearchManagerUsers http.HandlerFunc
|
SearchManagerUsers http.HandlerFunc
|
||||||
GrantManagerVIP http.HandlerFunc
|
GrantManagerVIP http.HandlerFunc
|
||||||
ManagerBlocks http.HandlerFunc
|
ManagerBlocks http.HandlerFunc
|
||||||
ListManagerBlocks http.HandlerFunc
|
ListManagerBlocks http.HandlerFunc
|
||||||
CreateManagerBlock http.HandlerFunc
|
CreateManagerBlock http.HandlerFunc
|
||||||
UnblockManagerBlock http.HandlerFunc
|
UnblockManagerBlock http.HandlerFunc
|
||||||
SetManagerUserLevel http.HandlerFunc
|
SetManagerUserLevel http.HandlerFunc
|
||||||
CreateManagerBDLeader http.HandlerFunc
|
CreateManagerBDLeader http.HandlerFunc
|
||||||
|
TransferManagerUserCountry http.HandlerFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type RoomHandlers struct {
|
type RoomHandlers struct {
|
||||||
@ -462,6 +463,7 @@ func (r routes) registerManagerRoutes() {
|
|||||||
r.profile("/manager-center/blocks/{block_id}/unblock", http.MethodPost, h.UnblockManagerBlock)
|
r.profile("/manager-center/blocks/{block_id}/unblock", http.MethodPost, h.UnblockManagerBlock)
|
||||||
r.profile("/manager-center/levels", http.MethodPost, h.SetManagerUserLevel)
|
r.profile("/manager-center/levels", http.MethodPost, h.SetManagerUserLevel)
|
||||||
r.profile("/manager-center/bd-leaders", http.MethodPost, h.CreateManagerBDLeader)
|
r.profile("/manager-center/bd-leaders", http.MethodPost, h.CreateManagerBDLeader)
|
||||||
|
r.profile("/manager-center/users/country", http.MethodPost, h.TransferManagerUserCountry)
|
||||||
r.profile("/business/users/lookup", http.MethodGet, h.LookupBusinessUsers)
|
r.profile("/business/users/lookup", http.MethodGet, h.LookupBusinessUsers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -68,7 +68,7 @@ func TestListManagerGrantResourcesChecksCapabilityAndFiltersWalletRequest(t *tes
|
|||||||
if recorder.Code != http.StatusOK {
|
if recorder.Code != http.StatusOK {
|
||||||
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||||
}
|
}
|
||||||
if hostClient.lastCapability == nil || hostClient.lastCapability.GetActorUserId() != 42 || hostClient.lastCapability.GetCapability() != "manager_resource_grant" {
|
if hostClient.lastCapability == nil || hostClient.lastCapability.GetActorUserId() != 42 || hostClient.lastCapability.GetCapability() != "manager_grant_badge" {
|
||||||
t.Fatalf("manager capability request mismatch: %+v", hostClient.lastCapability)
|
t.Fatalf("manager capability request mismatch: %+v", hostClient.lastCapability)
|
||||||
}
|
}
|
||||||
if walletClient.lastListResources == nil {
|
if walletClient.lastListResources == nil {
|
||||||
@ -160,6 +160,75 @@ func TestBusinessUserLookupForwardsSceneAndActor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSearchManagerUsersDefaultsSameCountryAndAllowsAllForCountryTransfer(t *testing.T) {
|
||||||
|
profileClient := &fakeUserProfileClient{
|
||||||
|
businessLookupResp: &userv1.BusinessUserLookupResponse{
|
||||||
|
Users: []*userv1.BusinessUserLookupItem{
|
||||||
|
{UserId: 900001, DisplayUserId: "163006", Status: userv1.UserStatus_USER_STATUS_ACTIVE},
|
||||||
|
{UserId: 900002, DisplayUserId: "163007", Status: userv1.UserStatus_USER_STATUS_ACTIVE},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
usersByID: map[int64]*userv1.User{
|
||||||
|
42: {UserId: 42, Status: userv1.UserStatus_USER_STATUS_ACTIVE, Country: "US"},
|
||||||
|
900001: {UserId: 900001, Status: userv1.UserStatus_USER_STATUS_ACTIVE, DisplayUserId: "163006", Country: "US"},
|
||||||
|
900002: {UserId: 900002, Status: userv1.UserStatus_USER_STATUS_ACTIVE, DisplayUserId: "163007", Country: "CA"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hostClient := &fakeUserHostClient{}
|
||||||
|
router := newManagerCenterTestRouter(&fakeWalletClient{}, hostClient, profileClient)
|
||||||
|
|
||||||
|
sameCountryReq := httptest.NewRequest(http.MethodGet, "/api/v1/manager-center/users/search?keyword=163&page_size=20", nil)
|
||||||
|
sameCountryReq.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||||
|
sameCountryReq.Header.Set("X-Request-ID", "req-manager-search-same")
|
||||||
|
sameCountryRecorder := httptest.NewRecorder()
|
||||||
|
router.ServeHTTP(sameCountryRecorder, sameCountryReq)
|
||||||
|
|
||||||
|
if sameCountryRecorder.Code != http.StatusOK {
|
||||||
|
t.Fatalf("same-country search status mismatch: got %d body=%s", sameCountryRecorder.Code, sameCountryRecorder.Body.String())
|
||||||
|
}
|
||||||
|
var sameEnvelope struct {
|
||||||
|
Data struct {
|
||||||
|
Items []struct {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
} `json:"items"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(sameCountryRecorder.Body).Decode(&sameEnvelope); err != nil {
|
||||||
|
t.Fatalf("decode same-country response failed: %v", err)
|
||||||
|
}
|
||||||
|
if sameEnvelope.Data.Total != 1 || sameEnvelope.Data.Items[0].UserID != "900001" {
|
||||||
|
t.Fatalf("same-country search must filter cross-country users: %+v", sameEnvelope.Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
allReq := httptest.NewRequest(http.MethodGet, "/api/v1/manager-center/users/search?keyword=163&page_size=20&scope=all", nil)
|
||||||
|
allReq.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||||
|
allReq.Header.Set("X-Request-ID", "req-manager-search-all")
|
||||||
|
allRecorder := httptest.NewRecorder()
|
||||||
|
router.ServeHTTP(allRecorder, allReq)
|
||||||
|
|
||||||
|
if allRecorder.Code != http.StatusOK {
|
||||||
|
t.Fatalf("all-country search status mismatch: got %d body=%s", allRecorder.Code, allRecorder.Body.String())
|
||||||
|
}
|
||||||
|
var allEnvelope struct {
|
||||||
|
Data struct {
|
||||||
|
Items []struct {
|
||||||
|
UserID string `json:"user_id"`
|
||||||
|
} `json:"items"`
|
||||||
|
Total int `json:"total"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
if err := json.NewDecoder(allRecorder.Body).Decode(&allEnvelope); err != nil {
|
||||||
|
t.Fatalf("decode all-country response failed: %v", err)
|
||||||
|
}
|
||||||
|
if allEnvelope.Data.Total != 2 || allEnvelope.Data.Items[1].UserID != "900002" {
|
||||||
|
t.Fatalf("country transfer search must include cross-country users: %+v", allEnvelope.Data)
|
||||||
|
}
|
||||||
|
if !hostClient.sawCapability("manager_transfer_user_country") {
|
||||||
|
t.Fatalf("scope=all search must check transfer-country capability, saw %#v", hostClient.capabilities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestGrantManagerResourceUsesServerControlledGrantShape(t *testing.T) {
|
func TestGrantManagerResourceUsesServerControlledGrantShape(t *testing.T) {
|
||||||
walletClient := &fakeWalletClient{}
|
walletClient := &fakeWalletClient{}
|
||||||
hostClient := &fakeUserHostClient{}
|
hostClient := &fakeUserHostClient{}
|
||||||
@ -441,6 +510,32 @@ func TestCreateManagerBDLeaderRejectsCrossCountryTarget(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTransferManagerUserCountryUsesAdminCountryChangeWithoutSameCountryFilter(t *testing.T) {
|
||||||
|
profileClient := &fakeUserProfileClient{usersByID: map[int64]*userv1.User{
|
||||||
|
42: {UserId: 42, Status: userv1.UserStatus_USER_STATUS_ACTIVE, Country: "US", RegionId: 1001},
|
||||||
|
}}
|
||||||
|
hostClient := &fakeUserHostClient{}
|
||||||
|
router := newManagerCenterTestRouter(&fakeWalletClient{}, hostClient, profileClient)
|
||||||
|
body := []byte(`{"command_id":"mgr-country-1","target_user_id":"900001","country":"CA"}`)
|
||||||
|
request := httptest.NewRequest(http.MethodPost, "/api/v1/manager-center/users/country", bytes.NewReader(body))
|
||||||
|
request.Header.Set("Authorization", "Bearer "+signGatewayToken(t, "secret", 42))
|
||||||
|
request.Header.Set("X-Request-ID", "req-manager-country")
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
|
router.ServeHTTP(recorder, request)
|
||||||
|
|
||||||
|
if recorder.Code != http.StatusOK {
|
||||||
|
t.Fatalf("status mismatch: got %d body=%s", recorder.Code, recorder.Body.String())
|
||||||
|
}
|
||||||
|
generatedRequestID := assertGeneratedRequestID(t, recorder, "req-manager-country")
|
||||||
|
if req := profileClient.lastAdminCountry; req == nil || req.GetMeta().GetRequestId() != generatedRequestID || req.GetAdminUserId() != 42 || req.GetTargetUserId() != 900001 || req.GetCountry() != "CA" || req.GetReason() != "manager_center_country_transfer" {
|
||||||
|
t.Fatalf("AdminChangeUserCountry request mismatch: %+v", req)
|
||||||
|
}
|
||||||
|
if !hostClient.sawCapability("manager_transfer_user_country") {
|
||||||
|
t.Fatalf("country transfer must check manager_transfer_user_country, saw %#v", hostClient.capabilities)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type fakeGrowthLevelClient struct {
|
type fakeGrowthLevelClient struct {
|
||||||
lastSet *activityv1.SetUserLevelRequest
|
lastSet *activityv1.SetUserLevelRequest
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,17 +35,18 @@ func New(config Config) *Handler {
|
|||||||
|
|
||||||
func (h *Handler) ManagerHandlers() httproutes.ManagerHandlers {
|
func (h *Handler) ManagerHandlers() httproutes.ManagerHandlers {
|
||||||
return httproutes.ManagerHandlers{
|
return httproutes.ManagerHandlers{
|
||||||
ListManagerGrantResources: h.listManagerGrantResources,
|
ListManagerGrantResources: h.listManagerGrantResources,
|
||||||
GrantManagerResource: h.grantManagerResource,
|
GrantManagerResource: h.grantManagerResource,
|
||||||
LookupBusinessUsers: h.lookupBusinessUsers,
|
LookupBusinessUsers: h.lookupBusinessUsers,
|
||||||
GetManagerOverview: h.getManagerOverview,
|
GetManagerOverview: h.getManagerOverview,
|
||||||
SearchManagerUsers: h.searchManagerUsers,
|
SearchManagerUsers: h.searchManagerUsers,
|
||||||
GrantManagerVIP: h.grantManagerVIP,
|
GrantManagerVIP: h.grantManagerVIP,
|
||||||
ManagerBlocks: h.managerBlocks,
|
ManagerBlocks: h.managerBlocks,
|
||||||
ListManagerBlocks: h.listManagerBlocks,
|
ListManagerBlocks: h.listManagerBlocks,
|
||||||
CreateManagerBlock: h.createManagerBlock,
|
CreateManagerBlock: h.createManagerBlock,
|
||||||
UnblockManagerBlock: h.unblockManagerBlock,
|
UnblockManagerBlock: h.unblockManagerBlock,
|
||||||
SetManagerUserLevel: h.setManagerUserLevel,
|
SetManagerUserLevel: h.setManagerUserLevel,
|
||||||
CreateManagerBDLeader: h.createManagerBDLeader,
|
CreateManagerBDLeader: h.createManagerBDLeader,
|
||||||
|
TransferManagerUserCountry: h.transferManagerUserCountry,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,11 +20,13 @@ const managerCenterCapability = "manager_center"
|
|||||||
const managerResourceGrantCapability = "manager_resource_grant"
|
const managerResourceGrantCapability = "manager_resource_grant"
|
||||||
const managerGrantAvatarFrameCapability = "manager_grant_avatar_frame"
|
const managerGrantAvatarFrameCapability = "manager_grant_avatar_frame"
|
||||||
const managerGrantVehicleCapability = "manager_grant_vehicle"
|
const managerGrantVehicleCapability = "manager_grant_vehicle"
|
||||||
|
const managerGrantBadgeCapability = "manager_grant_badge"
|
||||||
const managerUpdateUserLevelCapability = "manager_update_user_level"
|
const managerUpdateUserLevelCapability = "manager_update_user_level"
|
||||||
const managerAddBDLeaderCapability = "manager_add_bd_leader"
|
const managerAddBDLeaderCapability = "manager_add_bd_leader"
|
||||||
const managerAddAdminCapability = "manager_add_admin"
|
const managerAddAdminCapability = "manager_add_admin"
|
||||||
const managerAddSuperadminCapability = "manager_add_superadmin"
|
const managerAddSuperadminCapability = "manager_add_superadmin"
|
||||||
const managerBlockUserCapability = "manager_block_user"
|
const managerBlockUserCapability = "manager_block_user"
|
||||||
|
const managerTransferUserCountryCapability = "manager_transfer_user_country"
|
||||||
const managerGrantDayMS int64 = 24 * 60 * 60 * 1000
|
const managerGrantDayMS int64 = 24 * 60 * 60 * 1000
|
||||||
const managerGrantDefaultDurationMS int64 = 7 * managerGrantDayMS
|
const managerGrantDefaultDurationMS int64 = 7 * managerGrantDayMS
|
||||||
|
|
||||||
@ -146,10 +148,16 @@ func (h *Handler) searchManagerUsers(writer http.ResponseWriter, request *http.R
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// 搜索先走已有 business lookup 保持短号/昵称解析一致,再用 BatchGetUsers 拿国家和等级投影做最终过滤。
|
// 搜索先走已有 business lookup 保持短号/昵称解析一致,再用 BatchGetUsers 拿国家和等级投影做最终过滤。
|
||||||
|
// 只有国家转移入口显式带 scope=all 且经理拥有对应开关时,才允许跨国家搜索;其他动作继续默认同国家。
|
||||||
actor, ok := h.requireManagerCenterActor(writer, request)
|
actor, ok := h.requireManagerCenterActor(writer, request)
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
scope := strings.ToLower(strings.TrimSpace(request.URL.Query().Get("scope")))
|
||||||
|
allCountries := scope == "all"
|
||||||
|
if allCountries && !h.requireManagerCapability(writer, request, managerTransferUserCountryCapability) {
|
||||||
|
return
|
||||||
|
}
|
||||||
pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 20)
|
pageSize, ok := httpkit.PositiveInt32Query(request, "page_size", 20)
|
||||||
if !ok {
|
if !ok {
|
||||||
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
||||||
@ -193,8 +201,8 @@ func (h *Handler) searchManagerUsers(writer http.ResponseWriter, request *http.R
|
|||||||
items := make([]managerUserData, 0, len(userIDs))
|
items := make([]managerUserData, 0, len(userIDs))
|
||||||
for _, userID := range userIDs {
|
for _, userID := range userIDs {
|
||||||
user := usersResp.GetUsers()[userID]
|
user := usersResp.GetUsers()[userID]
|
||||||
// 同国家限制必须放在 gateway 写入口和读入口都做,避免 H5 通过短号枚举跨国家用户。
|
// 默认搜索必须和写入口一样收敛到同国家;国家转移已单独校验权限,允许在这里跨国家挑选目标。
|
||||||
if user == nil || user.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE || !sameCountry(actor.GetCountry(), user.GetCountry()) {
|
if user == nil || user.GetStatus() != userv1.UserStatus_USER_STATUS_ACTIVE || (!allCountries && !sameCountry(actor.GetCountry(), user.GetCountry())) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
items = append(items, managerUserFromProto(user, levels[userID]))
|
items = append(items, managerUserFromProto(user, levels[userID]))
|
||||||
@ -671,6 +679,65 @@ func (h *Handler) createManagerBDLeader(writer http.ResponseWriter, request *htt
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (h *Handler) transferManagerUserCountry(writer http.ResponseWriter, request *http.Request) {
|
||||||
|
if h.userProfileClient == nil || h.userHostClient == nil {
|
||||||
|
httpkit.WriteError(writer, request, http.StatusBadGateway, httpkit.CodeUpstreamError, "upstream service error")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var body struct {
|
||||||
|
TargetUserID json.RawMessage `json:"target_user_id"`
|
||||||
|
TargetUserIDAlt json.RawMessage `json:"targetUserId"`
|
||||||
|
Country string `json:"country"`
|
||||||
|
CountryCode string `json:"country_code"`
|
||||||
|
CountryCodeAlt string `json:"countryCode"`
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
}
|
||||||
|
if !httpkit.Decode(writer, request, &body) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
actor, ok := h.requireManagerCenterActor(writer, request)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !h.requireManagerCapability(writer, request, managerTransferUserCountryCapability) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
targetUserID, targetOK := rawInt64(body.TargetUserID, body.TargetUserIDAlt)
|
||||||
|
country := strings.ToUpper(strings.TrimSpace(body.Country))
|
||||||
|
if country == "" {
|
||||||
|
country = strings.ToUpper(strings.TrimSpace(body.CountryCode))
|
||||||
|
}
|
||||||
|
if country == "" {
|
||||||
|
country = strings.ToUpper(strings.TrimSpace(body.CountryCodeAlt))
|
||||||
|
}
|
||||||
|
if !targetOK || targetUserID <= 0 || country == "" {
|
||||||
|
httpkit.WriteError(writer, request, http.StatusBadRequest, httpkit.CodeInvalidArgument, "invalid argument")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
reason := strings.TrimSpace(body.Reason)
|
||||||
|
if reason == "" {
|
||||||
|
reason = "manager_center_country_transfer"
|
||||||
|
}
|
||||||
|
// 国家转移是治理动作,不复用同国家目标校验;权限由 manager_profiles 独立开关控制,实际国家/区域事务由 user-service 统一落库和发 outbox。
|
||||||
|
resp, err := h.userProfileClient.AdminChangeUserCountry(request.Context(), &userv1.AdminChangeUserCountryRequest{
|
||||||
|
Meta: httpkit.UserMeta(request, ""),
|
||||||
|
TargetUserId: targetUserID,
|
||||||
|
Country: country,
|
||||||
|
AdminUserId: actor.GetUserId(),
|
||||||
|
Reason: reason,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
httpkit.WriteRPCError(writer, request, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpkit.WriteOK(writer, request, map[string]any{
|
||||||
|
"user": managerUserFromProto(resp.GetUser(), nil),
|
||||||
|
"old_region_id": resp.GetOldRegionId(),
|
||||||
|
"new_region_id": resp.GetNewRegionId(),
|
||||||
|
"region_changed": resp.GetRegionChanged(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (h *Handler) requireManagerResourceGrantCapability(writer http.ResponseWriter, request *http.Request) bool {
|
func (h *Handler) requireManagerResourceGrantCapability(writer http.ResponseWriter, request *http.Request) bool {
|
||||||
return h.requireManagerCapability(writer, request, managerResourceGrantCapability)
|
return h.requireManagerCapability(writer, request, managerResourceGrantCapability)
|
||||||
}
|
}
|
||||||
@ -743,22 +810,26 @@ func (h *Handler) requireSameCountryTargetWithActor(writer http.ResponseWriter,
|
|||||||
|
|
||||||
func (h *Handler) managerPermissionOverview(request *http.Request) map[string]bool {
|
func (h *Handler) managerPermissionOverview(request *http.Request) map[string]bool {
|
||||||
permissions := map[string]bool{
|
permissions := map[string]bool{
|
||||||
"can_grant_avatar_frame": false,
|
"can_grant_avatar_frame": false,
|
||||||
"can_grant_vehicle": false,
|
"can_grant_vehicle": false,
|
||||||
"can_update_user_level": false,
|
"can_grant_badge": false,
|
||||||
"can_add_bd_leader": false,
|
"can_update_user_level": false,
|
||||||
"can_add_admin": false,
|
"can_add_bd_leader": false,
|
||||||
"can_add_superadmin": false,
|
"can_add_admin": false,
|
||||||
"can_block_user": false,
|
"can_add_superadmin": false,
|
||||||
|
"can_block_user": false,
|
||||||
|
"can_transfer_user_country": false,
|
||||||
}
|
}
|
||||||
for key, capability := range map[string]string{
|
for key, capability := range map[string]string{
|
||||||
"can_grant_avatar_frame": managerGrantAvatarFrameCapability,
|
"can_grant_avatar_frame": managerGrantAvatarFrameCapability,
|
||||||
"can_grant_vehicle": managerGrantVehicleCapability,
|
"can_grant_vehicle": managerGrantVehicleCapability,
|
||||||
"can_update_user_level": managerUpdateUserLevelCapability,
|
"can_grant_badge": managerGrantBadgeCapability,
|
||||||
"can_add_bd_leader": managerAddBDLeaderCapability,
|
"can_update_user_level": managerUpdateUserLevelCapability,
|
||||||
"can_add_admin": managerAddAdminCapability,
|
"can_add_bd_leader": managerAddBDLeaderCapability,
|
||||||
"can_add_superadmin": managerAddSuperadminCapability,
|
"can_add_admin": managerAddAdminCapability,
|
||||||
"can_block_user": managerBlockUserCapability,
|
"can_add_superadmin": managerAddSuperadminCapability,
|
||||||
|
"can_block_user": managerBlockUserCapability,
|
||||||
|
"can_transfer_user_country": managerTransferUserCountryCapability,
|
||||||
} {
|
} {
|
||||||
resp, err := h.userHostClient.CheckBusinessCapability(request.Context(), &userv1.CheckBusinessCapabilityRequest{
|
resp, err := h.userHostClient.CheckBusinessCapability(request.Context(), &userv1.CheckBusinessCapabilityRequest{
|
||||||
Meta: httpkit.UserMeta(request, ""),
|
Meta: httpkit.UserMeta(request, ""),
|
||||||
@ -781,6 +852,8 @@ func managerCapabilityForResourceType(resourceType string) string {
|
|||||||
return managerGrantAvatarFrameCapability
|
return managerGrantAvatarFrameCapability
|
||||||
case "vehicle":
|
case "vehicle":
|
||||||
return managerGrantVehicleCapability
|
return managerGrantVehicleCapability
|
||||||
|
case "badge":
|
||||||
|
return managerGrantBadgeCapability
|
||||||
default:
|
default:
|
||||||
return managerResourceGrantCapability
|
return managerResourceGrantCapability
|
||||||
}
|
}
|
||||||
|
|||||||
@ -314,6 +314,7 @@ type fakeUserProfileClient struct {
|
|||||||
lastUpdate *userv1.UpdateUserProfileRequest
|
lastUpdate *userv1.UpdateUserProfileRequest
|
||||||
lastBackground *userv1.UpdateUserProfileBackgroundRequest
|
lastBackground *userv1.UpdateUserProfileBackgroundRequest
|
||||||
lastCountry *userv1.ChangeUserCountryRequest
|
lastCountry *userv1.ChangeUserCountryRequest
|
||||||
|
lastAdminCountry *userv1.AdminChangeUserCountryRequest
|
||||||
lastCreateBlock *userv1.CreateManagerUserBlockRequest
|
lastCreateBlock *userv1.CreateManagerUserBlockRequest
|
||||||
lastListBlocks *userv1.ListManagerUserBlocksRequest
|
lastListBlocks *userv1.ListManagerUserBlocksRequest
|
||||||
lastUnblock *userv1.UnblockManagerUserRequest
|
lastUnblock *userv1.UnblockManagerUserRequest
|
||||||
@ -865,6 +866,21 @@ func (f *fakeUserProfileClient) ChangeUserCountry(_ context.Context, req *userv1
|
|||||||
}, NextChangeAllowedAtMs: 3600000, OldRegionId: 1001, NewRegionId: 2002, RegionChanged: true}, nil
|
}, NextChangeAllowedAtMs: 3600000, OldRegionId: 1001, NewRegionId: 2002, RegionChanged: true}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *fakeUserProfileClient) AdminChangeUserCountry(_ context.Context, req *userv1.AdminChangeUserCountryRequest) (*userv1.ChangeUserCountryResponse, error) {
|
||||||
|
f.lastAdminCountry = req
|
||||||
|
if f.countryErr != nil {
|
||||||
|
return nil, f.countryErr
|
||||||
|
}
|
||||||
|
return &userv1.ChangeUserCountryResponse{User: &userv1.User{
|
||||||
|
UserId: req.GetTargetUserId(),
|
||||||
|
DisplayUserId: "100001",
|
||||||
|
Country: req.GetCountry(),
|
||||||
|
RegionId: 2002,
|
||||||
|
Status: userv1.UserStatus_USER_STATUS_ACTIVE,
|
||||||
|
UpdatedAtMs: 3000,
|
||||||
|
}, OldRegionId: 1001, NewRegionId: 2002, RegionChanged: true}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (f *fakeUserProfileClient) CreateManagerUserBlock(_ context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
func (f *fakeUserProfileClient) CreateManagerUserBlock(_ context.Context, req *userv1.CreateManagerUserBlockRequest) (*userv1.CreateManagerUserBlockResponse, error) {
|
||||||
f.lastCreateBlock = req
|
f.lastCreateBlock = req
|
||||||
if f.blockErr != nil {
|
if f.blockErr != nil {
|
||||||
|
|||||||
@ -772,11 +772,13 @@ CREATE TABLE IF NOT EXISTS manager_profiles (
|
|||||||
contact_info VARCHAR(128) NOT NULL DEFAULT '' COMMENT '联系信息',
|
contact_info VARCHAR(128) NOT NULL DEFAULT '' COMMENT '联系信息',
|
||||||
can_grant_avatar_frame TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送头像框',
|
can_grant_avatar_frame TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送头像框',
|
||||||
can_grant_vehicle TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送座驾',
|
can_grant_vehicle TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送座驾',
|
||||||
|
can_grant_badge TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心赠送徽章',
|
||||||
can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心升级用户等级',
|
can_update_user_level TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心升级用户等级',
|
||||||
can_add_bd_leader TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 BD Leader',
|
can_add_bd_leader TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 BD Leader',
|
||||||
can_add_admin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Admin',
|
can_add_admin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Admin',
|
||||||
can_add_superadmin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Superadmin',
|
can_add_superadmin TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心添加 Superadmin',
|
||||||
can_block_user TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心封禁用户',
|
can_block_user TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心封禁用户',
|
||||||
|
can_transfer_user_country TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否允许在经理中心转移用户国家',
|
||||||
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台管理员 ID',
|
created_by_admin_id BIGINT NOT NULL DEFAULT 0 COMMENT '创建后台管理员 ID',
|
||||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||||
|
|||||||
@ -195,11 +195,13 @@ type ManagerProfile struct {
|
|||||||
Contact string
|
Contact string
|
||||||
CanGrantAvatarFrame bool
|
CanGrantAvatarFrame bool
|
||||||
CanGrantVehicle bool
|
CanGrantVehicle bool
|
||||||
|
CanGrantBadge bool
|
||||||
CanUpdateUserLevel bool
|
CanUpdateUserLevel bool
|
||||||
CanAddBDLeader bool
|
CanAddBDLeader bool
|
||||||
CanAddAdmin bool
|
CanAddAdmin bool
|
||||||
CanAddSuperadmin bool
|
CanAddSuperadmin bool
|
||||||
CanBlockUser bool
|
CanBlockUser bool
|
||||||
|
CanTransferCountry bool
|
||||||
CreatedByAdminID int64
|
CreatedByAdminID int64
|
||||||
CreatedAtMs int64
|
CreatedAtMs int64
|
||||||
UpdatedAtMs int64
|
UpdatedAtMs int64
|
||||||
|
|||||||
@ -12,11 +12,13 @@ const (
|
|||||||
CapabilityManagerResourceGrant = "manager_resource_grant"
|
CapabilityManagerResourceGrant = "manager_resource_grant"
|
||||||
CapabilityManagerGrantAvatarFrame = "manager_grant_avatar_frame"
|
CapabilityManagerGrantAvatarFrame = "manager_grant_avatar_frame"
|
||||||
CapabilityManagerGrantVehicle = "manager_grant_vehicle"
|
CapabilityManagerGrantVehicle = "manager_grant_vehicle"
|
||||||
|
CapabilityManagerGrantBadge = "manager_grant_badge"
|
||||||
CapabilityManagerUpdateUserLevel = "manager_update_user_level"
|
CapabilityManagerUpdateUserLevel = "manager_update_user_level"
|
||||||
CapabilityManagerAddBDLeader = "manager_add_bd_leader"
|
CapabilityManagerAddBDLeader = "manager_add_bd_leader"
|
||||||
CapabilityManagerAddAdmin = "manager_add_admin"
|
CapabilityManagerAddAdmin = "manager_add_admin"
|
||||||
CapabilityManagerAddSuperadmin = "manager_add_superadmin"
|
CapabilityManagerAddSuperadmin = "manager_add_superadmin"
|
||||||
CapabilityManagerBlockUser = "manager_block_user"
|
CapabilityManagerBlockUser = "manager_block_user"
|
||||||
|
CapabilityManagerTransferCountry = "manager_transfer_user_country"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CheckBusinessCapability 是 App/H5 业务入口的服务端身份边界。
|
// CheckBusinessCapability 是 App/H5 业务入口的服务端身份边界。
|
||||||
@ -42,11 +44,13 @@ func (s *Service) CheckBusinessCapability(ctx context.Context, actorUserID int64
|
|||||||
case CapabilityManagerResourceGrant,
|
case CapabilityManagerResourceGrant,
|
||||||
CapabilityManagerGrantAvatarFrame,
|
CapabilityManagerGrantAvatarFrame,
|
||||||
CapabilityManagerGrantVehicle,
|
CapabilityManagerGrantVehicle,
|
||||||
|
CapabilityManagerGrantBadge,
|
||||||
CapabilityManagerUpdateUserLevel,
|
CapabilityManagerUpdateUserLevel,
|
||||||
CapabilityManagerAddBDLeader,
|
CapabilityManagerAddBDLeader,
|
||||||
CapabilityManagerAddAdmin,
|
CapabilityManagerAddAdmin,
|
||||||
CapabilityManagerAddSuperadmin,
|
CapabilityManagerAddSuperadmin,
|
||||||
CapabilityManagerBlockUser:
|
CapabilityManagerBlockUser,
|
||||||
|
CapabilityManagerTransferCountry:
|
||||||
// 细分能力必须同时满足 active 经理身份和对应权限开关;这样 H5 被篡改后直调写接口也会被 user-service 拒绝。
|
// 细分能力必须同时满足 active 经理身份和对应权限开关;这样 H5 被篡改后直调写接口也会被 user-service 拒绝。
|
||||||
ok, err := s.repository.HasActiveManagerCapability(ctx, actorUserID, capability)
|
ok, err := s.repository.HasActiveManagerCapability(ctx, actorUserID, capability)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -317,16 +317,18 @@ func (r *Repository) HasActiveManagerProfile(ctx context.Context, userID int64)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasActiveManagerCapability 精确校验经理中心单项能力。
|
// HasActiveManagerCapability 精确校验经理中心单项能力。
|
||||||
// 这里把 legacy 的 manager_resource_grant 收敛成“头像框或座驾任一资源赠送权限”,写入口仍会按真实资源类型再校验一次。
|
// 这里把 legacy 的 manager_resource_grant 收敛成任一资源赠送权限,写入口仍会按真实资源类型再校验一次。
|
||||||
func (r *Repository) HasActiveManagerCapability(ctx context.Context, userID int64, capability string) (bool, error) {
|
func (r *Repository) HasActiveManagerCapability(ctx context.Context, userID int64, capability string) (bool, error) {
|
||||||
columnSQL := ""
|
columnSQL := ""
|
||||||
switch capability {
|
switch capability {
|
||||||
case hostservice.CapabilityManagerResourceGrant:
|
case hostservice.CapabilityManagerResourceGrant:
|
||||||
columnSQL = "(can_grant_avatar_frame = 1 OR can_grant_vehicle = 1)"
|
columnSQL = "(can_grant_avatar_frame = 1 OR can_grant_vehicle = 1 OR can_grant_badge = 1)"
|
||||||
case hostservice.CapabilityManagerGrantAvatarFrame:
|
case hostservice.CapabilityManagerGrantAvatarFrame:
|
||||||
columnSQL = "can_grant_avatar_frame = 1"
|
columnSQL = "can_grant_avatar_frame = 1"
|
||||||
case hostservice.CapabilityManagerGrantVehicle:
|
case hostservice.CapabilityManagerGrantVehicle:
|
||||||
columnSQL = "can_grant_vehicle = 1"
|
columnSQL = "can_grant_vehicle = 1"
|
||||||
|
case hostservice.CapabilityManagerGrantBadge:
|
||||||
|
columnSQL = "can_grant_badge = 1"
|
||||||
case hostservice.CapabilityManagerUpdateUserLevel:
|
case hostservice.CapabilityManagerUpdateUserLevel:
|
||||||
columnSQL = "can_update_user_level = 1"
|
columnSQL = "can_update_user_level = 1"
|
||||||
case hostservice.CapabilityManagerAddBDLeader:
|
case hostservice.CapabilityManagerAddBDLeader:
|
||||||
@ -337,6 +339,8 @@ func (r *Repository) HasActiveManagerCapability(ctx context.Context, userID int6
|
|||||||
columnSQL = "can_add_superadmin = 1"
|
columnSQL = "can_add_superadmin = 1"
|
||||||
case hostservice.CapabilityManagerBlockUser:
|
case hostservice.CapabilityManagerBlockUser:
|
||||||
columnSQL = "can_block_user = 1"
|
columnSQL = "can_block_user = 1"
|
||||||
|
case hostservice.CapabilityManagerTransferCountry:
|
||||||
|
columnSQL = "can_transfer_user_country = 1"
|
||||||
default:
|
default:
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -766,25 +766,27 @@ func (r *Repository) PutManagerProfile(profile hostdomain.ManagerProfile) {
|
|||||||
|
|
||||||
_, err := r.schema.DB.ExecContext(context.Background(), `
|
_, err := r.schema.DB.ExecContext(context.Background(), `
|
||||||
INSERT INTO manager_profiles (
|
INSERT INTO manager_profiles (
|
||||||
user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle,
|
user_id, status, contact_info, can_grant_avatar_frame, can_grant_vehicle, can_grant_badge,
|
||||||
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user,
|
can_update_user_level, can_add_bd_leader, can_add_admin, can_add_superadmin, can_block_user, can_transfer_user_country,
|
||||||
created_by_admin_id, created_at_ms, updated_at_ms
|
created_by_admin_id, created_at_ms, updated_at_ms
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
status = VALUES(status),
|
status = VALUES(status),
|
||||||
contact_info = VALUES(contact_info),
|
contact_info = VALUES(contact_info),
|
||||||
can_grant_avatar_frame = VALUES(can_grant_avatar_frame),
|
can_grant_avatar_frame = VALUES(can_grant_avatar_frame),
|
||||||
can_grant_vehicle = VALUES(can_grant_vehicle),
|
can_grant_vehicle = VALUES(can_grant_vehicle),
|
||||||
|
can_grant_badge = VALUES(can_grant_badge),
|
||||||
can_update_user_level = VALUES(can_update_user_level),
|
can_update_user_level = VALUES(can_update_user_level),
|
||||||
can_add_bd_leader = VALUES(can_add_bd_leader),
|
can_add_bd_leader = VALUES(can_add_bd_leader),
|
||||||
can_add_admin = VALUES(can_add_admin),
|
can_add_admin = VALUES(can_add_admin),
|
||||||
can_add_superadmin = VALUES(can_add_superadmin),
|
can_add_superadmin = VALUES(can_add_superadmin),
|
||||||
can_block_user = VALUES(can_block_user),
|
can_block_user = VALUES(can_block_user),
|
||||||
|
can_transfer_user_country = VALUES(can_transfer_user_country),
|
||||||
updated_at_ms = VALUES(updated_at_ms)
|
updated_at_ms = VALUES(updated_at_ms)
|
||||||
`, profile.UserID, profile.Status, profile.Contact, defaultTrue(profile.CanGrantAvatarFrame),
|
`, profile.UserID, profile.Status, profile.Contact, defaultTrue(profile.CanGrantAvatarFrame),
|
||||||
defaultTrue(profile.CanGrantVehicle), defaultTrue(profile.CanUpdateUserLevel),
|
defaultTrue(profile.CanGrantVehicle), defaultTrue(profile.CanGrantBadge), defaultTrue(profile.CanUpdateUserLevel),
|
||||||
defaultTrue(profile.CanAddBDLeader), defaultTrue(profile.CanAddAdmin),
|
defaultTrue(profile.CanAddBDLeader), defaultTrue(profile.CanAddAdmin),
|
||||||
defaultTrue(profile.CanAddSuperadmin), defaultTrue(profile.CanBlockUser),
|
defaultTrue(profile.CanAddSuperadmin), defaultTrue(profile.CanBlockUser), defaultTrue(profile.CanTransferCountry),
|
||||||
profile.CreatedByAdminID, profile.CreatedAtMs, profile.UpdatedAtMs)
|
profile.CreatedByAdminID, profile.CreatedAtMs, profile.UpdatedAtMs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.t.Fatalf("seed manager profile %d failed: %v", profile.UserID, err)
|
r.t.Fatalf("seed manager profile %d failed: %v", profile.UserID, err)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user