Skip to content

6. 新增“用户”管理表逻辑

向 AI 提问: 我要新增一个用户管理表。根据待办事项的项目逻辑,把这张用户管理表的逻辑补充完整。

好的,我来帮你添加用户管理相关的功能。

首先创建用户模型:

go
// :d:\works\RD\todo-list\models\user.go
package models

import (
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Username string `json:"username" gorm:"uniqueIndex;size:50;not null"`
	Password string `json:"password" gorm:"size:100;not null"`
	Email    string `json:"email" gorm:"size:100"`
	Status   int    `json:"status" gorm:"default:1"` // 1: 正常, 0: 禁用
	Todos    []Todo `json:"todos" gorm:"foreignKey:UserID"`
}

修改 Todo 模型,添加用户关联:

go
// :d:\works\RD\todo-list\models\todo.go
type Todo struct {
	gorm.Model
	Title       string    `json:"title"`
	Description string    `json:"description"`
	DueDate     time.Time `json:"due_date"`
	Completed   bool      `json:"completed"`
	UserID      uint      `json:"user_id"`
}

创建用户控制器:

go
// :d:\works\RD\todo-list\controllers\user_controller.go
package controllers

import (
	"net/http"
	"todo-list/models"
	"todo-list/utils"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

type UserController struct {
	DB *gorm.DB
}

func NewUserController(db *gorm.DB) *UserController {
	return &UserController{DB: db}
}

// 创建用户
func (uc *UserController) Create(c *gin.Context) {
	var user models.User
	if err := c.ShouldBindJSON(&user); err != nil {
		utils.ErrorResponse(c, http.StatusBadRequest, err.Error())
		return
	}

	// 密码加密
	user.Password = utils.HashPassword(user.Password)

	if err := uc.DB.Create(&user).Error; err != nil {
		utils.ErrorResponse(c, http.StatusInternalServerError, "创建用户失败")
		return
	}

	user.Password = "" // 返回数据时隐藏密码
	utils.SuccessResponse(c, user)
}

// 用户登录
func (uc *UserController) Login(c *gin.Context) {
	var loginInfo struct {
		Username string `json:"username"`
		Password string `json:"password"`
	}

	if err := c.ShouldBindJSON(&loginInfo); err != nil {
		utils.ErrorResponse(c, http.StatusBadRequest, err.Error())
		return
	}

	var user models.User
	if err := uc.DB.Where("username = ?", loginInfo.Username).First(&user).Error; err != nil {
		utils.ErrorResponse(c, http.StatusUnauthorized, "用户名或密码错误")
		return
	}

	if !utils.CheckPassword(loginInfo.Password, user.Password) {
		utils.ErrorResponse(c, http.StatusUnauthorized, "用户名或密码错误")
		return
	}

	// 生成 token
	token := utils.GenerateToken(user.ID)
	utils.SuccessResponse(c, gin.H{"token": token})
}

// 获取用户信息
func (uc *UserController) GetInfo(c *gin.Context) {
	userID := c.GetUint("userID") // 从中间件获取用户ID
	var user models.User

	if err := uc.DB.First(&user, userID).Error; err != nil {
		utils.ErrorResponse(c, http.StatusNotFound, "用户不存在")
		return
	}

	user.Password = "" // 隐藏密码
	utils.SuccessResponse(c, user)
}

添加密码加密和 Token 相关工具函数:

go
// :d:\works\RD\todo-list\utils\auth.go
package utils

import (
	"golang.org/x/crypto/bcrypt"
	"github.com/dgrijalva/jwt-go"
	"time"
)

var jwtSecret = []byte("your-secret-key")

func HashPassword(password string) string {
	bytes, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
	return string(bytes)
}

func CheckPassword(password, hash string) bool {
	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
	return err == nil
}

func GenerateToken(userID uint) string {
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
		"userID": userID,
		"exp":    time.Now().Add(time.Hour * 24).Unix(),
	})

	tokenString, _ := token.SignedString(jwtSecret)
	return tokenString
}

添加认证中间件:

go
// :d:\works\RD\todo-list\middleware\auth.go
package middleware

import (
	"github.com/gin-gonic/gin"
	"github.com/dgrijalva/jwt-go"
	"todo-list/utils"
	"strings"
)

func AuthMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		authHeader := c.GetHeader("Authorization")
		if authHeader == "" {
			utils.ErrorResponse(c, 401, "未授权")
			c.Abort()
			return
		}

		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			utils.ErrorResponse(c, 401, "无效的认证格式")
			c.Abort()
			return
		}

		token, err := jwt.Parse(parts[1], func(token *jwt.Token) (interface{}, error) {
			return []byte("your-secret-key"), nil
		})

		if err != nil || !token.Valid {
			utils.ErrorResponse(c, 401, "无效的token")
			c.Abort()
			return
		}

		claims := token.Claims.(jwt.MapClaims)
		c.Set("userID", uint(claims["userID"].(float64)))
		c.Next()
	}
}

更新路由配置:

go
// :d:\works\RD\todo-list\routes\routes.go
func SetupRouter() *gin.Engine {
	r := gin.Default()
	r.Use(middleware.Logger())

	db, _ := config.InitDB()
	todoController := controllers.NewTodoController(db)
	userController := controllers.NewUserController(db)

	v1 := r.Group("/api/v1")
	{
		// 用户相关路由
		users := v1.Group("/users")
		{
			users.POST("/register", userController.Create)
			users.POST("/login", userController.Login)
			users.GET("/info", middleware.AuthMiddleware(), userController.GetInfo)
		}

		// 待办事项路由,添加认证中间件
		todos := v1.Group("/todos", middleware.AuthMiddleware())
		{
			todos.POST("/", todoController.Create)
			todos.GET("/", todoController.GetAll)
			todos.GET("/:id", todoController.GetOne)
			todos.PUT("/:id", todoController.Update)
			todos.DELETE("/:id", todoController.Delete)
		}
	}

	return r
}

最后更新数据库配置,添加用户表迁移:

go
// :d:\works\RD\todo-list\config\database.go
	// 自动迁移数据库结构
	err = db.AutoMigrate(&models.User{}, &models.Todo{})
	if err != nil {
		return nil, err
	}

需要安装新的依赖:

bash
go get -u golang.org/x/crypto/bcrypt
go get -u github.com/dgrijalva/jwt-go

这样就完成了用户管理相关的功能,包括:

  1. 用户注册
  2. 用户登录
  3. 获取用户信息
  4. JWT 认证
  5. 待办事项与用户关联

所有的待办事项接口都需要认证才能访问,确保数据安全。

报错一

报错

An image

解决方法

你需要在项目目录下执行这些命令。让我们切换到正确的目录并安装依赖:

bash
cd d:\works\RD\todo-list
go get -u golang.org/x/crypto/bcrypt
go get -u github.com/dgrijalva/jwt-go

如果项目还没有初始化 go module,需要先执行:

bash
cd d:\works\RD\todo-list
go mod init todo-list
go mod tidy

然后再执行安装依赖的命令:

bash
go get -u golang.org/x/crypto/bcrypt
go get -u github.com/dgrijalva/jwt-go

这样就能正确安装所需的依赖包了。go mod tidy 命令会自动处理项目的依赖关系,添加缺少的依赖并删除不需要的依赖。

报错二

报错

An image

在 routes.go 中使用了 middleware 包但没有正确导入。

go
// routes/routes.go
package routes

import (
	"github.com/gin-gonic/gin"
	"todo-list/controllers"
	"todo-list/config"
	"todo-list/middleware"  // 添加这行导入
)
// 其他略...