本文最后更新于 2024-08-19,文章内容距离上一次更新已经过去了很久啦,可能已经过时了,请谨慎参考喵。

前情提要

https://blog.imbhj.com/archives/fsaSCzwqhttps://blog.imbhj.com/archives/VePoGk3Dhttps://blog.imbhj.com/archives/csO7QabUhttps://blog.imbhj.com/archives/ZemDwiHz

JWT鉴权配置

首先安装依赖:

go get github.com/dgrijalva/jwt-go

下一步在 /config.yaml 中添加配置:

# 系统配置
system:
  # 运行地址:本地
  host: "0.0.0.0"
  # 端口
  port: 5001
  # 启动环境:debug/release
  env: debug

# logger 日志配置
logger:
  # 日志等级
  level: info
  # 日志前缀
  prefix: '[goblog-admin]'
  # 日志文件路径
  director: logger
  # 显示行号
  show_line: true
  # 是否打印在控制台
  log_in_console: true

# MySQL 配置
mysql:
  # 监听地址
  host: 127.0.0.1
  # 端口
  port: 3306
  # 数据库名称
  db_name: goblogtest
  # 链接数据库用户名
  username: goblogtest
  # 链接数据库密码
  password: goblogtest
  # 日志等级
  log_level: dev
  # 字符集设置
  charset: utf8mb4
  # 最大连接数
  maxIdle: 50
  # 最大连接时间
  maxOpen: 150

# Redis 配置
redis:
  # redis 访问地址
  address: 127.0.0.1:6379
  # redis 连接密码
  password:
  # 数据库(默认为0,第一个)
  db: 0

# JWT token 配置
token:
  # 请求头
  headers: Bearer
  # 过期时间
  expireTime: 24
  # 密钥
  secret: goblog-admin
  # 签名
  issuer: admin

下一步在 /config/config.go 中定义结构体:

// 读取配置文件
// @author DaBaiLuoBo

package config

import (
    "gopkg.in/yaml.v2"
    "io/ioutil"
)

// config 总配置文件
type config struct {
    System system `yaml:"system"`
    Logger logger `yaml:"logger"`
    Mysql  mysql  `yaml:"mysql"`
    Redis  redis  `yaml:"redis"`
    Token  token  `yaml:"token"`
}

// 系统配置
type system struct {
    Host string `yaml:"host"`
    Port int    `yaml:"port"`
    Env  string `yaml:"env"`
}

// logger 日志配置
type logger struct {
    Level        string `yaml:"level"`
    Prefix       string `yaml:"prefix"`
    Director     string `yaml:"director"`
    ShowLine     bool   `yaml:"show_line"`
    LogInConsole bool   `yaml:"log_in_console"`
}

// MySQL 配置
type mysql struct {
    Host     string `yaml:"host"`
    Port     int    `yaml:"port"`
    DbName   string `yaml:"db_name"`
    Username string `yaml:"username"`
    Password string `yaml:"password"`
    LogLevel string `yaml:"log_level"`
    Charset  string `yaml:"charset"`
    MaxIdle  int    `yaml:"maxIdle"`
    MaxOpen  int    `yaml:"maxOpen"`
}

// Redis 配置
type redis struct {
    Address  string `yaml:"address"`
    Password string `yaml:"password"`
    Db       int    `yaml:"db"`
}

// JWT token 配置
type token struct {
    Headers    string `yaml:"headers"`
    ExpireTime int    `yaml:"expireTime"`
    Secret     string `yaml:"secret"`
    Issuer     string `yaml:"issuer"`
}

var Config *config

// init 初始化配置
func init() {
    yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
    // 如果为空,直接返回错误内容
    if err != nil {
        return
    }
    // 接收配置文件,存入 Config 中
    yaml.Unmarshal(yamlFile, &Config)
}

/main.go 中打印测试一下配置是否正确:

// 测试打印 token 配置
global.Log.Infof("Token 配置:%s:%d:%s:%s", config.Config.Token.Headers, config.Config.Token.ExpireTime, config.Config.Token.Secret, config.Config.Token.Issuer)

启动测试一下:

可以看到,有一个参数是不对的,修改一下配置文件 /config/config.go 中的过期时间:

ExpireTime int    `yaml:"expireTime"`

