首次提交:初始化项目代码
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package auth
|
||||
|
||||
// RegisterRequest represents the request body for user registration.
|
||||
// RegisterRequest 表示用户注册的请求体。
|
||||
//
|
||||
// Fields:
|
||||
// - Username: The desired username (3-50 characters).
|
||||
// - Password: The desired password (minimum 6 characters).
|
||||
//
|
||||
// Validation:
|
||||
// - Username is required and must be 3-50 characters.
|
||||
// - Password is required and must be at least 6 characters.
|
||||
//
|
||||
// 字段:
|
||||
// - Username: 期望的用户名(3-50 个字符)。
|
||||
// - Password: 期望的密码(最少 6 个字符)。
|
||||
//
|
||||
// 验证规则:
|
||||
// - Username 是必填项,必须是 3-50 个字符。
|
||||
// - Password 是必填项,必须至少 6 个字符。
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=50"`
|
||||
Password string `json:"password" binding:"required,min=6"`
|
||||
Email string `json:"email"`
|
||||
Nickname string `json:"nickname"`
|
||||
CaptchaID string `json:"captcha_id" binding:"required"`
|
||||
CaptchaAnswer string `json:"captcha_answer" binding:"required"`
|
||||
EmailCode string `json:"email_code"` // 邮箱注册时必填
|
||||
}
|
||||
|
||||
// LoginRequest represents the request body for user login.
|
||||
// LoginRequest 表示用户登录的请求体。
|
||||
//
|
||||
// Fields:
|
||||
// - Username: The user's username.
|
||||
// - Password: The user's password.
|
||||
//
|
||||
// Validation:
|
||||
// - Both fields are required.
|
||||
//
|
||||
// 字段:
|
||||
// - Username: 用户的用户名。
|
||||
// - Password: 用户的密码。
|
||||
//
|
||||
// 验证规则:
|
||||
// - 两个字段都是必填项。
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" binding:"required"`
|
||||
Password string `json:"password" binding:"required"`
|
||||
CaptchaID string `json:"captcha_id" binding:"required"`
|
||||
CaptchaAnswer string `json:"captcha_answer" binding:"required"`
|
||||
}
|
||||
|
||||
// TokenResponse represents the response body for successful login.
|
||||
// TokenResponse 表示成功登录的响应体。
|
||||
//
|
||||
// Fields:
|
||||
// - Token: The JWT access token.
|
||||
//
|
||||
// 字段:
|
||||
// - Token: JWT 访问令牌。
|
||||
type TokenResponse struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
// UpdateProfileRequest represents the request body for updating user profile.
|
||||
// UpdateProfileRequest 表示更新用户个人信息的请求体。
|
||||
//
|
||||
// Fields:
|
||||
// - Email: The user's email address.
|
||||
// - Phone: The user's phone number.
|
||||
//
|
||||
// 字段:
|
||||
// - Email: 用户的邮箱地址。
|
||||
// - Phone: 用户的手机号码。
|
||||
type UpdateProfileRequest struct {
|
||||
Email string `json:"email"`
|
||||
Phone string `json:"phone"`
|
||||
Nickname string `json:"nickname"`
|
||||
}
|
||||
|
||||
// ChangePasswordRequest represents the request body for changing password.
|
||||
// ChangePasswordRequest 表示修改密码的请求体。
|
||||
//
|
||||
// Fields:
|
||||
// - CurrentPassword: The user's current password.
|
||||
// - NewPassword: The new password.
|
||||
//
|
||||
// 字段:
|
||||
// - CurrentPassword: 用户当前的密码。
|
||||
// - NewPassword: 新密码。
|
||||
type ChangePasswordRequest struct {
|
||||
CurrentPassword string `json:"currentPassword" binding:"required"`
|
||||
NewPassword string `json:"newPassword" binding:"required,min=6"`
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloudnest/internal/domain/auth/entity"
|
||||
"cloudnest/internal/domain/auth/repository"
|
||||
"cloudnest/internal/pkg/crypto"
|
||||
"cloudnest/internal/pkg/errors"
|
||||
"cloudnest/internal/pkg/jwt"
|
||||
)
|
||||
|
||||
type EmailVerifier interface {
|
||||
VerifyCode(email, code string) bool
|
||||
}
|
||||
|
||||
type AuthService struct {
|
||||
userRepo repository.UserRepository
|
||||
emailService EmailVerifier
|
||||
jwtSecret string
|
||||
jwtExpires int
|
||||
}
|
||||
|
||||
func NewAuthService(userRepo repository.UserRepository, emailService EmailVerifier, jwtSecret string, jwtExpires int) *AuthService {
|
||||
return &AuthService{
|
||||
userRepo: userRepo,
|
||||
emailService: emailService,
|
||||
jwtSecret: jwtSecret,
|
||||
jwtExpires: jwtExpires,
|
||||
}
|
||||
}
|
||||
|
||||
func generateUserCode() string {
|
||||
const charset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
r := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
b := make([]byte, 8)
|
||||
for i := range b {
|
||||
b[i] = charset[r.Intn(len(charset))]
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func (s *AuthService) Register(username, password, email, nickname, emailCode string) error {
|
||||
existing, err := s.userRepo.FindByUsername(username)
|
||||
if err == nil && existing != nil {
|
||||
return errors.Conflict("用户名已存在")
|
||||
}
|
||||
|
||||
if email != "" {
|
||||
existing, err := s.userRepo.FindByEmail(email)
|
||||
if err == nil && existing != nil {
|
||||
return errors.Conflict("邮箱已被注册")
|
||||
}
|
||||
if emailCode == "" {
|
||||
return errors.BadRequest("邮箱验证码不能为空")
|
||||
}
|
||||
if !s.emailService.VerifyCode(email, emailCode) {
|
||||
return errors.BadRequest("邮箱验证码错误")
|
||||
}
|
||||
}
|
||||
|
||||
hashed, err := crypto.HashPassword(password)
|
||||
if err != nil {
|
||||
return errors.InternalError("密码加密失败", err)
|
||||
}
|
||||
|
||||
userCode := generateUserCode()
|
||||
for {
|
||||
existing, err := s.userRepo.FindByUserCode(userCode)
|
||||
if err != nil || existing == nil {
|
||||
break
|
||||
}
|
||||
userCode = generateUserCode()
|
||||
}
|
||||
|
||||
user := entity.NewUser(username, hashed, userCode, nickname)
|
||||
if email != "" {
|
||||
user.Email = email
|
||||
}
|
||||
return s.userRepo.Create(user)
|
||||
}
|
||||
|
||||
func (s *AuthService) Login(loginName, password string) (string, *entity.User, error) {
|
||||
var user *entity.User
|
||||
var err error
|
||||
|
||||
if strings.Contains(loginName, "@") {
|
||||
user, err = s.userRepo.FindByEmail(loginName)
|
||||
} else {
|
||||
user, err = s.userRepo.FindByUsername(loginName)
|
||||
}
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if !crypto.CheckPasswordHash(password, user.Password) {
|
||||
return "", nil, errors.Unauthorized("用户名或密码错误")
|
||||
}
|
||||
|
||||
expiresIn := time.Duration(s.jwtExpires) * time.Hour
|
||||
token, err := jwt.GenerateToken(user.ID, user.Username, user.UserCode, s.jwtSecret, expiresIn)
|
||||
return token, user, err
|
||||
}
|
||||
|
||||
func (s *AuthService) CheckStorageLimit(userCode string, fileSize int64) error {
|
||||
user, err := s.userRepo.FindByUserCode(userCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if user.StorageUsed+fileSize > user.StorageLimit {
|
||||
return errors.BadRequest("存储空间不足,当前已使用 " + formatSize(user.StorageUsed) + ",限制 " + formatSize(user.StorageLimit))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthService) AddStorageUsed(userCode string, fileSize int64) error {
|
||||
return s.userRepo.UpdateStorageUsed(userCode, fileSize)
|
||||
}
|
||||
|
||||
func (s *AuthService) SubtractStorageUsed(userCode string, fileSize int64) error {
|
||||
return s.userRepo.UpdateStorageUsed(userCode, -fileSize)
|
||||
}
|
||||
|
||||
func (s *AuthService) UpdateProfile(userCode, email, phone, nickname string) error {
|
||||
return s.userRepo.UpdateProfile(userCode, email, phone, nickname)
|
||||
}
|
||||
|
||||
func (s *AuthService) ChangePassword(userCode, currentPassword, newPassword string) error {
|
||||
user, err := s.userRepo.FindByUserCode(userCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !crypto.CheckPasswordHash(currentPassword, user.Password) {
|
||||
return errors.Unauthorized("当前密码不正确")
|
||||
}
|
||||
hashed, err := crypto.HashPassword(newPassword)
|
||||
if err != nil {
|
||||
return errors.InternalError("密码加密失败", err)
|
||||
}
|
||||
return s.userRepo.UpdatePassword(userCode, hashed)
|
||||
}
|
||||
|
||||
func (s *AuthService) GetUserInfo(userCode string) (*entity.User, error) {
|
||||
return s.userRepo.FindByUserCode(userCode)
|
||||
}
|
||||
|
||||
func formatSize(bytes int64) string {
|
||||
if bytes < 1024 {
|
||||
return fmt.Sprintf("%d B", bytes)
|
||||
} else if bytes < 1024*1024 {
|
||||
return fmt.Sprintf("%.2f KB", float64(bytes)/1024)
|
||||
} else if bytes < 1024*1024*1024 {
|
||||
return fmt.Sprintf("%.2f MB", float64(bytes)/(1024*1024))
|
||||
}
|
||||
return fmt.Sprintf("%.2f GB", float64(bytes)/(1024*1024*1024))
|
||||
}
|
||||
Reference in New Issue
Block a user