mirror of
https://github.com/yv1ing/gin-admin.git
synced 2025-10-24 10:12:05 +08:00
完成基本框架搭建及系统用户登录流程
This commit is contained in:
58
internal/api/system/user_api.go
Normal file
58
internal/api/system/user_api.go
Normal 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
55
internal/auth/jwt.go
Normal 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非法")
|
||||
}
|
||||
49
internal/core/bootstrap.go
Normal file
49
internal/core/bootstrap.go
Normal 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)
|
||||
}
|
||||
}
|
||||
18
internal/core/config/global.go
Normal file
18
internal/core/config/global.go
Normal 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
|
||||
14
internal/core/config/mysql.go
Normal file
14
internal/core/config/mysql.go
Normal 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
|
||||
}
|
||||
20
internal/core/initialize/config.go
Normal file
20
internal/core/initialize/config.go
Normal 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
|
||||
}
|
||||
41
internal/core/initialize/database.go
Normal file
41
internal/core/initialize/database.go
Normal 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
|
||||
}
|
||||
20
internal/core/initialize/engine.go
Normal file
20
internal/core/initialize/engine.go
Normal 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
|
||||
}
|
||||
26
internal/core/initialize/user.go
Normal file
26
internal/core/initialize/user.go
Normal 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",
|
||||
})
|
||||
}
|
||||
12
internal/model/system/base.go
Normal file
12
internal/model/system/base.go
Normal 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"`
|
||||
}
|
||||
19
internal/model/system/user.go
Normal file
19
internal/model/system/user.go
Normal 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
|
||||
}
|
||||
22
internal/repository/repository.go
Normal file
22
internal/repository/repository.go
Normal 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,
|
||||
}
|
||||
}
|
||||
42
internal/repository/system/user_repo.go
Normal file
42
internal/repository/system/user_repo.go
Normal 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
19
internal/router/router.go
Normal 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)
|
||||
}
|
||||
40
internal/service/system/user_service.go
Normal file
40
internal/service/system/user_service.go
Normal 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
|
||||
}
|
||||
Reference in New Issue
Block a user