119 lines
4.1 KiB
Go
119 lines
4.1 KiB
Go
package user
|
||
|
||
import (
|
||
"context"
|
||
|
||
"hyapp/pkg/appcode"
|
||
"hyapp/pkg/xerr"
|
||
userdomain "hyapp/services/user-service/internal/domain/user"
|
||
)
|
||
|
||
// GetUser 查询单个用户主状态。
|
||
func (s *Service) GetUser(ctx context.Context, userID int64) (userdomain.User, error) {
|
||
if userID <= 0 {
|
||
// service 层统一做参数校验,transport 只负责协议适配。
|
||
return userdomain.User{}, xerr.New(xerr.InvalidArgument, "user_id is required")
|
||
}
|
||
|
||
if s.userRepository == nil {
|
||
// repository 缺失代表应用装配不完整。
|
||
return userdomain.User{}, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
user, err := s.userRepository.GetUser(ctx, userID)
|
||
if err != nil {
|
||
return userdomain.User{}, err
|
||
}
|
||
|
||
// 查询用户时顺带执行靓号懒过期,返回给上游的是当前有效展示号。
|
||
return s.refreshExpiredUser(ctx, user, "")
|
||
}
|
||
|
||
// BatchGetUsers 批量查询用户主状态。
|
||
func (s *Service) BatchGetUsers(ctx context.Context, userIDs []int64) (map[int64]userdomain.User, error) {
|
||
if len(userIDs) == 0 {
|
||
// 空批量请求返回空 map,避免调用方处理 nil。
|
||
return map[int64]userdomain.User{}, nil
|
||
}
|
||
|
||
for _, userID := range userIDs {
|
||
if userID <= 0 {
|
||
// 任一非法 ID 都让整个批量请求失败,避免部分成功掩盖输入错误。
|
||
return nil, xerr.New(xerr.InvalidArgument, "user_ids contains invalid value")
|
||
}
|
||
}
|
||
|
||
if s.userRepository == nil {
|
||
// 批量查询同样不能绕过 repository。
|
||
return nil, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
users, err := s.userRepository.BatchGetUsers(ctx, userIDs)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
for userID, user := range users {
|
||
// 批量返回前逐个刷新过期靓号,保证身份投影一致。
|
||
refreshed, err := s.refreshExpiredUser(ctx, user, "")
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
users[userID] = refreshed
|
||
}
|
||
|
||
return users, nil
|
||
}
|
||
|
||
// CreateUser 创建用户主记录,并同时分配 active display_user_id。
|
||
// 该方法供注册和三方首次登录后续接入,避免出现没有短号的用户。
|
||
func (s *Service) CreateUser(ctx context.Context) (userdomain.User, userdomain.Identity, error) {
|
||
if s.userRepository == nil {
|
||
// 没有持久化能力时不能创建用户。
|
||
return userdomain.User{}, userdomain.Identity{}, xerr.New(xerr.Unavailable, "user repository is not configured")
|
||
}
|
||
|
||
for attempt := 0; attempt < s.allocateMaxAttempts; attempt++ {
|
||
// user_id 和 display_user_id 都在每次尝试中重新生成,降低冲突概率。
|
||
userID := s.idGenerator.NewInt64()
|
||
nowMs := s.now().UnixMilli()
|
||
displayUserID := s.displayUserIDAllocator.NextDisplayUserID(userID, attempt)
|
||
if !userdomain.ValidDisplayUserID(displayUserID) {
|
||
// allocator 理论上应生成合法短号;非法候选直接跳过本次尝试。
|
||
continue
|
||
}
|
||
|
||
createdUser := userdomain.User{
|
||
AppCode: appcode.FromContext(ctx),
|
||
UserID: userID,
|
||
DefaultDisplayUserID: displayUserID,
|
||
CurrentDisplayUserID: displayUserID,
|
||
CurrentDisplayUserIDKind: userdomain.DisplayUserIDKindDefault,
|
||
Status: userdomain.StatusActive,
|
||
CreatedAtMs: nowMs,
|
||
UpdatedAtMs: nowMs,
|
||
}
|
||
identity := userdomain.Identity{
|
||
AppCode: appcode.FromContext(ctx),
|
||
UserID: userID,
|
||
DisplayUserID: displayUserID,
|
||
DefaultDisplayUserID: displayUserID,
|
||
DisplayUserIDKind: userdomain.DisplayUserIDKindDefault,
|
||
Status: userdomain.DisplayUserIDStatusActive,
|
||
}
|
||
|
||
err := s.userRepository.CreateUserWithIdentity(ctx, createdUser, identity)
|
||
if err == nil {
|
||
// repository 事务成功后,用户和默认短号一定同时存在。
|
||
return createdUser, identity, nil
|
||
}
|
||
if !xerr.IsCode(err, xerr.DisplayUserIDExists) {
|
||
// 非短号冲突错误不能重试,否则可能掩盖真实存储异常。
|
||
return userdomain.User{}, userdomain.Identity{}, err
|
||
}
|
||
}
|
||
|
||
// 有限重试耗尽后返回稳定 reason,调用方可以提示稍后重试。
|
||
return userdomain.User{}, userdomain.Identity{}, xerr.New(xerr.DisplayUserIDAllocateFailed, "display_user_id allocation failed")
|
||
}
|