这里插一嘴,最好将 /config.yaml/config/config.go 中的相关参数名称更换为驼峰命名,请尽量减少下划线的使用

我已经更改完毕了 /config.yaml

# 系统配置
system:
  # 运行地址:本地
  host: "0.0.0.0"
  # 端口
  port: 5001
  # 启动环境:debug/release
  env: debug

# logger 日志配置
logger:
  # 日志等级
  level: info
  # 日志前缀
  prefix: '[goblog-admin]'
  # 日志文件路径
  director: logger
  # 显示行号
  showLine: true
  # 是否打印在控制台
  logInConsole: true

# MySQL 配置
mysql:
  # 监听地址
  host: 127.0.0.1
  # 端口
  port: 3306
  # 数据库名称
  dbName: goblogtest
  # 链接数据库用户名
  username: goblogtest
  # 链接数据库密码
  password: goblogtest
  # 日志等级
  logLevel: dev
  # 字符集设置
  charset: utf8mb4
  # 最大连接数
  maxIdle: 50
  # 最大连接时间
  maxOpen: 150

# Redis 配置
redis:
  # redis 访问地址
  address: 127.0.0.1:6379
  # redis 连接密码
  password:
  # 数据库(默认为0,第一个)
  db: 0

# JWT token 配置
token:
  # 请求头
  headers: Bearer
  # 过期时间
  expireTime: 24
  # 密钥
  secret: goblog-admin
  # 签名
  issuer: admin

/config/config.go

// 读取配置文件
// @author DaBaiLuoBo

package config

import (
    "gopkg.in/yaml.v2"
    "io/ioutil"
)

// config 总配置文件
type config struct {
    System system `yaml:"system"`
    Logger logger `yaml:"logger"`
    Mysql  mysql  `yaml:"mysql"`
    Redis  redis  `yaml:"redis"`
    Token  token  `yaml:"token"`
}

// 系统配置
type system struct {
    Host string `yaml:"host"`
    Port int    `yaml:"port"`
    Env  string `yaml:"env"`
}

// logger 日志配置
type logger struct {
    Level        string `yaml:"level"`
    Prefix       string `yaml:"prefix"`
    Director     string `yaml:"director"`
    ShowLine     bool   `yaml:"showLine"`
    LogInConsole bool   `yaml:"logInConsole"`
}

// MySQL 配置
type mysql struct {
    Host     string `yaml:"host"`
    Port     int    `yaml:"port"`
    DbName   string `yaml:"dbName"`
    Username string `yaml:"username"`
    Password string `yaml:"password"`
    LogLevel string `yaml:"logLevel"`
    Charset  string `yaml:"charset"`
    MaxIdle  int    `yaml:"maxIdle"`
    MaxOpen  int    `yaml:"maxOpen"`
}

// Redis 配置
type redis struct {
    Address  string `yaml:"address"`
    Password string `yaml:"password"`
    Db       int    `yaml:"db"`
}

// JWT token 配置
type token struct {
    Headers    string `yaml:"headers"`
    ExpireTime int    `yaml:"expireTime"`
    Secret     string `yaml:"secret"`
    Issuer     string `yaml:"issuer"`
}

var Config *config

// init 初始化配置
func init() {
    yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
    // 如果为空,直接返回错误内容
    if err != nil {
        return
    }
    // 接收配置文件,存入 Config 中
    yaml.Unmarshal(yamlFile, &Config)
}

再测试一下:

现在没什么问题了

Token生成

第一步还是先定义生成token所需要的一个结构体 /model/sysAdmin.go

为什么还是在用户模型里呢?这是因为 token 的生成是根据登录用户的特征去生成的

// 系统用户相关模型
// @author DaBaiLuoBo

package model

import "goblog-admin/utils"

