Files
2026-09-23 17:42:55 +08:00

182 lines
5.3 KiB
Go

package liveclient
import (
"context"
"strings"
"time"
kratoserrors "github.com/go-kratos/kratos/v3/errors"
"golang.org/x/crypto/bcrypt"
"kra/internal/biz/system"
)
const (
ClientAuthorityID uint = 1000
InitialDiamonds int64 = 100
)
var (
ErrInvalidAccount = kratoserrors.BadRequest("LIVE_CLIENT_INVALID", "invalid client account data")
ErrUsernameExists = kratoserrors.BadRequest("LIVE_CLIENT_USERNAME_EXISTS", "username already exists")
ErrAccountNotFound = kratoserrors.NotFound("LIVE_CLIENT_NOT_FOUND", "client account not found")
)
type Account struct {
UserID uint
UUID string
Username string
Nickname string
Avatar string
AuthorityID uint
Enabled bool
DiamondBalance int64
CreatedAt time.Time
PasswordUpdatedAt *time.Time
}
type RegisterInput struct {
Username, Nickname, Password, ConfirmPassword string
}
type LoginInput struct {
Username, Password, IP, Agent string
}
type ProfileInput struct {
UserID uint
Nickname, Avatar string
}
type PasswordInput struct {
UserID uint
OldPassword, NewPassword, ConfirmPassword string
IP, Agent string
}
type AuthResult struct {
Account *Account
Auth *system.AuthenticationResult
}
type Repo interface {
Register(context.Context, *Account, string, int64) (*Account, error)
Get(context.Context, uint) (*Account, error)
UpdateProfile(context.Context, *ProfileInput) (*Account, error)
}
type Usecase struct {
repo Repo
security *system.SecurityUsecase
auth *system.AuthenticationUsecase
users *system.UserUsecase
}
func NewUsecase(repo Repo, security *system.SecurityUsecase, auth *system.AuthenticationUsecase, users *system.UserUsecase) *Usecase {
return &Usecase{repo: repo, security: security, auth: auth, users: users}
}
func (uc *Usecase) Register(ctx context.Context, in *RegisterInput, ip, agent string) (*AuthResult, error) {
if in == nil {
return nil, ErrInvalidAccount
}
in.Username = strings.TrimSpace(in.Username)
if n := len([]rune(in.Username)); n < 3 || n > 32 || in.Password == "" || in.Password != in.ConfirmPassword {
return nil, ErrInvalidAccount
}
in.Nickname = strings.TrimSpace(in.Nickname)
if in.Nickname == "" {
in.Nickname = in.Username
} else if n := len([]rune(in.Nickname)); n < 2 || n > 16 {
return nil, ErrInvalidAccount
}
config, err := uc.security.Current(ctx)
if err != nil {
return nil, err
}
if err = uc.security.ValidatePassword(config, in.Password); err != nil {
return nil, err
}
hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
if err != nil {
return nil, err
}
account, err := uc.repo.Register(ctx, &Account{Username: in.Username, Nickname: in.Nickname, AuthorityID: ClientAuthorityID, Enabled: true}, string(hash), InitialDiamonds)
if err != nil {
return nil, err
}
auth, err := uc.auth.AuthenticateRegisteredUser(ctx, accountUser(account), ip, agent)
if err != nil {
return nil, err
}
return &AuthResult{Account: account, Auth: auth}, nil
}
func (uc *Usecase) Login(ctx context.Context, in *LoginInput) (*AuthResult, error) {
if in == nil || strings.TrimSpace(in.Username) == "" || in.Password == "" {
return nil, ErrInvalidAccount
}
auth, err := uc.auth.LoginClient(ctx, &system.LoginAttempt{Username: strings.TrimSpace(in.Username), Password: in.Password, IP: in.IP, Agent: in.Agent})
if err != nil {
return nil, err
}
account, err := uc.repo.Get(ctx, auth.User.ID)
if err != nil {
return nil, err
}
return &AuthResult{Account: account, Auth: auth}, nil
}
func (uc *Usecase) Get(ctx context.Context, userID uint) (*Account, error) {
if userID == 0 {
return nil, ErrAccountNotFound
}
return uc.repo.Get(ctx, userID)
}
func (uc *Usecase) UpdateProfile(ctx context.Context, in *ProfileInput) (*Account, error) {
if in == nil || in.UserID == 0 {
return nil, ErrInvalidAccount
}
in.Nickname = strings.TrimSpace(in.Nickname)
in.Avatar = strings.TrimSpace(in.Avatar)
if in.Nickname != "" {
if n := len([]rune(in.Nickname)); n < 2 || n > 16 {
return nil, ErrInvalidAccount
}
}
if len([]rune(in.Avatar)) > 1000 {
return nil, ErrInvalidAccount
}
return uc.repo.UpdateProfile(ctx, in)
}
func (uc *Usecase) ChangePassword(ctx context.Context, in *PasswordInput) (*AuthResult, error) {
if in == nil || in.UserID == 0 || in.OldPassword == "" || in.NewPassword == "" || in.NewPassword != in.ConfirmPassword {
return nil, ErrInvalidAccount
}
config, err := uc.security.Current(ctx)
if err != nil {
return nil, err
}
if err = uc.security.ValidatePassword(config, in.NewPassword); err != nil {
return nil, err
}
if err = uc.users.ChangePassword(ctx, in.UserID, in.OldPassword, in.NewPassword); err != nil {
return nil, err
}
account, err := uc.repo.Get(ctx, in.UserID)
if err != nil {
return nil, err
}
auth, err := uc.auth.AuthenticateRegisteredUser(ctx, accountUser(account), in.IP, in.Agent)
if err != nil {
return nil, err
}
return &AuthResult{Account: account, Auth: auth}, nil
}
func accountUser(account *Account) *system.User {
return &system.User{ID: account.UserID, UUID: account.UUID, Username: account.Username, NickName: account.Nickname, HeaderImg: account.Avatar, AuthorityID: account.AuthorityID, Enable: 1, PasswordUpdatedAt: account.PasswordUpdatedAt}
}