Skip to content
数据库后端golang

数据库基础实现

mysql作为例子,使用GORM

首先定义用户数据 ——

go
// /internal/db/model.go
type User struct {
    ID        uint   `gorm:"primaryKey"`
    Username  string `gorm:"uniqueIndex;size:64;not null"`
    Password  string `gorm:"size:255;not null"` // 存储 bcrypt 哈希
    CreatedAt time.Time
    UpdatedAt time.Time
}

接着连接和初始化数据库 ——

go
// /internal/db/db.go
var DB *gorm.DB
func Init() error {
    dsn := config.MySQLDSN()
    gormDB, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        return err
    }
    
    // 自动迁移 User
    err = gormDB.AutoMigrate(&User{})
    if err != nil {
        return err
    }
    DB = gormDB
    return nil
}

然后CRUD这类操作在handler处实现,其中密码调用bcrypt做一遍哈希加密

go
// /internal/db/auth.go
func HashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    return string(bytes), err
}
func CheckPassword(hash, password string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

假设在已经初始化的数据库下,先提供注册、登录两个方法 ——

go
// internal/handler/auth.go
type registerReq struct {
    Username string `json:"username" binding:"required,min=3"`
    Password string `json:"password" binding:"required,min=6"`
}
func Register(c *gin.Context) {
    var req registerReq
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    hashed, err := db.HashPassword(req.Password)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "密码加密失败"})
        return
    }
    user := db.User{Username: req.Username, Password: hashed}
    if err := db.DB.Create(&user).Error; err != nil {
        c.JSON(http.StatusConflict, gin.H{"error": "用户名已存在"})
        return
    }
    c.JSON(http.StatusCreated, gin.H{"message": "注册成功"})
}
go
// internal/handler/auth.go
var jwtKey = []byte(config.JWTSecret())

type loginReq struct {
    Username string `json:"username" binding:"required"`
    Password string `json:"password" binding:"required"`
}
func Login(c *gin.Context) {
    var req loginReq
    if err := c.ShouldBindJSON(&req); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
        return
    }
    var user db.User
    if err := db.DB.Where("username = ?", req.Username).First(&user).Error; err != nil {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "用户不存在"})
        return
    }
    if !db.CheckPassword(user.Password, req.Password) {
        c.JSON(http.StatusUnauthorized, gin.H{"error": "密码错误"})
        return
    }
    
    // 生成 JWT
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
        "sub": user.ID,
        "exp": time.Now().Add(24 * time.Hour).Unix(),
    })
    tokenString, err := token.SignedString(jwtKey)
    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"error": "Token生成失败"})
        return
    }
    c.JSON(http.StatusOK, gin.H{"token": tokenString})
}

最后在主函数中初始化数据库和绑定路由

go
func main() {
	config.LoadConfig("config/config.yaml")
    err := db.Init()
    if err != nil {
        log.Fatalf("数据库连接失败: %v", err)
    }
    log.Println("✅ MySQL 已连接")
	r := gin.Default()
    r.POST("/register", handler.Register)
    r.POST("/login", handler.Login)
}

Released under the MIT License.