faker-douyin-4. 用户注册功能实现

188 阅读4分钟

1. 定义用户实体类

/model目录下新建entity目录,新建user.go文件夹,添加用户实体类TableUser

package entity

import "gorm.io/gorm"

type TableUser struct {
   gorm.Model
   Name     string
   Password string
}

// TableName 修改映射的表名为users
func (tableUser TableUser) TableName() string {
   return "users"
}

2. 迁移TableUser表结构

model/dao/initDao.go中调用AutoMigrate函数

err = Db.AutoMigrate(&entity.TableUser{})
if err != nil {
   panic(fmt.Sprintf("数据库迁移失败,%s", err))
}

3. 新增用户实体类的dao方法

model/dao/目录下新建userDao.go文件

package dao

import (
   "faker-douyin/model/entity"
   "log"
)

// GetTableUserList 获取所有用户
func GetTableUserList() ([]entity.TableUser, error) {
   var tableUsers []entity.TableUser
   if err := Db.Find(&tableUsers).Error; err != nil {
      log.Println(err.Error())
      return tableUsers, err
   }
   return tableUsers, nil
}

// GetTableUserByName 根据用户名获取第一个用户
func GetTableUserByName(name string) (entity.TableUser, error) {
   var user entity.TableUser
   if err := Db.Where("name = ?", name).First(&user).Error; err != nil {
      log.Println(err.Error())
      return user, err
   }
   return user, nil
}

// GetTableUserById 根据id获取第一个用户
func GetTableUserById(id uint64) (entity.TableUser, error) {
   var user entity.TableUser
   if err := Db.Where("id = ?", id).First(&user).Error; err != nil {
      log.Println(err.Error())
      return user, err
   }
   return user, nil
}

// InsertTableUser 将tableUser插入表内
func InsertTableUser(tableUser *entity.TableUser) bool {
   if err := Db.Create(tableUser).Error; err != nil {
      log.Println(err.Error())
      return false
   }
   return true
}

4. 新增user的service层代码

service目录下新增userService.go文件,定义userService接口

package service

import (
   "faker-douyin/model/entity"
)

type UserService interface {
   /*
      个人使用
   */
   // GetTableUserList 获得全部TableUser对象
   GetTableUserList() []entity.TableUser

   // GetTableUserByUsername 根据username获得TableUser对象
   GetTableUserByUsername(name string) entity.TableUser

   // GetTableUserById 根据user_id获得TableUser对象
   GetTableUserById(id uint64) entity.TableUser

   // InsertTableUser 将tableUser插入表内
   InsertTableUser(tableUser *entity.TableUser) bool
}

再在该目录下新增userServiceImpl.go文件,编写userService实现类

package service

import "C"
import (
   "faker-douyin/global"
   "faker-douyin/model/dao"
   "faker-douyin/model/entity"
   "fmt"
   "log"
)

type UserServiceImpl struct {
}

func (u UserServiceImpl) GetTableUserList() []entity.TableUser {
   tableUsers, err := dao.GetTableUserList()
   if err != nil {
      log.Println(err.Error())
      return tableUsers
   }
   return tableUsers
}

func (u UserServiceImpl) GetTableUserByUsername(name string) entity.TableUser {
   tableUser, err := dao.GetTableUserByName(name)
   if err != nil {
      log.Println(err.Error())
      return tableUser
   }
   return tableUser
}

func (u UserServiceImpl) GetTableUserById(id uint64) entity.TableUser {
   tableUser, err := dao.GetTableUserById(id)
   if err != nil {
      log.Println(err.Error())
      return tableUser
   }
   return tableUser
}

func (u UserServiceImpl) InsertTableUser(tableUser *entity.TableUser) bool {
   result := dao.InsertTableUser(tableUser)
   if result == false {
      log.Println("插入失败")
      return false
   }
   return true
}

5. 新增生成token和加密密码的工具

为了完成用户注册,还需要密码加密工具,同时现在app一般注册后同时也跳转登陆了,需要生成tokenutils/目录下新增md5.gojwt.go

package utils

import (
   "crypto/hmac"
   "crypto/sha256"
   "encoding/hex"
   "fmt"
)

// EnCoder 密码加密
func EnCoder(password string) string {
   h := hmac.New(sha256.New, []byte(password))
   sha := hex.EncodeToString(h.Sum(nil))
   fmt.Println("Result: " + sha)
   return sha
}

Json Web Token是一种跨域身份验证的解决方案,目前系统较简单,直接使用jwt.StandardClaims存储用户信息

package utils

import (
   "faker-douyin/global"
   "faker-douyin/service"
   "fmt"
   "github.com/dgrijalva/jwt-go"
   "strconv"
   "time"
)

