# Gin+Gorm实现简单备忘录 **Repository Path**: sadu_tqx/gin_gorm ## Basic Information - **Project Name**: Gin+Gorm实现简单备忘录 - **Description**: Gin+Gorm实现简单备忘录,使用golang实现。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2022-09-29 - **Last Updated**: 2023-11-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Gin+Gorm实现简单备忘录 ## 1 连接数据库 ### 1.1 编写配置文件("\conf\config.ini") ```ini [service] AppMode = debug HttpPort = :3000 [mysql] Db = mysql DBHost = 127.0.0.1 DbPort = 3306 DbUser = root DbPassWord = 123456 DbName = gin_gorm ``` ### 1.2 加载配置文件("\conf\conf.go") ```go // go get gopkg.in/ini.v1 安装依赖 package conf import ( "fmt" "gin_gorm/model" "strings" "gopkg.in/ini.v1" ) var ( AppMode string HttpPort string Db string DbHost string DbPort string DbUser string DbPassWord string DbName string ) func Init() { file, err := ini.Load("./conf/config.ini") if err != nil { fmt.Println("配置文件读取错误,请检查文件路径") } LoadServer(file) // 加载服务器配置 LoadMysql(file) // 加载数据库配置 path := strings.Join([]string{DbUser, ":", DbPassWord, "@tcp(", DbHost, ":", DbPort, ")/", DbName, "?charset=utf8mb4&parseTime=true"}, "") model.Database(path) // 连接数据库 } func LoadServer(file *ini.File) { AppMode = file.Section("service").Key("AppMode").String() HttpPort = file.Section("service").Key("HttpPort").String() } func LoadMysql(file *ini.File) { Db = file.Section("mysql").Key("Db").String() DbHost = file.Section("mysql").Key("DbHost").String() DbPort = file.Section("mysql").Key("DbPort").String() DbUser = file.Section("mysql").Key("DbUser").String() DbPassWord = file.Section("mysql").Key("DbPassWord").String() DbName = file.Section("mysql").Key("DbName").String() } ``` ### 1.3 连接数据库("\model\init.go") ```python package model import ( "fmt" "log" "time" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) var DB *gorm.DB // 连接数据库 func Database(connstring string) { fmt.Println(connstring) db, err := gorm.Open("mysql", connstring) if err != nil { log.Panic("mysql数据库连接错误") } fmt.Println("数据库连接成功") db.LogMode(true) // go get github.com/gin-gonic/gin if gin.Mode() == "release" { db.LogMode(false) // 发行版不用输出日志 } db.SingularTable(true) // 表明不加S db.DB().SetMaxIdleConns(20) // 设置连接池 db.DB().SetMaxOpenConns(100) // 设置最大连接数 db.DB().SetConnMaxLifetime(time.Second * 30) // 超时时间 DB = db } ``` ### 1.4 连接("/main.go") ```go package main import "gin_gorm/conf" func main() { conf.Init() } ``` ## 2 数据库建表 ### 2.1 建立用户表("\model\user.go") ```go package model import "github.com/jinzhu/gorm" type User struct { gorm.Model UserName string `gorm:"unique"` PasswordDigest string // 存储密文,也就是加密后的密码 } ``` ### 2.2 建立备忘录表("model\task.go") ```go package model import "github.com/jinzhu/gorm" type Task struct { gorm.Model User User `gorm:"ForeiKey:Uid"` Uid uint `gorm:"not null"` Title string `gorm:"index;not null"` Status int `gorm:"default:'0'"` // 0 未完成 1 已完成 Content string `gorm:"type:longtext"` StartTime int64 // 备忘录开始时间 EndTime int64 // 备忘录完成时间 } ``` ### 2.3 自动迁移("model\migrate.go") ```go package model func migration() { // 自动迁移模式 DB.Set("gorm:gin_gorm", "charset=utf8mb4"). AutoMigrate(&User{}).AutoMigrate(&Task{}) DB.Model(&Task{}).AddForeignKey("uid", "User(id)", "CASCADE", "CASCADE") // 增加外键 } ``` ### 2.4 执行迁移("model\init.go") ```go package model import ( "fmt" "log" "time" "github.com/gin-gonic/gin" "github.com/jinzhu/gorm" _ "github.com/jinzhu/gorm/dialects/mysql" ) var DB *gorm.DB // 连接数据库 func Database(connstring string) { fmt.Println(connstring) db, err := gorm.Open("mysql", connstring) if err != nil { log.Panic("mysql数据库连接错误") } fmt.Println("数据库连接成功") db.LogMode(true) // go get github.com/gin-gonic/gin if gin.Mode() == "release" { db.LogMode(false) // 发行版不用输出日志 } db.SingularTable(true) // 表明不加S db.DB().SetMaxIdleConns(20) // 设置连接池 db.DB().SetMaxOpenConns(100) // 设置最大连接数 db.DB().SetConnMaxLifetime(time.Second * 30) // 超时时间 DB = db migration() // 数据库迁移 } ``` ## 3 用户注册 ### 3.1 配置注册路由("routers\routes.go") ```go package routers import ( "gin_gorm/api" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" //go get github.com/gin-contrib/sessions ) func NewRouter() *gin.Engine { r := gin.Default() store := cookie.NewStore([]byte("something-very-secret")) r.Use(sessions.Sessions("mysession", store)) v1 := r.Group("api/v1") { // 用户操作 v1.POST("user/register", api.UserRegister) } return r } ``` ### 3.2 定义注册操作("api\user.go") ```go package api import ( "gin_gorm/service" "net/http" "github.com/gin-gonic/gin" ) func UserRegister(ctx *gin.Context) { var userRegister service.UserService // 声明user服务对象,绑定获取数据 if err := ctx.ShouldBind(&userRegister); err == nil { res := userRegister.Register() // 执行注册方法 ctx.JSON(http.StatusOK, res) } else { ctx.JSON(400, err) } } ``` ### 3.3 注册接口及方法("service\user.go") ```go package service import ( "gin_gorm/model" "gin_gorm/serializer" "net/http" ) type UserService struct { UserName string `form:"user_name" json:"user_name" binding:"required,min=3,max=15" example:"tangshao"` Password string `form:"password" json:"password" binding:"required,min=5,max=16" example:"tangshao"` } func (service *UserService) Register() serializer.Response { var user model.User var count int model.DB.Model(&model.User{}).Where("user_name=?", service.UserName).First(&user).Count(&count) if count != 0 { return serializer.Response{ Status: http.StatusBadGateway, Msg: "用户已经存在", } } user.UserName = service.UserName // 加密 if err := user.SetPassword(service.Password); err != nil { return serializer.Response{ Status: http.StatusBadGateway, Msg: err.Error(), } } // 创建用户 if err := model.DB.Create(&user).Error; err != nil { return serializer.Response{ Status: 500, Msg: "数据库操作错误", } } return serializer.Response{ Status: http.StatusOK, Msg: "用户注册成功", } } ``` ### 3.4 数据序列化接口("serializer\common.go") ```go package serializer // 基础的序列化器 type Response struct { Status int `json:"status"` Data interface{} `json:"data"` Msg string `json:"msg"` Error string `json:"error"` } ``` ### 3.5 启动服务("main.go") ```go package main import ( "gin_gorm/conf" "gin_gorm/routers" ) func main() { conf.Init() r := routers.NewRouter() r.Run(conf.HttpPort) } ``` ## 4 用户登录 ### 4.1 配置注册路由("routers\routes.go") ```go package routers import ( "gin_gorm/api" "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" //go get github.com/gin-contrib/sessions ) func NewRouter() *gin.Engine { r := gin.Default() store := cookie.NewStore([]byte("something-very-secret")) r.Use(sessions.Sessions("mysession", store)) v1 := r.Group("api/v1") { // 用户操作 v1.POST("user/register", api.UserRegister) // 用户登录 v1.POST("user/login",api.UserLogin) } return r } ``` ### 4.2 定义注册操作("api\user.go") ```go func UserLogin(ctx *gin.Context) { var userLogin service.UserService if err := ctx.ShouldBind(&userLogin); err == nil { res := userLogin.Login() ctx.JSON(http.StatusOK, res) } else { ctx.JSON(400, err) } } ``` ### 4.3 注册接口及方法("service\user.go") ```go func (service *UserService) Login() serializer.Response { var user model.User // 先去数据库找一下有没有这个人 if err := model.DB.Where("user_name=?", service.UserName).First(&user).Error; err != nil { if gorm.IsRecordNotFoundError(err) { return serializer.Response{ Status: 400, Msg: "用户不存在,请先登录", } } //如果不是用户不存在,其他因素导致的 return serializer.Response{ Status: 500, Msg: "数据库错误", } } if !user.CheckPassword(service.Password) { return serializer.Response{ Status: 400, Msg: "密码错误", } } // 发一个token,为了其他要身份验证所给前端存储的 // 创建一个备忘录,这个功能就要token,不然都不知道谁创建的备忘录 token, err := utils.GenerateToken(user.ID, service.UserName, service.Password) if err != nil { return serializer.Response{ Status: 500, Msg: "token签发错误", } } return serializer.Response{ Status: 200, Data: serializer.TokenData{ User: serializer.BuildUser(user), Token: token, }, Msg: "登录成功", } } ``` ### 4.4 数据序列化接口("serializer\user.go") ```go package serializer import "gin_gorm/model" type User struct { ID uint `json:"id" form:"id" example:"1"` // 用户id UserName string `json:"user_name" form:"user_name" example:"tangshao"` // 用户名 Status string `json:"status" form:"status"` // 用户状态 CreateAt int64 `json:"create_at" form:"create_at"` // 创建时间 } func BuildUser(user model.User) User { return User{ ID: user.ID, UserName: user.UserName, CreateAt: user.CreatedAt.Unix(), } } ``` ### 4.5 jwt发放token和解析token("pkg\utils\utils.go") ```go package utils import ( "fmt" "time" "github.com/dgrijalva/jwt-go" ) // go get github.com/dgrijalva/jwt-go var JWTsecret = []byte("ABAB") type Claims struct { Id uint `json:"id"` UserName string `json:"user_name"` Password string `json:"password"` jwt.StandardClaims } // 签发token func GenerateToken(id uint, username, password string) (string, error) { notTime := time.Now() expireTime := notTime.Add(24 * time.Hour) claims := Claims{ Id: id, UserName: username, Password: password, StandardClaims: jwt.StandardClaims{ ExpiresAt: expireTime.Unix(), Issuer: "to-do-list", }, } tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token, err := tokenClaims.SignedString(JWTsecret) fmt.Println(err) return token, err } // 验证token func ParseToken(token string) (*Claims, error) { tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(t *jwt.Token) (interface{}, error) { return JWTsecret, nil }) if tokenClaims != nil { if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid { return claims, nil } } return nil, err } ``` ## 5 jwt中间件 ### 5.1 创建用户登录的路由("routers\routes.go") ```go func NewRouter() *gin.Engine { r := gin.Default() store := cookie.NewStore([]byte("something-very-secret")) r.Use(sessions.Sessions("mysession", store)) v1 := r.Group("api/v1") { // 用户操作 v1.POST("user/register", api.UserRegister) // 用户登录 v1.POST("user/login", api.UserLogin) // jwt中间件认证分组 authed := v1.Group("/") authed.Use(middleware.JWT()) { } } return r } ``` ### 5.2 创建中间件("middleware\jwt.go") ```go package middleware import ( "gin_gorm/pkg/utils" "time" "github.com/gin-gonic/gin" ) func JWT() gin.HandlerFunc { return func(ctx *gin.Context) { code := 200 token := ctx.GetHeader("Authorization") if token == "" { code = 404 // 请求错误 } else { claim, err := utils.ParseToken(token) if err != nil { code = 403 // 无权限 } else if time.Now().Unix() > claim.ExpiresAt { code = 401 //过期 } } if code != 200 { ctx.JSON(200, gin.H{ "status": code, "msg": "Token解析错误", }) ctx.Abort() return } ctx.Next() } } ``` ## 6 创建备忘录 ### 6.1 配置备忘录路由("routers\routes.go") ```go func NewRouter() *gin.Engine { r := gin.Default() store := cookie.NewStore([]byte("something-very-secret")) r.Use(sessions.Sessions("mysession", store)) v1 := r.Group("api/v1") { // 用户操作 v1.POST("user/register", api.UserRegister) // 用户登录 v1.POST("user/login", api.UserLogin) // jwt中间件认证分组 authed := v1.Group("/") authed.Use(middleware.JWT()) { authed.POST("task", api.CreateTask) } } return r } ``` ### 6.2 定义创建备忘录操作("api\tasks.go") ```go package api import ( "gin_gorm/pkg/utils" "gin_gorm/service" "github.com/gin-gonic/gin" logging "github.com/sirupsen/logrus" ) func CreateTask(ctx *gin.Context) { var createTask service.CreateTaskService claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&createTask); err == nil { res := createTask.Create(claim.Id) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 6.3 创建备忘录的服务("service\task.go") ```go package service import ( "gin_gorm/model" "gin_gorm/serializer" "time" ) type CreateTaskService struct { Title string `json:"title" form:"title"` Content string `json:"content" form:"content"` Status int `json:"status" form:"status"` // 0是未做,1是已做 } func (service *CreateTaskService) Create(id uint) serializer.Response { var user model.User model.DB.First(&user, id) task := model.Task{ User: user, Uid: user.ID, Title: service.Title, Status: 0, Content: service.Content, StartTime: time.Now().Unix(), EndTime: 0, } err := model.DB.Create(&task).Error code := 200 if err != nil { code = 500 return serializer.Response{ Status: code, Msg: "创建备忘录失败", } } return serializer.Response{ Status: code, Msg: "创建成功", } } ``` ### 6.4 补充错误的序列化("api\main.go") ```go package api import ( "encoding/json" "fmt" "gin_gorm/serializer" ) // 返回错误信息ErrorResponse func ErrorResponse(err error) serializer.Response { if _, ok := err.(*json.UnmarshalTypeError); ok { return serializer.Response{ Status: 40001, Msg: "Json类型不匹配", Error: fmt.Sprint(err), } } return serializer.Response{ Status: 40002, Msg: "参数错误", Error: fmt.Sprint(err), } } ``` ## 7 显示一条备忘录 ### 7.1 添加路由("routers\routes.go") ```go func NewRouter() *gin.Engine { r := gin.Default() store := cookie.NewStore([]byte("something-very-secret")) r.Use(sessions.Sessions("mysession", store)) v1 := r.Group("api/v1") { // 用户操作 v1.POST("user/register", api.UserRegister) // 用户登录 v1.POST("user/login", api.UserLogin) // jwt中间件认证分组 authed := v1.Group("/") authed.Use(middleware.JWT()) { authed.POST("task", api.CreateTask) // 创建备忘录 authed.GET("task/:id", api.ShowTask) // 展示一条备忘录 } } return r } ``` ### 7.2 显示备忘录的操作("api\tasks.go") ```go // 展示一条备忘录 func ShowTask(ctx *gin.Context) { var showTask service.ShowTaskService // claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&showTask); err == nil { res := showTask.Show(ctx.Param("id")) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 7.3 显示一条备忘录的服务("service\task.go") ```go type ShowTaskService struct { } func (service *ShowTaskService) Show(tid string) serializer.Response { var task model.Task code := 200 err := model.DB.First(&task, tid).Error if err != nil { code = 500 return serializer.Response{ Status: code, Msg: "查询失败", } } return serializer.Response{ Status: code, Data: serializer.BuildTask(task), } } ``` ### 7.4 备忘录序列化("serializer\task.go") ```go package serializer import ( "gin_gorm/model" ) type Task struct { ID uint `json:"id"` Title string `json:"title"` Status int `json:"status"` Content string `json:"content"` View uint64 `json:"view"` StartTime int64 `json:"start_time"` EndTime int64 `json:"end_time"` CreateAt int64 `json:"create_at"` } func BuildTask(item model.Task) Task { return Task{ ID: item.ID, Title: item.Title, Status: item.Status, Content: item.Content, StartTime: item.StartTime, EndTime: item.EndTime, CreateAt: item.CreatedAt.Unix(), } } ``` ## 8 展示所有的备忘录 ### 8.1 配置路由 ```go authed.GET("tasks",api.ListTask) // 展示所有的备忘录 ``` ### 8.2 显示所有备忘录的操作("api\tasks.go") ```go // 展示所有的备忘录 func ListTask(ctx *gin.Context) { var listTask service.ListaskService claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&listTask); err == nil { res := listTask.List(claim.Id) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 8.3 显示所有备忘录的服务("") ```go type ListaskService struct { PageNum int `json:"page_num" form:"page_num"` PageSize int `json:"page_size" form:"page_size"` } // 显示所有备忘录 func (service *ListaskService) List(uid uint) serializer.Response { var tasks []model.Task count := 0 if service.PageSize == 0 { service.PageSize = 15 } model.DB.Model(&model.Task{}).Preload("User").Where("uid=?", uid).Count(&count). Limit(service.PageSize).Offset((service.PageNum - 1) * service.PageSize).Find(&tasks) return serializer.BuildListResponse(serializer.BuildTasks(tasks), uint(count)) } ``` ### 8.4 序列化("") ```go func BuildTasks(items []model.Task) (tasks []Task) { for _, item := range items { task := BuildTask(item) tasks = append(tasks, task) } return tasks } //DataList 带有总数的Data结构 type DataList struct { Item interface{} `json:"item"` Total uint `json:"total"` } //BulidListResponse 带有总数的列表构建器 func BuildListResponse(items interface{}, total uint) Response { return Response{ Status: 200, Data: DataList{ Item: items, Total: total, }, Msg: "ok", } } ``` ## 9 更新一条备忘录 ### 9.1 配置路由 ```go authed.PUT("task/:id", api.UpdateTask) // 更新一条备忘录 ``` ### 9.2 更新备忘录的操作("api\tasks.go") ```go // 更新一条备忘录 func UpdateTask(ctx *gin.Context) { var updateTask service.UpdateTaskService // claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&updateTask); err == nil { res := updateTask.Update(ctx.Param("id")) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 9.3 更新备忘录的服务("service\task.go") ```go type UpdateTaskService struct { Title string `json:"title" form:"title"` Content string `json:"content" form:"content"` Status int `json:"status" form:"status"` // 0是未做,1是已做 } // 更新一条备忘录 func (service *UpdateTaskService) Update(tid string) serializer.Response { var task model.Task model.DB.First(&task, tid) task.Content = service.Content task.Title = service.Title task.Status = service.Status err := model.DB.Save(&task).Error if err != nil { return serializer.Response{ Status: 500, Msg: "数据保存出错", } } return serializer.Response{ Status: 200, Data: serializer.BuildTask(task), Msg: "更新完成", } } ``` ## 10 模糊查询 ### 10.1 配置路由 ```go authed.POST("search", api.SearchTask) // 寻找一条备忘录 ``` ### 10.2 查询操作("api\tasks.go") ```go // 寻找一条备忘录 func SearchTask(ctx *gin.Context) { var searchTask service.SearchTaskService claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&searchTask); err == nil { res := searchTask.Search(claim.Id) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 10.3 配置查询服务("service\task.go") ```go type SearchTaskService struct { Info string `json:"info" form:"info"` PageNum int `json:"page_num" form:"page_num"` PageSize int `json:"page_size" form:"page_size"` } // 查找备忘录 func (service *SearchTaskService) Search(uid uint) serializer.Response { var tasks []model.Task count := 0 if service.PageSize == 0 { service.PageSize = 15 } model.DB.Model(&model.Task{}).Preload("User").Where("uid=?", uid). Where("title LIKE ? OR content LIKE ?", "%"+service.Info+"%", "%"+service.Info+"%"). Count(&count). Limit(service.PageSize).Offset((service.PageNum - 1) * service.PageSize).Find(&tasks) return serializer.BuildListResponse(serializer.BuildTasks(tasks), uint(count)) } ``` ## 11 删除一条备忘录 ### 11.1 配置路由 ```go authed.POST("task/:id", api.DeleteTask) // 删除一条备忘录 ``` ### 11.2 删除操作("api\tasks.go") ```go // 删除一条备忘录 func DeleteTask(ctx *gin.Context) { var deleteTask service.DeleteTaskService // claim, _ := utils.ParseToken(ctx.GetHeader("Authorization")) if err := ctx.ShouldBind(&deleteTask); err == nil { res := deleteTask.Delete(ctx.Param("id")) ctx.JSON(200, res) } else { logging.Error(err) // go get github.com/sirupsen/logrus ctx.JSON(400, ErrorResponse(err)) } } ``` ### 11.3 删除服务 ```go type DeleteTaskService struct { } // 删除一条备忘录 func (service *DeleteTaskService) Delete(tid string) serializer.Response { var task model.Task err := model.DB.Delete(&task, tid).Error if err != nil { return serializer.Response{ Status: 500, Msg: "删除失败", } } return serializer.Response{ Status: 200, Msg: "删除成功", } } ```