// SysAdmin 用户模型对象
type SysAdmin struct {
    ID         uint        `gorm:"column:id;comment:'主键';primaryKey;NOT NULL" json:"id"`                      // ID
    Username   string      `gorm:"column:username;varchar(64);comment:'用户账号';NOT NULL" json:"username"`       // 用户账号
    Password   string      `gorm:"column:password;varchar(64);comment:'密码';NOT NULL" json:"password"`         // 密码
    Nickname   string      `gorm:"column:nickname;varchar(64);comment:'昵称'" json:"nickname"`                  // 昵称
    Status     int         `gorm:"column:status;default:1;comment:'账号启用状态:1-启用;2-禁用';NOT NULL" json:"status"` // 账号启用状态:1-启用;2-禁用
    Icon       string      `gorm:"column:icon;varchar(500);comment:'头像'" json:"icon"`                         // 头像
    Sex        int         `gorm:"column:sex;default:1;comment:'性别:1-男;2-女';NOT NULL" json:"sex"`             // 性别:1-男;2-女
    Email      string      `gorm:"column:email;varchar(64);comment:'邮箱'" json:"email"`                        // 邮箱
    Phone      string      `gorm:"column:phone;varchar(64);comment:'手机号'" json:"phone"`                       // 手机号
    Note       string      `gorm:"column:note;varchar(500);comment:'备注信息'" json:"note"`                       // 备注信息
    CreateTime utils.HTime `gorm:"column:create_time;comment:'创建时间';NOT NULL" json:"createTime"`              // 创建时间
}

func (SysAdmin) TableName() string {
    return "sys_admin"
}

// AddSysAdminDto 新增用户参数对象
type AddSysAdminDto struct {
    RoleId   uint   `validate:"required"` // 角色ID(不是用户ID)
    Username string `validate:"required"` // 用户名(账号)
    Password string `validate:"required"` // 密码
    Nickname string `validate:"required"` // 昵称
    Sex      int    // 性别:1-男;2-女
    Phone    string `validate:"required"` // 手机号
    Email    string `validate:"required"` // 邮箱
    Note     string // 备注信息
    Status   int    `validate:"required"` // 账号启用状态:1-启用;2-禁用
}

// SysAdminInfo 根据ID查询用户
type SysAdminInfo struct {
    ID       uint   `json:"id"`       // ID
    RoleId   uint   `json:"roleId"`   // 角色ID(不是用户ID)
    Username string `json:"username"` // 用户名(账号)
    Nickname string `json:"nickname"` // 昵称
    Sex      int    `json:"sex"`      // 性别:1-男;2-女
    Phone    string `json:"phone"`    // 手机号
    Email    string `json:"email"`    // 邮箱
    Note     string `json:"note"`     // 备注信息
    Status   int    `json:"status"`   // 账号启用状态:1-启用;2-禁用
}

// UpdateSysAdminDto 修改用户信息
type UpdateSysAdminDto struct {
    ID       uint   // ID
    RoleId   uint   `validate:"required"` // 角色ID(不是用户ID)
    Username string `validate:"required"` // 用户名(账号)
    Nickname string `validate:"required"` // 昵称
    Sex      int    // 性别:1-男;2-女
    Phone    string `validate:"required"` // 手机号
    Email    string `validate:"required"` // 邮箱
    Note     string // 备注信息
    Status   int    `validate:"required"` // 账号启用状态:1-启用;2-禁用
}

// SysAdminIdDto 用户ID参数
type SysAdminIdDto struct {
    ID uint `json:"id"` // ID
}

// UpdateSysAdminStatusDto 设置启用状态参数
type UpdateSysAdminStatusDto struct {
    ID     uint // ID
    Status int  // 账号启用状态:1-启用;2-禁用
}

// ResetSysAdminPasswordDto 重置密码参数对象
type ResetSysAdminPasswordDto struct {
    ID       uint   // ID
    Password string // 密码
}

// SysAdminVo 用户参数对象
type SysAdminVo struct {
    ID         uint        `json:"id"`         // ID
    Username   string      `json:"username"`   // 用户名(账号)
    Nickname   string      `json:"nickname"`   // 昵称
    Status     int         `json:"status"`     // 账号启用状态:1-启用;2-禁用
    Icon       string      `json:"icon"`       // 头像
    Sex        int         `json:"sex"`        // 性别:1-男;2-女
    Email      string      `json:"email"`      // 邮箱
    Phone      string      `json:"phone"`      // 手机号
    Note       string      `json:"note"`       // 备注信息
    RoleName   string      `json:"roleName"`   // 备注名称
    CreateTime utils.HTime `json:"createTime"` // 创建时间
}