// GenerateToken 根据username生成一个token
func GenerateToken(user *entity.TableUser) string {
   //fmt.Printf("generate token: %v\n", u)
   expiresTime := time.Now().Unix() + int64(global.OneDayOfHours)
   id64 := user.ID
   fmt.Printf("id: %v\n", strconv.FormatInt(int64(id64), 10))
   claims := jwt.StandardClaims{
      Audience:  user.Name,
      ExpiresAt: expiresTime,
      Id:        strconv.FormatInt(int64(id64), 10),
      IssuedAt:  time.Now().Unix(),
      Issuer:    "tiktok",
      NotBefore: time.Now().Unix(),
      Subject:   "token",
   }
   var jwtSecret = []byte(global.Config.Jwt.Secret)
   tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
   if token, err := tokenClaims.SignedString(jwtSecret); err == nil {
      println("generate token success!\n")
      return token
   } else {
      println("generate token fail\n")
      return "fail"
   }
}

6. 定义控制器层的输入

接受前端输入,进行参数绑定和校验。在model/目录下新建dto目录,在model/dto目录下新建requestresponse目录,在model/dao/request目录下新建user.go文件

package request

type UserRegisterReq struct {
   Name     string `json:"name,omitempty" binding:"required"`
   Password string `json:"password,omitempty" binding:"required"`
}

model/dao/response目录下新建user.go文件

package response

type UserLoginSuccessRes struct {
   Id    uint64 `json:"id"`
   Token string `json:"token"`
}

定义常用输出,减少代码重复度,这里直接copy开源项目gin-vue-admin的代码,是一个非常优秀的Go+Vue前后端分离的项目,有兴趣的可以学习下gin-vue-admin。 在model/目录下新增common目录,定义常用输入输出,在model/common目录下新增request.goresponse.go文件

package common

import (
   "net/http"

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

type Response struct {
   Code int         `json:"code"`
   Data interface{} `json:"data"`
   Msg  string      `json:"msg"`
}

const (
   ERROR   = 7
   SUCCESS = 0
)

func Result(code int, data interface{}, msg string, c *gin.Context) {
   // 开始时间
   c.JSON(http.StatusOK, Response{
      code,
      data,
      msg,
   })
}

func Ok(c *gin.Context) {
   Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
}

func OkWithMessage(message string, c *gin.Context) {
   Result(SUCCESS, map[string]interface{}{}, message, c)
}

func OkWithData(data interface{}, c *gin.Context) {
   Result(SUCCESS, data, "查询成功", c)
}

func OkWithDetailed(data interface{}, message string, c *gin.Context) {
   Result(SUCCESS, data, message, c)
}

func Fail(c *gin.Context) {
   Result(ERROR, map[string]interface{}{}, "操作失败", c)
}

func FailWithMessage(message string, c *gin.Context) {
   Result(ERROR, map[string]interface{}{}, message, c)
}

func FailWithDetailed(data interface{}, message string, c *gin.Context) {
   Result(ERROR, data, message, c)
}

7. 编写contrller层业务逻辑代码

api目录下新建v1目录,在api/v1目录下新增userController.go文件

package v1

import (
   "faker-douyin/model/common"
   "faker-douyin/model/dto/request"
   "faker-douyin/model/dto/response"
   "faker-douyin/model/entity"
   "faker-douyin/service"
   "faker-douyin/utils"
   "github.com/gin-gonic/gin"
   "log"
)

// Register POST douyin/user/register/ 用户注册
func Register(c *gin.Context) {
   var userRegisterReq request.UserRegisterReq
   if err := c.ShouldBindJSON(&userRegisterReq); err != nil {
      common.FailWithMessage(err.Error(), c)
      return
   }
   usi := service.UserServiceImpl{}
   u := usi.GetTableUserByUsername(userRegisterReq.Name)
   if u.Name != "" {
      common.FailWithMessage("User already exist", c)
   } else {
      newUser := entity.TableUser{
         Name:     userRegisterReq.Name,
         Password: utils.EnCoder(userRegisterReq.Password),
      }
      if usi.InsertTableUser(&newUser) != true {
         println("Insert Data Fail")
      }
      u := usi.GetTableUserByUsername(userRegisterReq.Name)
      token := utils.GenerateToken(&u)
      log.Println("注册返回的id: ", u.ID)
      common.OkWithData(response.UserLoginSuccessRes{Id: uint64(u.ID), Token: token}, c)
   }
}

8. 定义路由映射

router目录下新建router.go文件,进行路由分组v1,注册路由

package router

import (
   v1 "faker-douyin/api/v1"
   "github.com/gin-gonic/gin"
)

func InitRouter(r *gin.Engine) {
   apiRouter := r.Group("/douyin/v1")
   apiRouter.POST("/user/register/", v1.Register)
}

9. 测试

修改main.go文件,注册路由,编译运行项目,用postman测试

package main

import (
   "faker-douyin/global"
   "faker-douyin/model/dao"
   "faker-douyin/router"
   "github.com/gin-gonic/gin"
)

func main() {
   global.LoadConfig()
   dao.Init()
   gin.SetMode(global.Config.Server.AppMode)
   r := gin.Default()
   router.InitRouter(r)
   r.Run(global.Config.Server.HttpPort)
}

缺少字段或者字段值为空

WechatIMG51.jpeg 用户名已存在

WechatIMG52.jpeg 注册成功,返回idtoken

WechatIMG53.jpeg