feat: build core architecture & implement user auth modules 🚀 feat: 搭建核心架构并实现用户认证模块 🚀 Framework Migration: Switched from Hertz to Gin, providing a more idiomatic and lightweight web foundation. ⚡ 框架迁移:从 Hertz 切换至 Gin,构建了更符合 Go 惯例且轻量级的 Web 基础。⚡ Architectural Overhaul: Refactored the 3-layer architecture from global-variable-based calls to Explicit Dependency Injection (DI) via New... factory functions. This significantly improves testability and decoupling. 🏗️ 架构重构:将三层架构从基于“全局变量”的调用重构为通过 New... 工厂函数实现的显式依赖注入 (DI)。这大幅提升了代码的可测试性与解耦程度。🏗️ User Auth: Completed and tested Register, Login, and Token Refresh APIs with robust error handling and Bcrypt password hashing. 🔐 用户认证:完成了注册、登录与 Token 刷新接口并通过测试,包含健壮的错误处理与 Bcrypt 密码哈希加密。🔐 Config Management: Integrated Viper for centralized, environment-aware configuration management. ⚙️ 配置管理:集成了 Viper,实现了中心化且具备环境感知能力的配置管理。⚙️ DevOps & Docs: Added docker-compose.yml for seamless MySQL 8.0 & environment setup. 🐳 Updated README.md with corrections for mistakes in image quoting and formats. 📝 运维与文档: 新增 docker-compose.yml,实现 MySQL 8.0 环境的一键启动。🐳 更新 README.md,修改了一些图片引用和格式上小错误。📝
110 lines
3.2 KiB
Go
110 lines
3.2 KiB
Go
// Package service 业务逻辑层
|
||
// 包含所有核心业务逻辑
|
||
package service
|
||
|
||
import (
|
||
"errors"
|
||
|
||
"github.com/golang-jwt/jwt/v4"
|
||
"github.com/smartflow/backend/auth"
|
||
"github.com/smartflow/backend/dao"
|
||
"github.com/smartflow/backend/model"
|
||
"github.com/smartflow/backend/respond"
|
||
"github.com/smartflow/backend/utils"
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
type UserService struct {
|
||
// 伸出手:准备接住 DAO
|
||
repo *dao.UserDAO
|
||
}
|
||
|
||
// NewUserService:组装 Service 的“工厂”
|
||
func NewUserService(repo *dao.UserDAO) *UserService {
|
||
return &UserService{
|
||
repo: repo, // 把传进来的 DAO 揣进口袋里
|
||
}
|
||
}
|
||
|
||
func (sv *UserService) UserRegister(user model.UserRegisterRequest) (*model.UserRegisterResponse, error) {
|
||
//检查是否有空字段
|
||
if user.Username == "" || user.Password == "" ||
|
||
user.PhoneNumber == "" {
|
||
return nil, respond.MissingParam
|
||
}
|
||
// 检查字段长度是否超过90%
|
||
if len(user.Username) > 45 || len(user.Password) > 229 || len(user.PhoneNumber) > 18 {
|
||
return nil, respond.ParamTooLong
|
||
}
|
||
//检查用户名是否已存在
|
||
result, err := sv.repo.IfUsernameExists(user.Username)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
if result {
|
||
return nil, respond.InvalidName
|
||
}
|
||
hashedPwd, err := utils.HashPassword(user.Password) //调用utils层的方法
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
user.Password = hashedPwd //将user的密码字段改为加密后的密码
|
||
newUser, err := sv.repo.Create(user.Username, user.PhoneNumber, user.Password)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
//返回注册成功的用户ID
|
||
return &model.UserRegisterResponse{ID: newUser.ID}, nil
|
||
}
|
||
|
||
func (sv *UserService) UserLogin(req *model.UserLoginRequest) (*model.Tokens, error) {
|
||
var tokens model.Tokens
|
||
hashedPwd, err := sv.repo.GetUserHashedPasswordByName(req.Username) //调用dao层的方法
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, respond.WrongName
|
||
}
|
||
return nil, err
|
||
}
|
||
result, err := utils.CompareHashPwdAndPwd(hashedPwd, req.Password) //比较密码是否匹配
|
||
if err != nil { //其他错误
|
||
return &tokens, err
|
||
} else if !result { //密码不匹配
|
||
return nil, respond.WrongPwd
|
||
}
|
||
id, err := sv.repo.GetUserIDByName(req.Username)
|
||
if err != nil {
|
||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
return nil, respond.WrongName
|
||
}
|
||
return nil, err
|
||
}
|
||
tokens.AccessToken, tokens.RefreshToken, err = auth.GenerateTokens(id) //生成jwt key
|
||
if err != nil { //其他错误
|
||
return nil, err
|
||
}
|
||
return &tokens, nil
|
||
}
|
||
|
||
func (sv *UserService) RefreshTokenHandler(refreshToken string) (*model.Tokens, error) {
|
||
// 验证刷新令牌
|
||
token, err := auth.ValidateRefreshToken(refreshToken)
|
||
if err != nil || !token.Valid { // 刷新令牌无效
|
||
return nil, respond.InvalidRefreshToken
|
||
}
|
||
|
||
// 生成新的访问令牌和刷新令牌
|
||
if claims, ok := token.Claims.(jwt.MapClaims); ok {
|
||
userID := int(claims["user_id"].(float64))
|
||
newAccessToken, newRefreshToken, err := auth.GenerateTokens(userID)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 返回新的访问令牌和刷新令牌
|
||
return &model.Tokens{AccessToken: newAccessToken, RefreshToken: newRefreshToken}, nil
|
||
} else {
|
||
return nil, respond.InvalidClaims
|
||
}
|
||
}
|