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一般注册后同时也跳转登陆了,需要生成token
在utils/目录下新增md5.go和jwt.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目录下新建request和response目录,在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.go和response.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)
}
缺少字段或者字段值为空
用户名已存在
注册成功,返回
id和token