使用Gorm、Hertz实现简单登录注册 | 青训营笔记

363 阅读3分钟

这是我参与「第五届青训营 」伴学笔记创作活动的第 4 天

前置知识

Gorm

Gorm 中文文档 GORM 指南 | GORM - The fantastic ORM library for Golang, aims to be developer friendly.

定义Gorm model。

注意:

  • 如果不写函数TableName(),会使用结构体名的蛇形负数加上 s 作为表名。
  • 使用 delete_at 作为软删除字段,使用create_at作为创建时间,使用 update_at 作为更新时间。
  • gorm使用名为id的字段作为主键,如果定义。
  • gorm.Model 相当于自带了 ID uint 默认主键,CreatedAt time.Time 创建时间,UpdatedAt time.Time 更新时间,DeletedAt DeletedAt 软删除时间
type User struct {
    gorm.Model
    UserAccount  string `gorm:"column:user_account;type:varchar(255);" json:"account"`
    UserPassword string `gorm:"column:user_password;type:varchar(255);" json:"password"`
}
func (table *User) TableName() string {
    return "user"
}

链接数据库

// 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情
  dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

CRUD ,仅列举常用

// 创建数据
db.Create(&User{UserAccount:"rheght", UserPassword:"123456"})
// 查找数据
var User user
db.First(&user, 1) // 根据主键查找
db.First(&user, "user_account", "rheght")// 查找user_account为rheght的用户
// 更新数据
db.Model(&user).Update("user_password", "111111") // 将 user 的 user_password 更新为 111111
db.Model(&user).updates(User{UserAccount:"rheght123", UserPassword:"123123"}) // 仅更新为零值字段
db.MOdel(&user).updates(map[string]interface{}{UserAccount:"rheght", UserPassword:"123456"}) // 可更新零值
// 删除
db.Delete(&user, 1) 

Hertz

Hertz 中文文档快速开始 | CloudWeGo

下面是一个简单接口,可以运行测试环境是否配置完成。

package main
​
import (
    "context"
​
    "github.com/cloudwego/hertz/pkg/app"
    "github.com/cloudwego/hertz/pkg/app/server"
    "github.com/cloudwego/hertz/pkg/common/utils"
    "github.com/cloudwego/hertz/pkg/protocol/consts"
)
​
func main() {
    h := server.Default()
​
    h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
            ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
    })
​
    h.Spin()
}

接下来是需要用到的方法,接收前端传来的请求体,将 json 数据转成结构体对象,

bytes, err := ctx.Body()
    if err != nil {
        panic(err)
    }
var user models.User
    if err = json.Unmarshal(bytes, &user); err != nil {
        panic(err)
    }
result := models.MatchUser(user.UserAccount, user.UserPassword) // 此时的user已经存有数据
ctx.JSON(consts.StatusOK, result)

代码实现

首先先创建软件包,参考 Hertz 的自动生成代码的目录

  • router
  • models
  • common
  • handler
  • test
  • service

数据库建表

user

  • id bigint
  • user_account varchar(25)
  • user_password varchar(25)
  • deleted_at datetime
  • create_at datetime
  • update_at datetime

main.go

package main
​
import (
    "hertz_demo/router"
)
​
func main() {
    h := router.Router()
    h.Spin()
}

在router 包内创建app.go

package router
​
import (
    "github.com/cloudwego/hertz/pkg/app/server"
    "hertz_demo/handler"
)
​
func Router() *server.Hertz {
    h := server.Default()
    userGroup := h.Group("/user")
    {
        userGroup.POST("/login", handler.UserLogin)
        userGroup.POST("/register", handler.UserRegister)
    }
​
    return h
}

在 common 包下创建init.go

package common
​
import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "log"
)
​
var DB = Init()
​
func Init() *gorm.DB {
    dsn := "root:123456@tcp(127.0.0.1:3306)/test_gorm_hertz?charset=utf8mb4&parseTime=True&loc=Local"
    db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal(err)
    }
    return db
}

在 common 包下创建 response.go ,封装统一返回结构体,通常需要前后端沟通。

package common
​
type Response struct {
    Code int         `json:"code"`
    Msg  string      `json:"msg"`
    Data interface{} `json:"data"`
}
​
func Success(data interface{}) Response {
    return Response{
        Code: 20000,
        Msg:  "Success",
        Data: data,
    }
}
​
func Fail(code int) Response {
    return Response{
        Code: code,
        Msg:  "System error",
        Data: nil,
    }
}
​
func FailWithMsg(code int, msg string) Response {
    return Response{
        Code: code,
        Msg:  msg,
        Data: nil,
    }
}

在 models 包下创建 user.go ,作为与数据库一一对应的实体,实际业务中应该进行包装成 dto ,例如本次登录返回时就可以隐藏掉密码

package models
​
type User struct {
    UserAccount  string `gorm:"column:user_account;type:varchar(255);" json:"account"`
    UserPassword string `gorm:"column:user_password;type:varchar(255);" json:"password"`
}
​
func (table *User) TableName() string {
    return "user"
}

在 service 包下创建 userService.go ,从 java 来的习惯,不一定适用全部人

package service
​
import (
    "errors"
    "fmt"
    "hertz_demo/common"
    "hertz_demo/models"
)
​
func MatchUser(userAccount, userPassword string) (models.User, error) {
    user := models.User{}
    err := common.DB.First(&user, "user_account = ? and user_password = ?", userAccount, userPassword).Error
    return user, err
}
​
func AddUser(user models.User) error {
    tempUser := models.User{}
    common.DB.Find(&tempUser, "user_account = ?", user.UserAccount)
    if tempUser != (models.User{}) {
        return errors.New("账号重复")
    }
    err := common.DB.Model(&user).Create(user).Error
    if err != nil {
        return err
    }
    return nil
}

在 handler 包下 创建 userHandler.go

package handler
​
import (
   "context"
   "encoding/json"
   "github.com/cloudwego/hertz/pkg/app"
   "github.com/cloudwego/hertz/pkg/protocol/consts"
   "hertz_demo/common"
   "hertz_demo/models"
   "hertz_demo/service"
   "log"
)
​
func UserLogin(c context.Context, ctx *app.RequestContext) {
​
   bytes, err := ctx.Body()
   if err != nil {
      panic(err)
   }
​
   var user models.User
   if err = json.Unmarshal(bytes, &user); err != nil {
      panic(err)
   }
   result, err := service.MatchUser(user.UserAccount, user.UserPassword)
   if err != nil {
      ctx.JSON(consts.StatusOK, common.Fail(40001))
      return
   }
   ctx.JSON(consts.StatusOK, common.Success(result))
}
​
func UserRegister(c context.Context, ctx *app.RequestContext) {
   bytes, err := ctx.Body()
   if err != nil {
      ctx.JSON(consts.StatusOK, common.FailWithMsg(40001, "wrong params"))
      panic(err)
   }
   var user models.User
   err = json.Unmarshal(bytes, &user)
   if err != nil {
      ctx.JSON(consts.StatusOK, common.Fail(50000))
      panic(err)
   }
   log.Println(user)
​
   if err = service.AddUser(user); err != nil {
      ctx.JSON(consts.StatusOK, common.FailWithMsg(50000, err.Error()))
      panic(err)
   }
   ctx.JSON(consts.StatusOK, common.Success("注册成功"))
}

至此,登录注册功能已经开发完成,显然在代码实现和业务场景都比较简单,只能用作学习。