// JwtAdmin 鉴权用户结构体
type JwtAdmin struct {
    ID         uint   `json:"id"`         // ID
    Username   string `json:"username"`   // 用户名(账号)
    Nickname   string `json:"nickname"`   // 昵称
    Status     int    `json:"status"`     // 账号启用状态:1-启用;2-禁用
    Icon       string `json:"icon"`       // 头像
    Email      string `json:"email"`      // 邮箱
    Phone      string `json:"phone"`      // 手机号
    Note       string `json:"note"`       // 备注信息
    ExpireTime int64  `json:"expireTime"` // 过期时间
}

创建一个核心组件 /core/jwt.go

// JWT 用户鉴权
// @author DaBaiLuoBo

package core

import (
    "github.com/dgrijalva/jwt-go"
    "goblog-admin/config"
    "goblog-admin/model"
    "time"
)

// userStdClaims 结构体
type userStdClaims struct {
    model.JwtAdmin
    jwt.StandardClaims
}

var (
    TokenExpireDuration = time.Duration(config.Config.Token.ExpireTime) * time.Hour // token 过期时间
    Secret              = []byte(config.Config.Token.Secret)                        // token 密钥
    Issuer              = config.Config.Token.Issuer                                // token 签发人
    ErrAbsent           = "Token is absent"                                         // 令牌不存在
    ErrInvalid          = "Token is invalid"                                        // 令牌无效
)

// GenerateTokenByAdmin 根据用户信息生成 token
func GenerateTokenByAdmin(sysAdmin model.SysAdmin) (string, error) {
    var jwtAdmin = model.JwtAdmin{
        ID:         sysAdmin.ID,
        Username:   sysAdmin.Username,
        Nickname:   sysAdmin.Nickname,
        Status:     sysAdmin.Status,
        Icon:       sysAdmin.Icon,
        Email:      sysAdmin.Email,
        Phone:      sysAdmin.Phone,
        Note:       sysAdmin.Note,
        ExpireTime: time.Now().Add(TokenExpireDuration).Unix(),
    }
    c := userStdClaims{
        jwtAdmin,
        jwt.StandardClaims{
            ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(),
            Issuer:    Issuer,
        },
    }
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
    return token.SignedString(Secret)
}

这样token生成工具就写好了,下一节来测试

登录以及测试Token

第一步还是一样,先在 /model/sysAdmin.go 中定义结构体:

// 系统用户相关模型
// @author DaBaiLuoBo

package model

import "goblog-admin/utils"

// SysAdmin 用户模型对象
type SysAdmin struct {
    ID         uint        `gorm:"column:id;comment:'主键';primaryKey;NOT NULL" json:"id"`                      // ID
    Username   string      `gorm:"column:username;varchar(64);comment:'用户账号';NOT NULL" json:"username"`       // 用户账号
    Password   string      `gorm:"column:password;varchar(64);comment:'密码';NOT NULL" json:"password"`         // 密码
    Nickname   string      `gorm:"column:nickname;varchar(64);comment:'昵称'" json:"nickname"`                  // 昵称
    Status     int         `gorm:"column:status;default:1;comment:'账号启用状态:1-启用;2-禁用';NOT NULL" json:"status"` // 账号启用状态:1-启用;2-禁用
    Icon       string      `gorm:"column:icon;varchar(500);comment:'头像'" json:"icon"`                         // 头像
    Sex        int         `gorm:"column:sex;default:1;comment:'性别:1-男;2-女';NOT NULL" json:"sex"`             // 性别:1-男;2-女
    Email      string      `gorm:"column:email;varchar(64);comment:'邮箱'" json:"email"`                        // 邮箱
    Phone      string      `gorm:"column:phone;varchar(64);comment:'手机号'" json:"phone"`                       // 手机号
    Note       string      `gorm:"column:note;varchar(500);comment:'备注信息'" json:"note"`                       // 备注信息
    CreateTime utils.HTime `gorm:"column:create_time;comment:'创建时间';NOT NULL" json:"createTime"`              // 创建时间
}

func (SysAdmin) TableName() string {
    return "sys_admin"
}

// AddSysAdminDto 新增用户参数对象
type AddSysAdminDto struct {
    RoleId   uint   `validate:"required"` // 角色ID(不是用户ID)
    Username string `validate:"required"` // 用户名(账号)
    Password string `validate:"required"` // 密码
    Nickname string `validate:"required"` // 昵称
    Sex      int    // 性别:1-男;2-女
    Phone    string `validate:"required"` // 手机号
    Email    string `validate:"required"` // 邮箱
    Note     string // 备注信息
    Status   int    `validate:"required"` // 账号启用状态:1-启用;2-禁用
}

