完成基本框架搭建及系统用户登录流程

This commit is contained in:
2025-08-28 17:24:32 +08:00
parent 5a8029b3c6
commit 0a3be6cf9e
22 changed files with 736 additions and 16 deletions

View File

@@ -0,0 +1,58 @@
package system
import (
"github.com/gin-gonic/gin"
"net/http"
systemmodel "gin-admin/internal/model/system"
systemservice "gin-admin/internal/service/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 16:29
// @Desc: 系统用户服务接口
type SysUserLoginReq struct {
Username string `json:"username"`
Password string `json:"password"`
}
func SysUserLoginHandler(ctx *gin.Context) {
var req SysUserLoginReq
err := ctx.ShouldBindJSON(&req)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, systemmodel.Response{
Code: http.StatusBadRequest,
Info: "请求参数非法",
})
return
}
jwtToken, err := systemservice.SysUserLogin(req.Username, req.Password)
if err != nil {
ctx.AbortWithStatusJSON(http.StatusInternalServerError, systemmodel.Response{
Code: http.StatusInternalServerError,
Info: "登录请求失败:" + err.Error(),
})
return
}
if jwtToken == "" {
ctx.AbortWithStatusJSON(http.StatusForbidden, systemmodel.Response{
Code: http.StatusForbidden,
Info: "登录请求失败:账号或密码错误",
})
return
} else {
ctx.AbortWithStatusJSON(http.StatusOK, systemmodel.Response{
Code: http.StatusOK,
Info: "登录请求成功",
Data: gin.H{
"token": jwtToken,
},
})
return
}
}

55
internal/auth/jwt.go Normal file
View File

