91 lines
3.5 KiB
Go
91 lines
3.5 KiB
Go
package user
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
invitedomain "hyapp/services/user-service/internal/domain/invite"
|
||
userdomain "hyapp/services/user-service/internal/domain/user"
|
||
)
|
||
|
||
// SearchInviteReferrer 通过邀请码搜索可绑定邀请人。
|
||
// 搜索结果只允许同 App、同国家、已完成资料且 active 的普通用户,避免 H5 枚举跨国家用户资料。
|
||
func (s *Service) SearchInviteReferrer(ctx context.Context, userID int64, inviteCode string) (userdomain.User, error) {
|
||
inviteCode = invitedomain.NormalizeCode(inviteCode)
|
||
if userID <= 0 {
|
||
return userdomain.User{}, xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
if !invitedomain.ValidCode(inviteCode) {
|
||
// 对外搜索入口只接受合法邀请码;非法输入直接拒绝,不落入 NotFound 分支。
|
||
return userdomain.User{}, xerr.New(xerr.InvalidInviteCode, "invalid invite code")
|
||
}
|
||
if s.userRepository == nil {
|
||
return userdomain.User{}, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
current, err := s.userRepository.GetUser(ctx, userID)
|
||
if err != nil {
|
||
return userdomain.User{}, err
|
||
}
|
||
if !current.ProfileCompleted {
|
||
// 当前账号未完成资料时没有稳定国家边界,不能用这个入口搜索邀请人。
|
||
return userdomain.User{}, xerr.New(xerr.ProfileRequired, "profile required")
|
||
}
|
||
if current.Country == "" {
|
||
return userdomain.User{}, xerr.New(xerr.InvalidArgument, "current user country is required")
|
||
}
|
||
|
||
inviter, err := s.userRepository.FindInviteReferrerByCode(ctx, appcode.FromContext(ctx), inviteCode)
|
||
if err != nil {
|
||
return userdomain.User{}, err
|
||
}
|
||
if err := validateInviteReferrerPair(current, inviter); err != nil {
|
||
return userdomain.User{}, err
|
||
}
|
||
|
||
return s.refreshExpiredUser(ctx, inviter, "")
|
||
}
|
||
|
||
// BindInviteReferrer 原子绑定当前用户的邀请人。
|
||
// H5 只能传用户输入的邀请码;归属用户由 repository 在事务内重新解析并复核。
|
||
func (s *Service) BindInviteReferrer(ctx context.Context, userID int64, inviteCode string, requestID string) (userdomain.User, error) {
|
||
inviteCode = invitedomain.NormalizeCode(inviteCode)
|
||
if userID <= 0 {
|
||
return userdomain.User{}, xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
if !invitedomain.ValidCode(inviteCode) {
|
||
return userdomain.User{}, xerr.New(xerr.InvalidInviteCode, "invalid invite code")
|
||
}
|
||
if s.userRepository == nil {
|
||
return userdomain.User{}, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
return s.userRepository.BindInviteReferrer(ctx, userdomain.BindInviteReferrerCommand{
|
||
AppCode: appcode.FromContext(ctx),
|
||
InvitedUserID: userID,
|
||
InviteCode: inviteCode,
|
||
RequestID: strings.TrimSpace(requestID),
|
||
BoundAtMs: s.now().UnixMilli(),
|
||
})
|
||
}
|
||
|
||
func validateInviteReferrerPair(invited userdomain.User, inviter userdomain.User) error {
|
||
if invited.UserID <= 0 || inviter.UserID <= 0 {
|
||
return xerr.New(xerr.NotFound, "inviter not found")
|
||
}
|
||
if invited.UserID == inviter.UserID {
|
||
// 自邀不进入邀请码解析,避免把当前用户自己的邀请码可用性暴露出去。
|
||
return xerr.New(xerr.InvalidInviteCode, "invalid invite code")
|
||
}
|
||
if !inviter.ProfileCompleted || !inviter.CanLogin() {
|
||
return xerr.New(xerr.NotFound, "inviter not found")
|
||
}
|
||
if invited.Country == "" || inviter.Country == "" || invited.Country != inviter.Country {
|
||
// 产品要求只能搜索和绑定本国家邀请人;跨国家统一返回 not found,避免泄漏目标用户是否存在。
|
||
return xerr.New(xerr.NotFound, "inviter not found")
|
||
}
|
||
return nil
|
||
}
|