// SysAdminInfo 根据ID查询用户
type SysAdminInfo struct {
    ID       uint   `json:"id"`       // ID
    RoleId   uint   `json:"roleId"`   // 角色ID(不是用户ID)
    Username string `json:"username"` // 用户名(账号)
    Nickname string `json:"nickname"` // 昵称
    Sex      int    `json:"sex"`      // 性别:1-男;2-女
    Phone    string `json:"phone"`    // 手机号
    Email    string `json:"email"`    // 邮箱
    Note     string `json:"note"`     // 备注信息
    Status   int    `json:"status"`   // 账号启用状态:1-启用;2-禁用
}

// UpdateSysAdminDto 修改用户信息
type UpdateSysAdminDto struct {
    ID       uint   // ID
    RoleId   uint   `validate:"required"` // 角色ID(不是用户ID)
    Username string `validate:"required"` // 用户名(账号)
    Nickname string `validate:"required"` // 昵称
    Sex      int    // 性别:1-男;2-女
    Phone    string `validate:"required"` // 手机号
    Email    string `validate:"required"` // 邮箱
    Note     string // 备注信息
    Status   int    `validate:"required"` // 账号启用状态:1-启用;2-禁用
}

// SysAdminIdDto 用户ID参数
type SysAdminIdDto struct {
    ID uint `json:"id"` // ID
}

// UpdateSysAdminStatusDto 设置启用状态参数
type UpdateSysAdminStatusDto struct {
    ID     uint // ID
    Status int  // 账号启用状态:1-启用;2-禁用
}

// ResetSysAdminPasswordDto 重置密码参数对象
type ResetSysAdminPasswordDto struct {
    ID       uint   // ID
    Password string // 密码
}

// SysAdminVo 用户参数对象
type SysAdminVo struct {
    ID         uint        `json:"id"`         // ID
    Username   string      `json:"username"`   // 用户名(账号)
    Nickname   string      `json:"nickname"`   // 昵称
    Status     int         `json:"status"`     // 账号启用状态:1-启用;2-禁用
    Icon       string      `json:"icon"`       // 头像
    Sex        int         `json:"sex"`        // 性别:1-男;2-女
    Email      string      `json:"email"`      // 邮箱
    Phone      string      `json:"phone"`      // 手机号
    Note       string      `json:"note"`       // 备注信息
    RoleName   string      `json:"roleName"`   // 备注名称
    CreateTime utils.HTime `json:"createTime"` // 创建时间
}

// JwtAdmin 鉴权用户结构体
type JwtAdmin struct {
    ID         uint   `json:"id"`         // ID
    Username   string `json:"username"`   // 用户名(账号)
    Nickname   string `json:"nickname"`   // 昵称
    Status     int    `json:"status"`     // 账号启用状态:1-启用;2-禁用
    Icon       string `json:"icon"`       // 头像
    Email      string `json:"email"`      // 邮箱
    Phone      string `json:"phone"`      // 手机号
    Note       string `json:"note"`       // 备注信息
    ExpireTime int64  `json:"expireTime"` // 过期时间
}

// LoginDto 登录结构体参数
type LoginDto struct {
    Username string `json:"username" validate:"required"` // 用户名(账号)
    Password string `json:"password" validate:"required"` // 密码
}

再创建一个定义系统常量的组件 /constant/constant.go

// 系统常量定义
// @author DaBaiLuoBo

package constant

const (
    NormalStatus  = 1 // 状态启用
    StatusDisable = 2 // 状态停用
)

回到 /api/sysAdmin.go 来写登录方法:

// 系统用户相关接口
// @author DaBaiLuoBo

package api

import (
    "github.com/gin-gonic/gin"
    "github.com/go-playground/validator/v10"
    "goblog-admin/constant"
    "goblog-admin/core"
    "goblog-admin/global"
    "goblog-admin/model"
    "goblog-admin/result"
    "goblog-admin/utils"
    "strconv"
    "time"
)