@@ -0,0 +1,55 @@
package auth
import (
"errors"
"fmt"
"gin-admin/internal/core/config"
"github.com/golang-jwt/jwt/v5"
"time"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 15:54
// @Desc: jwt鉴权方法
type AccessClaims struct {
ID uint `json:"id"`
Username string `json:"username"`
jwt.RegisteredClaims
}
func CreateAccessToken(ID uint, username string, ttl time.Duration) (string, error) {
jwtSecret := []byte(config.Config.SecretKey)
claims := AccessClaims{
ID: ID,
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
Subject: "gin-admin",
IssuedAt: jwt.NewNumericDate(time.Now()),
ExpiresAt: jwt.NewNumericDate(time.Now().Add(ttl)),
ID: fmt.Sprintf("%d-%d", ID, time.Now().UnixNano()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(jwtSecret)
}
func ParseAccessToken(tokenStr string) (*AccessClaims, error) {
jwtSecret := []byte(config.Config.SecretKey)
token, err := jwt.ParseWithClaims(tokenStr, &AccessClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwtSecret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*AccessClaims); ok && token.Valid {
return claims, nil
}
return nil, errors.New("token非法")
}

View File

@@ -0,0 +1,49 @@
package core
import (
"fmt"
"gin-admin/internal/core/config"
"gin-admin/internal/core/initialize"
"gin-admin/pkg/logger"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 13:58
// @Desc: API服务入口
func Start() {
var err error
// 加载配置
err = initialize.InitConfig()
if err != nil {
logger.Error("加载配置失败:", err)
return
}
// 初始化数据库
err = initialize.InitDatabase()
if err != nil {
logger.Error("初始化数据库失败:", err)
return
}
// 初始化系统用户
err = initialize.InitSysUser()
if err != nil {
logger.Error("初始化系统用户失败:", err)
return
}
// 初始化引擎
eng := initialize.InitEngine()
addr := fmt.Sprintf("%s:%d", config.Config.ListenHost, config.Config.ListenPort)
// 启动引擎
logger.Info("正在启动引擎,监听在:", addr)
err = eng.Run(addr)
if err != nil {
logger.Error("启动引擎失败:", err)
}
}

View File

@@ -0,0 +1,18 @@
package config
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 14:01
// @Desc: 系统全局配置
type globalConfig struct {
ListenHost string
ListenPort uint
SecretKey string
RootName string
RootPass string
Mysql mysqlConfig
}
var Config *globalConfig

View File

@@ -0,0 +1,14 @@
package config
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 14:24
// @Desc: MySQL配置
type mysqlConfig struct {
Host string
Port uint
Username string
Password string
Database string
}

View File

@@ -0,0 +1,20 @@
package initialize
import (
"gin-admin/internal/core/config"
"github.com/BurntSushi/toml"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 14:06
// @Desc: 初始化系统配置
func InitConfig() error {
_, err := toml.DecodeFile("configs/config.toml", &config.Config)
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,41 @@
package initialize
import (
"fmt"
"gin-admin/internal/core/config"
"gin-admin/internal/repository"
"gorm.io/driver/mysql"
"gorm.io/gorm"
systemmodel "gin-admin/internal/model/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 14:22
// @Desc: 初始化mysql数据库
func dsn() string {
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
config.Config.Mysql.Username,
config.Config.Mysql.Password,
config.Config.Mysql.Host,
config.Config.Mysql.Port,
config.Config.Mysql.Database,
)
}
func InitDatabase() error {
db, err := gorm.Open(mysql.Open(dsn()), &gorm.Config{})
if err != nil {
return err
}
err = db.AutoMigrate(
&systemmodel.User{},
)
repository.SetupRepository(db)
return nil
}

View File

@@ -0,0 +1,20 @@
package initialize
import (
"gin-admin/internal/router"
"github.com/gin-gonic/gin"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 13:59
// @Desc: 初始化gin引擎
func InitEngine() *gin.Engine {
gin.SetMode(gin.ReleaseMode)
eng := gin.New()
router.SetupRoutes(eng)
return eng
}

View File

@@ -0,0 +1,26 @@
package initialize
import (
"gin-admin/internal/core/config"
"gin-admin/internal/repository"
"gin-admin/pkg/encrypt"
systemmodel "gin-admin/internal/model/system"
systemrepository "gin-admin/internal/repository/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 17:05
// @Desc: 初始化系统用户
func InitSysUser() error {
return systemrepository.CreateUser(repository.Repo.DB, &systemmodel.User{
IsActive: true,
Username: config.Config.RootName,
Password: encrypt.Sha256String(config.Config.RootPass, config.Config.SecretKey),
Avatar: "/logo.png",
Email: "root@gin-admin.cn",
Phone: "18888888888",
})
}

View File

@@ -0,0 +1,12 @@
package system
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 16:00
// @Desc: 系统基础数据模型
type Response struct {
Code int `json:"code"`
Info string `json:"info"`
Data any `json:"data"`
}

View File

@@ -0,0 +1,19 @@
package system
import "gorm.io/gorm"
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 15:46
// @Desc: 系统用户角色模型
type User struct {
gorm.Model
IsActive bool `gorm:"default:true"`
Username string `gorm:"uniqueIndex;size:255"`
Password string
Avatar string
Email string
Phone string
}

View File

@@ -0,0 +1,22 @@
package repository
import (
"gorm.io/gorm"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 14:34
// @Desc: 数据操作的抽象封装
type repository struct {
DB *gorm.DB
}
var Repo *repository
func SetupRepository(db *gorm.DB) {
Repo = &repository{
DB: db,
}
}

View File

@@ -0,0 +1,42 @@
package system
import (
"gorm.io/gorm"
systemmodel "gin-admin/internal/model/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 16:33
// @Desc: 系统用户数据库操作实现
func CreateUser(db *gorm.DB, user *systemmodel.User) error {
return db.Create(user).Error
}
func DeleteUser(db *gorm.DB, id uint) error {
return db.Delete(&systemmodel.User{}, id).Error
}
func UpdateUser(db *gorm.DB, user *systemmodel.User) error {
return db.Save(user).Error
}
func GetUserByID(db *gorm.DB, id uint) (*systemmodel.User, error) {
var user systemmodel.User
err := db.First(&user, id).Error
if err != nil {
return nil, err
}
return &user, nil
}
func GetUserByUsername(db *gorm.DB, username string) (*systemmodel.User, error) {
var user systemmodel.User
err := db.Where("username = ?", username).First(&user).Error
if err != nil {
return nil, err
}
return &user, nil
}

19
internal/router/router.go Normal file
View File

@@ -0,0 +1,19 @@
package router
import (
"github.com/gin-gonic/gin"
systemapi "gin-admin/internal/api/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 16:17
// @Desc: 路由注册入口
func SetupRoutes(eng *gin.Engine) {
api := eng.Group("/api")
/* 系统内置接口 */
api.POST("/sys/login", systemapi.SysUserLoginHandler)
}

View File

@@ -0,0 +1,40 @@
package system
import (
"errors"
"gin-admin/internal/auth"
"gin-admin/internal/core/config"
"gin-admin/internal/repository"
"gin-admin/pkg/encrypt"
"gorm.io/gorm"
systemrepository "gin-admin/internal/repository/system"
)
// @Author: yv1ing
// @Author: me@yvling.cn
// @Date: 2025/8/28 16:30
// @Desc: 系统用户服务实现
// SysUserLogin 系统用户登录
func SysUserLogin(username, password string) (string, error) {
user, err := systemrepository.GetUserByUsername(repository.Repo.DB, username)
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
return "", errors.New("系统内部错误")
}
if errors.Is(err, gorm.ErrRecordNotFound) || encrypt.Sha256String(password, config.Config.SecretKey) != user.Password {
return "", nil
}
if !user.IsActive {
return "", errors.New("用户已被禁用")
}
jwtToken, err := auth.CreateAccessToken(user.ID, user.Username, 3600)
if err != nil {
return "", errors.New("系统内部错误")
}
return jwtToken, nil
}