Skip to content

第28章:实战项目一 —— RESTful API 用户管理系统 ​

本章目标 ​

学完本章后,你将能够独立搭建一个完整的用户管理 API 系统

技术栈:Gin + GORM + JWT + bcrypt ​

项目结构 ​

user-api/
├── main.go
├── config/config.go
├── models/user.go
├── handlers/user.go
├── middlewares/auth.go
├── response/response.go
└── routes/router.go

核心代码 ​

go
// models/user.go
package models

import (
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
)

type User struct {
	gorm.Model
	Username string `gorm:"uniqueIndex;not null" json:"username"`
	Password string `gorm:"not null;->:false" json:"-"`
	Email    string `gorm:"uniqueIndex" json:"email"`
	Role     string `gorm:"default:user" json:"role"`
}

func (u *User) BeforeCreate(tx *gorm.DB) error {
	hashed, _ := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
	u.Password = string(hashed)
	return nil
}

func (u *User) CheckPassword(pwd string) bool {
	return bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(pwd)) == nil
}
go
// handlers/user.go
func Register(c *gin.Context) {
	var req struct {
		Username string `json:"username" binding:"required,min=3,max=50"`
		Password string `json:"password" binding:"required,min=6"`
		Email    string `json:"email" binding:"required,email"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.ValidationError(c, err.Error())
		return
	}
	user := models.User{Username: req.Username, Password: req.Password, Email: req.Email}
	if err := db.Create(&user).Error; err != nil {
		response.Error(c, 409, 1003, "用户名或邮箱已存在")
		return
	}
	response.Created(c, gin.H{"id": user.ID, "username": user.Username})
}

func Login(c *gin.Context) {
	var req struct {
		Username string `json:"username" binding:"required"`
		Password string `json:"password" binding:"required"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.ValidationError(c, err.Error())
		return
	}
	var user models.User
	if db.Where("username = ?", req.Username).First(&user).Error != nil {
		response.Error(c, 401, 1001, "用户名或密码错误")
		return
	}
	if !user.CheckPassword(req.Password) {
		response.Error(c, 401, 1002, "用户名或密码错误")
		return
	}
	token, _ := GenerateToken(user.ID, user.Username)
	response.Success(c, gin.H{"token": token})
}

func ListUsers(c *gin.Context) {
	page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10"))

	var users []models.User
	var total int64
	db.Model(&models.User{}).Count(&total)
	db.Scopes(Paginate(page, pageSize)).Find(&users)

	response.Success(c, gin.H{"users": users, "total": total})
}

路由注册 ​

go
// routes/router.go
func SetupRouter() *gin.Engine {
	r := gin.Default()

	api := r.Group("/api/v1")
	{
		api.POST("/register", handlers.Register)
		api.POST("/login", handlers.Login)

		auth := api.Group("")
		auth.Use(middlewares.JWTAuth())
		{
			auth.GET("/users", handlers.ListUsers)
			auth.GET("/users/:id", handlers.GetUser)
			auth.PUT("/users/:id", handlers.UpdateUser)
			auth.DELETE("/users/:id", handlers.DeleteUser)
		}
	}
	return r
}

API 接口列表 ​

方法路径认证说明
POST/api/v1/register否注册
POST/api/v1/login否登录
GET/api/v1/users是用户列表
GET/api/v1/users/:id是用户详情
PUT/api/v1/users/:id是更新用户
DELETE/api/v1/users/:id是删除用户

练习题 ​

  1. 克隆项目结构,实现所有 6 个接口。
  2. 添加"修改密码"接口(需要旧密码验证)。
  3. 添加"用户搜索"功能(按用户名/邮箱模糊搜索)。
  4. 添加角色权限控制(admin 可以看所有用户,user 只能看自己)。

把零散知识,整理成可以反复走的路。