// CreateSysAdmin 新增用户
// @Summary 新增用户
// @Tags 用户相关接口
// @Produce json
// @Description 新增用户
// @Param data body model.AddSysAdminDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/add [post]
func CreateSysAdmin(c *gin.Context) {
    var dto model.AddSysAdminDto
    _ = c.BindJSON(&dto)
    // 对必填参数进行一个校验
    err := validator.New().Struct(dto)
    if err != nil {
        result.Failed(c, int(result.ApiCode.MissParameter), result.ApiCode.GetMessage(result.ApiCode.MissParameter))
        return
    }
    // 查询用户名是否重复
    sysAdminByUsername := GetSysAdminByUsername(dto.Username)
    if sysAdminByUsername.ID > 0 {
        result.Failed(c, int(result.ApiCode.UsernameAlreadyExists), result.ApiCode.GetMessage(result.ApiCode.UsernameAlreadyExists))
        return
    }
    // 新增用户
    sysAdmin := model.SysAdmin{
        Username:   dto.Username,
        Nickname:   dto.Nickname,
        Password:   utils.EncryptionMd5(dto.Password), // 使用加密工具类加密密码
        Phone:      dto.Phone,
        Email:      dto.Email,
        Sex:        dto.Sex,
        Note:       dto.Note,
        Status:     dto.Status,
        CreateTime: utils.HTime{Time: time.Now()},
    }
    core.Db.Create(&sysAdmin)
    // 新增用户与角色关系表信息
    sysAdminExists := GetSysAdminByUsername(dto.Username)
    var sysAdminRole model.SysAdminRole
    sysAdminRole.AdminId = sysAdminExists.ID // 用户ID
    sysAdminRole.RoleId = dto.RoleId         // 角色ID
    core.Db.Create(&sysAdminRole)
    result.Success(c, true)
}

// GetSysAdmin 根据ID查询用户
// @Summary 根据ID查询用户
// @Tags 用户相关接口
// @Produce json
// @Description 根据ID查询用户
// @Param id query int true "用户ID"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/info [get]
func GetSysAdmin(c *gin.Context) {
    Id, _ := strconv.Atoi(c.Query("id"))
    // 定义返回的结构体
    var sysAdminInfo model.SysAdminInfo
    core.Db.Table("sys_admin").
        Select("sys_admin.*", "sys_admin_role.role_id").
        Joins("LEFT JOIN sys_admin_role ON sys_admin.id = sys_admin_role.admin_id").
        Joins("LEFT JOIN sys_role ON sys_admin_role.role_id = sys_role.id").
        First(&sysAdminInfo, Id)
    result.Success(c, sysAdminInfo)
}

// UpdateSysAdmin 修改用户信息
// @Summary 修改用户信息
// @Tags 用户相关接口
// @Produce json
// @Description 修改用户信息
// @Param data body model.UpdateSysAdminDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/update [put]
func UpdateSysAdmin(c *gin.Context) {
    var dto model.UpdateSysAdminDto
    _ = c.BindJSON(&dto)
    // 对必填参数进行一个校验
    err := validator.New().Struct(dto)
    if err != nil {
        result.Failed(c, int(result.ApiCode.MissParameter), result.ApiCode.GetMessage(result.ApiCode.MissParameter))
        return
    }
    // 先查询用户信息
    var sysAdmin model.SysAdmin
    core.Db.First(&sysAdmin, dto.ID)
    // 再修改用户信息
    if dto.Username != "" {
        sysAdmin.Username = dto.Username
    }
    if dto.Nickname != "" {
        sysAdmin.Nickname = dto.Nickname
    }
    if dto.Phone != "" {
        sysAdmin.Phone = dto.Phone
    }
    if dto.Email != "" {
        sysAdmin.Email = dto.Email
    }
    if dto.Note != "" {
        sysAdmin.Note = dto.Note
    }
    sysAdmin.Status = dto.Status
    sysAdmin.Sex = dto.Sex
    core.Db.Save(&sysAdmin)
    // 删除之前绑定的角色再分配新的角色(角色与用户关系表)
    var sysAdminRole model.SysAdminRole
    core.Db.Where("admin_id = ?", dto.ID).Delete(&model.SysAdminRole{})
    sysAdminRole.AdminId = dto.ID
    sysAdminRole.RoleId = dto.RoleId
    core.Db.Create(&sysAdminRole)
    result.Success(c, true)
}

