2026-05-31 16:39:49 +08:00

121 lines
4.6 KiB
Go
Raw 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 auth
import (
"context"
"strings"
"hyapp/pkg/appcode"
"hyapp/pkg/xerr"
authdomain "hyapp/services/user-service/internal/domain/auth"
invitedomain "hyapp/services/user-service/internal/domain/invite"
userdomain "hyapp/services/user-service/internal/domain/user"
)
// QuickCreateAccount 创建一个已完成资料、已设置密码、可立即登录的账号。
// 该能力只复用内部领域事务,不依赖 firebase/mock provider方便游戏方联调时快速拿 uid/token。
func (s *Service) QuickCreateAccount(ctx context.Context, password string, registration authdomain.ThirdPartyRegistration, meta Meta) (authdomain.Token, error) {
meta.AppCode = appcode.Normalize(meta.AppCode)
registration.AppCode = meta.AppCode
registration = normalizeThirdPartyRegistration(registration, meta)
meta.Platform = registration.Platform
meta.Language = registration.Language
meta.Timezone = registration.Timezone
password = strings.TrimSpace(password)
if password == "" {
return authdomain.Token{}, xerr.New(xerr.InvalidArgument, "password is required")
}
if registration.DeviceID == "" || registration.Platform == "" {
// refresh session 必须绑定设备,平台也会进入注册来源和风控审计。
return authdomain.Token{}, xerr.New(xerr.InvalidArgument, "device_id and platform are required")
}
if err := validateThirdPartyRegistrationRequiredForCreate(registration); err != nil {
// 快捷账号直接跳过 onboarding因此必须一次性写齐客户端准入所需的最小资料。
return authdomain.Token{}, err
}
if err := validateThirdPartyRegistration(registration); err != nil {
return authdomain.Token{}, err
}
if s.authRepository == nil {
return authdomain.Token{}, xerr.New(xerr.Unavailable, "auth repository is not configured")
}
registration, err := s.resolveRegistrationCountry(ctx, registration)
if err != nil {
return authdomain.Token{}, err
}
passwordHash, err := hashPassword(password)
if err != nil {
return authdomain.Token{}, err
}
var lastErr error
for attempt := 0; attempt < s.allocateMaxAttempts; attempt++ {
user, displayIdentity := s.newUserWithIdentity(attempt)
user = applyThirdPartyRegistration(user, registration)
user.ProfileCompleted = true
user.ProfileCompletedAtMs = user.CreatedAtMs
user.OnboardingStatus = userdomain.OnboardingStatusCompleted
displayUserID, err := s.authRepository.AllocateDisplayUserID(ctx, registration.AppCode)
if err != nil {
return authdomain.Token{}, err
}
user.DefaultDisplayUserID = displayUserID
user.CurrentDisplayUserID = displayUserID
displayIdentity.AppCode = registration.AppCode
displayIdentity.DisplayUserID = displayUserID
displayIdentity.DefaultDisplayUserID = displayUserID
if !userdomain.ValidDisplayUserID(displayIdentity.DisplayUserID) {
// 分配器或历史种子异常时不写库,直接尝试下一个候选。
continue
}
session, refreshToken, err := s.newSession(registration.AppCode, user.UserID, registration.DeviceID)
if err != nil {
return authdomain.Token{}, err
}
account := authdomain.PasswordAccount{
AppCode: registration.AppCode,
UserID: user.UserID,
DisplayUserID: displayUserID,
PasswordHash: passwordHash,
HashAlg: hashAlgBcrypt,
CreatedAtMs: user.CreatedAtMs,
UpdatedAtMs: user.UpdatedAtMs,
}
inviteBind := invitedomain.BindCommand{
AppCode: registration.AppCode,
InvitedUserID: user.UserID,
InvitedRegionID: registration.RegionID,
InviteCode: registration.InviteCode,
Source: "quick_account_creation",
RequestID: meta.RequestID,
BoundAtMs: user.CreatedAtMs,
}
err = s.authRepository.CreatePasswordUser(ctx, user, displayIdentity, account, session, inviteBind)
if err == nil {
s.audit(ctx, meta, user.UserID, loginPassword, "", resultSuccess, "")
token, tokenErr := s.issueToken(user, session.SessionID, refreshToken)
if tokenErr != nil {
return authdomain.Token{}, tokenErr
}
s.importIMAccountAfterCommit(ctx, user, meta)
s.enqueueLoginIPRiskJob(ctx, meta, token, "account", registration.Platform, registration.Language, registration.Timezone)
return token, nil
}
if xerr.IsCode(err, xerr.DisplayUserIDExists) {
// 默认短号冲突可重试,避免少量并发建号把快捷入口打成失败。
lastErr = err
continue
}
s.audit(ctx, meta, 0, loginPassword, "", resultFailed, string(xerr.CodeOf(err)))
return authdomain.Token{}, err
}
if lastErr != nil {
s.audit(ctx, meta, 0, loginPassword, "", resultFailed, string(xerr.DisplayUserIDAllocateFailed))
}
return authdomain.Token{}, xerr.New(xerr.DisplayUserIDAllocateFailed, "display_user_id allocation failed")
}