update
This commit is contained in:
117
internal/service/auth_service.go
Normal file
117
internal/service/auth_service.go
Normal file
@ -0,0 +1,117 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"fashionapi/internal/dto"
|
||||
"fashionapi/internal/model"
|
||||
"fashionapi/internal/pkg/jwt"
|
||||
"fashionapi/internal/repository"
|
||||
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// emailRegex 邮箱格式校验,与原项目一致。
|
||||
var emailRegex = regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`)
|
||||
|
||||
// minPasswordLength 密码最小长度。
|
||||
const minPasswordLength = 6
|
||||
|
||||
// AuthService 账号体系业务接口。
|
||||
type AuthService interface {
|
||||
Register(ctx context.Context, req dto.AuthRequest) (*dto.AuthResult, error)
|
||||
Login(ctx context.Context, req dto.AuthRequest) (*dto.AuthResult, error)
|
||||
Me(ctx context.Context, userID uint32) (*dto.UserPayload, error)
|
||||
}
|
||||
|
||||
type authService struct {
|
||||
users repository.UserRepository
|
||||
jwt *jwt.Manager
|
||||
}
|
||||
|
||||
// NewAuthService 创建账号服务。
|
||||
func NewAuthService(users repository.UserRepository, jwtManager *jwt.Manager) AuthService {
|
||||
return &authService{users: users, jwt: jwtManager}
|
||||
}
|
||||
|
||||
// Register 注册新用户,成功后直接签发令牌(免去前端再调一次登录)。
|
||||
func (s *authService) Register(ctx context.Context, req dto.AuthRequest) (*dto.AuthResult, error) {
|
||||
username := strings.TrimSpace(req.Username)
|
||||
email := strings.TrimSpace(req.Email)
|
||||
password := req.Password
|
||||
|
||||
if username == "" || email == "" || len(password) < minPasswordLength {
|
||||
return nil, badRequest("username、email 与 password(>=6 位) 均为必填")
|
||||
}
|
||||
if !emailRegex.MatchString(email) {
|
||||
return nil, badRequest("email 格式不正确")
|
||||
}
|
||||
|
||||
exists, err := s.users.ExistsByUsernameOrEmail(ctx, username, email)
|
||||
if err != nil {
|
||||
return nil, internalErr("server error")
|
||||
}
|
||||
if exists {
|
||||
return nil, conflict("该用户名或邮箱已被注册")
|
||||
}
|
||||
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, internalErr("server error")
|
||||
}
|
||||
|
||||
user := model.User{Username: username, Email: email, PasswordHash: string(hash)}
|
||||
if err := s.users.Create(ctx, &user); err != nil {
|
||||
return nil, internalErr("创建用户失败")
|
||||
}
|
||||
return s.issue(user)
|
||||
}
|
||||
|
||||
// Login 登录:account 可以是邮箱或用户名。
|
||||
func (s *authService) Login(ctx context.Context, req dto.AuthRequest) (*dto.AuthResult, error) {
|
||||
account := strings.TrimSpace(req.Account)
|
||||
password := req.Password
|
||||
if account == "" || password == "" {
|
||||
return nil, badRequest("账号与密码均为必填")
|
||||
}
|
||||
|
||||
user, err := s.users.FindByAccount(ctx, account)
|
||||
if err != nil {
|
||||
if errors.Is(err, repository.ErrNotFound) {
|
||||
// 账号不存在与密码错误返回同一文案,避免账号枚举
|
||||
return nil, unauthorized("账号或密码错误")
|
||||
}
|
||||
return nil, internalErr("server error")
|
||||
}
|
||||
if bcrypt.CompareHashAndPassword([]byte(user.PasswordHash), []byte(password)) != nil {
|
||||
return nil, unauthorized("账号或密码错误")
|
||||
}
|
||||
return s.issue(*user)
|
||||
}
|
||||
|
||||
// Me 用令牌中的 uid 取回最新用户信息。
|
||||
func (s *authService) Me(ctx context.Context, userID uint32) (*dto.UserPayload, error) {
|
||||
user, err := s.users.FindByID(ctx, userID)
|
||||
if err != nil {
|
||||
// 令牌有效但用户已不存在,同样视为未授权
|
||||
return nil, unauthorized("unauthorized")
|
||||
}
|
||||
p := toUserPayload(*user)
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
// issue 签发令牌并组装返回体。
|
||||
func (s *authService) issue(u model.User) (*dto.AuthResult, error) {
|
||||
token, err := s.jwt.Generate(u.ID, u.Username, u.Email)
|
||||
if err != nil {
|
||||
return nil, internalErr("server error")
|
||||
}
|
||||
return &dto.AuthResult{Token: token, User: toUserPayload(u)}, nil
|
||||
}
|
||||
|
||||
func toUserPayload(u model.User) dto.UserPayload {
|
||||
return dto.UserPayload{ID: u.ID, Username: u.Username, Email: u.Email}
|
||||
}
|
||||
Reference in New Issue
Block a user