// DeleteSysAdmin 根据ID删除用户
// @Summary 根据ID删除用户
// @Tags 用户相关接口
// @Produce json
// @Description 根据ID删除用户
// @Param data body model.SysAdminIdDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/delete [delete]
func DeleteSysAdmin(c *gin.Context) {
    var dto model.SysAdminIdDto
    _ = c.BindJSON(&dto)
    core.Db.Delete(&model.SysAdmin{}, dto.ID)
    // 再查询关系表,并删除
    core.Db.Where("admin_id = ?", dto.ID).Delete(&model.SysAdminRole{})
    result.Success(c, true)
}

// UpdateSysAdminStatus 设置用户启用状态
// @Summary 设置用户启用状态
// @Tags 用户相关接口
// @Produce json
// @Description 设置用户启用状态
// @Param data body model.UpdateSysAdminStatusDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/updateStatus [put]
func UpdateSysAdminStatus(c *gin.Context) {
    var dto model.UpdateSysAdminStatusDto
    _ = c.BindJSON(&dto)
    var sysAdmin model.SysAdmin
    core.Db.First(&sysAdmin, dto.ID)
    sysAdmin.Status = dto.Status
    core.Db.Save(&sysAdmin)
    result.Success(c, true)
}

// ResetSysAdminPassword 重置用户密码
// @Summary 重置用户密码
// @Tags 用户相关接口
// @Produce json
// @Description 重置用户密码
// @Param data body model.ResetSysAdminPasswordDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/resetPassword [put]
func ResetSysAdminPassword(c *gin.Context) {
    var dto model.ResetSysAdminPasswordDto
    _ = c.BindJSON(&dto)
    var sysAdmin model.SysAdmin
    core.Db.First(&sysAdmin, dto.ID)
    sysAdmin.Password = utils.EncryptionMd5(dto.Password)
    core.Db.Save(&sysAdmin)
    result.Success(c, true)
}

// GetSysAdminList 分页查询用户列表
// @Summary 分页查询用户列表
// @Tags 用户相关接口
// @Produce json
// @Description 分页查询用户列表
// @Param pageNum query int false "分页数"
// @Param pageSize query int false "每页数"
// @Param username query string false "用户名称"
// @Param status query string false "账号启用状态:1-启用;2-禁用"
// @Param beginTime query string false "开始时间"
// @Param endTime query string false "结束时间"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/list [get]
func GetSysAdminList(c *gin.Context) {
    PageNum, _ := strconv.Atoi(c.Query("pageNum"))
    PageSize, _ := strconv.Atoi(c.Query("pageSize"))
    Username := c.Query("username")
    Status := c.Query("status")
    BeginTime := c.Query("beginTime")
    EndTime := c.Query("endTime")
    if PageSize < 1 {
        PageSize = 10
    }
    if PageNum < 1 {
        PageNum = 1
    }
    var sysAdminVo []model.SysAdminVo
    var count int64
    curDb := core.Db.Table("sys_admin").
        Select("sys_admin.*, sys_role.role_name").
        Joins("LEFT JOIN sys_admin_role ON sys_admin.id = sys_admin_role.admin_id").
        Joins("LEFT JOIN sys_role ON sys_role.id = sys_admin_role.role_id")
    if Username != "" {
        curDb = curDb.Where("sys_admin.username = ?", Username)
    }
    if BeginTime != "" && EndTime != "" {
        curDb = curDb.Where("sys_admin.create_time BETWEEN ? AND ?", BeginTime, EndTime)
    }
    if Status != "" {
        curDb = curDb.Where("sys_admin.status = ?", Status)
    }
    curDb.Count(&count).Limit(PageSize).Offset((PageNum - 1) * PageSize).Order("sys_admin.create_time DESC").Find(&sysAdminVo)
    result.Success(c, map[string]interface{}{"total": count, "pageSize": PageSize, "pageNum": PageNum, "list": sysAdminVo})
}

// Login 用户登录接口
// @Summary 用户登录接口
// @Tags 用户相关接口
// @Produce json
// @Description 用户登录接口
// @Param data body model.LoginDto true "data"
// @Success 200 {object} result.Result
// @router /api/sysAdmin/login [post]
func Login(c *gin.Context) {
    // 绑定 JSON 参数
    var dto model.LoginDto
    _ = c.BindJSON(&dto)
    // 对必填参数进行一个校验
    err := validator.New().Struct(dto)
    if err != nil {
        result.Failed(c, int(result.ApiCode.MissParameter), result.ApiCode.GetMessage(result.ApiCode.MissParameter))
        return
    }
    // 查询用户信息
    sysAdmin := GetSysAdminByUsername(dto.Username)
    if sysAdmin.Status == constant.StatusDisable {
        result.Failed(c, int(result.ApiCode.StatusDisable), result.ApiCode.GetMessage(result.ApiCode.StatusDisable))
        return
    }
    // 用户存在
    if sysAdmin.ID > 0 {
        // 校验密码是否正确
        if sysAdmin.Password != utils.EncryptionMd5(dto.Password) {
            result.Failed(c, int(result.ApiCode.PasswordNotTrue), result.ApiCode.GetMessage(result.ApiCode.PasswordNotTrue))
            return
        }
        // 生成 token
        token, _ := core.GenerateTokenByAdmin(sysAdmin)
        global.Log.Infof("用户 token:%s", token)
        // 组装用户信息 todo
        // 用户左侧菜单列表数据 todo
        // 用户权限列表 todo
        // 返回信息 todo
        result.Success(c, map[string]any{"token": token})
    } else { // 用户不存在
        result.Failed(c, int(result.ApiCode.SysAdminIsNotExists), result.ApiCode.GetMessage(result.ApiCode.SysAdminIsNotExists))
        return
    }
}

// GetSysAdminByUsername 根据用户名称查询用户
func GetSysAdminByUsername(username string) (sysAdmin model.SysAdmin) {
    core.Db.Where("username = ?", username).First(&sysAdmin)
    return sysAdmin
}

再定义一下新加的 code 码 /result/code.go

// 状态码/状态信息
// @author DaiBaiLuoBo

package result

// Codes 定义状态
type Codes struct {
    Message               map[uint]string
    Success               uint
    Failed                uint
    SysMenuIsExist        uint
    DelSysMenuFailed      uint
    DelSysChildMenuFailed uint
    RoleAlreadyExists     uint
    DelSysRoleFailed      uint
    MissParameter         uint
    UsernameAlreadyExists uint
    StatusDisable         uint
    SysAdminIsNotExists   uint
    PasswordNotTrue       uint
}

// ApiCode 状态码
var ApiCode = &Codes{
    Success:               200,
    Failed:                501,
    SysMenuIsExist:        600,
    DelSysMenuFailed:      601,
    DelSysChildMenuFailed: 602,
    RoleAlreadyExists:     603,
    DelSysRoleFailed:      604,
    MissParameter:         605,
    UsernameAlreadyExists: 606,
    StatusDisable:         607,
    SysAdminIsNotExists:   608,
    PasswordNotTrue:       609,
}

// 状态信息初始化
func init() {
    ApiCode.Message = map[uint]string{
        ApiCode.Success:               "成功",
        ApiCode.Failed:                "失败",
        ApiCode.SysMenuIsExist:        "菜单名称已存在,请重新输入",
        ApiCode.DelSysMenuFailed:      "菜单已分配,不能删除",
        ApiCode.DelSysChildMenuFailed: "该菜单存在子菜单,不能删除",
        ApiCode.RoleAlreadyExists:     "角色名或角色Key已存在,请重新输入",
        ApiCode.DelSysRoleFailed:      "角色已分配,不能删除",
        ApiCode.MissParameter:         "缺少必填参数",
        ApiCode.UsernameAlreadyExists: "用户名称已存在,请重新输入",
        ApiCode.StatusDisable:         "该账户已被禁用,请联系管理员",
        ApiCode.SysAdminIsNotExists:   "用户不存在,请先注册",
        ApiCode.PasswordNotTrue:       "密码错误,请重新输入",
    }
}

// GetMessage 供给外部调用状态信息
func (c *Codes) GetMessage(code uint) string {
    message, ok := c.Message[code]
    // 如果不 ok,返回空,ok 则返回 message
    if !ok {
        return ""
    }
    return message
}

下一步加入路由 /router/router.go

router.POST("/api/sysAdmin/login", api.Login)

最后初始化swag,测试一下:

ok,没什么问题喵~