Gin+Gorm实现简单备忘录 视频链接:https://www.bilibili.com/video/BV1GT4y1R7tX
Github地址:https://github.com/CocaineCong/TodoList
自己做了一遍的Gin+Gorm备忘录项目:https://github.com/Unicorn-acc/Golang-Project/tree/main/GoProject_todolist
此项目使用Gin
+Gorm
,基于RESTful API
实现的一个备忘录 。
规范是非常重要的,此项目非常适合小白入门学习web开发
接口文档内容:
项目主要功能介绍 用户注册登录 ( jwt-go鉴权 ) 新增/删除/修改/查询 备忘录 存储每条备忘录的浏览次数 分页功能 一、项目初始化 创建项目文件的方式看gin
部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 D:\go \project>mkdir ginlearn D:\go \project>cd ginlearn D:\go \project\ginlearn>go work init D:\go \project\ginlearn>mkdir helloworld D:\go \project\ginlearn>cd helloworld D:\go \project\ginlearn\helloworld>go mod init test.com/helloworldgo : creating new go .mod: module test.com/helloworld D:\go \project\ginlearn\helloworld>cd .. D:\go \project\ginlearn>go work use ./helloworld
1 2 3 4 5 6 7 8 9 10 11 12 TodoList/ ├── api ├── cache ├── conf ├── middleware ├── model ├── pkg │ ├── e │ └── util ├── routes ├── serializer └── service
api : 用于定义接口函数 cache : 放置redis缓存 conf : 用于存储配置文件 middleware : 应用中间件 model : 应用数据库模型 pkg/e : 封装错误码 pkg/logging : 日志打印 pkg/util : 工具函数 routes : 路由逻辑处理 serializer : 将数据序列化为 json 的函数 service : 接口函数的实现 二、项目配置和数据库连接 一些依赖的导入
一、配置文件:conf/config.ini
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 [service] AppMode = debugHttpPort = :3000 [redis] RedisDb = redisRedisAddr = 127.0 .0.1 :6379 RedisPw =RedisDbName = 2 [mysql] Db = mysqlDbHost = 127.0 .0.1 DbPort = 3306 DbUser = rootDbPassWord = 123456 DbName = todolist
在conf.go
中读取配置文件到变量中,在Init()
函数中进行读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package configimport ( "gopkg.in/ini.v1" "log" "strings" "todoList.com/todoList/model" )var ( AppMode string HttpPort string RedisDb string RedisAddr string RedisPw string RedisDbName string Db string DbHost string DbPort string DbUser string DbPassWord string DbName string )func Init () { file, err := ini.Load("./config/config.ini" ) if err != nil { log.Println("配置文件读取错误,请检查文件路径" , err) } 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() }
读取到Mysql的配置文件后,调用model/init.go
进行数据库的连接
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package modelimport ( "fmt" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" "gorm.io/gorm/schema" "log" "time" )var DB *gorm.DBfunc DataBase (conn string ) { fmt.Println(conn) db, err := gorm.Open(mysql.New(mysql.Config{ DSN: conn, DefaultStringSize: 256 , DisableDatetimePrecision: true , DontSupportRenameIndex: true , DontSupportRenameColumn: true , SkipInitializeWithVersion: false , }), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), NamingStrategy: schema.NamingStrategy{ SingularTable: true , }, }) if err != nil { panic ("数据库连接错误" ) } log.Println("数据库连接成功" ) mysqldb, _ := db.DB() mysqldb.SetMaxIdleConns(20 ) mysqldb.SetMaxOpenConns(100 ) mysqldb.SetConnMaxLifetime(time.Second * 30 ) DB = db migration() }
三、数据库建表 建立两张表:User
和Task
表
1 2 3 4 5 6 7 8 9 10 package modelimport "gorm.io/gorm" type User struct { gorm.Model UserName string `gorm:"unique"` PasswordDigest string }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package modelimport "gorm.io/gorm" type Task struct { gorm.Model User User `gorm:"ForeignKey:Uid"` Uid uint `gorm:"not null"` Title string `gorm:"index;not null"` Status int `gorm:"default:0"` Content string `gorm:"type:longtext"` StartTime int64 EndTime int64 `gorm:"default:0"` }
新建magirate.go
实现自动迁移模式
1 2 3 4 5 6 7 8 9 10 11 12 13 package modelimport "log" func migration () { err := DB.Set("gorm:table_options" , "charset=utf8mb4" ). AutoMigrate(&User{}, &Task{}) if err != nil { log.Println("表迁移失败" ) return } }
然后在model.init.go
中创建完DB后进行调用迁移方法
四、用户注册与登录 依赖配置:
1 2 3 4 5 6 go get github.com/gin-contrib/sessionsgo get golang.org/x/crypto/bcryptgo get github.com/dgrijalva/jwt-go
1、用户注册功能 ①先编写全局路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package routesimport ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "todoList.com/todoList/api" )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 }
②然后再api包下建一个user.go的Controller层处理这个请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package apiimport ( "github.com/gin-gonic/gin" "todoList.com/todoList/service" )func UserRegister (ctx *gin.Context) { var userRegisterService service.UserService if err := ctx.ShouldBind(&userRegisterService); err == nil { res := userRegisterService.Register() ctx.JSON(200 , res) } else { ctx.JSON(400 , err) } }
③再service.go中实现这个被调用的方法userRegisterService.Register()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 package serviceimport ( "todoList.com/todoList/model" "todoList.com/todoList/serializer" )type UserService struct { UserName string `form:"user_name" json:"user_name" binding:"required,min=3,max=15" example:"FanOne"` Password string `form:"password" json:"password" binding:"required,min=5,max=16" example:"FanOne666"` }func (service *UserService) Register() serializer.Response { var user model.User var count int64 model.DB.Model(&model.User{}).Where("user_name = ?" , service.UserName). First(&user).Count(&count) if count == 1 { return serializer.Response{ Status: 400 , Msg: "数据库已存在该用户名!" , } } user.UserName = service.UserName if err := user.SetPassword(service.Password); err != nil { return serializer.Response{ Status: 400 , Msg: "用户名密码加密错误" + err.Error(), } } if err := model.DB.Create(&user).Error; err != nil { return serializer.Response{ Status: 400 , Msg: "数据库操作错误" + err.Error(), } } return serializer.Response{ Status: 200 , Msg: "用户注册成功!" , } }
返回类型是一个通用返回类Response
1 2 3 4 5 6 7 8 9 10 11 package serializertype Response struct { Status int `json:"status"` Data interface {} `json:"data"` Msg string `json:"msg"` Error string `json:"error"` }
中间涉及密码的加密user.SetPassword(service.Password)
:写在Model.user.go
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 func (user *User) SetPassword(password string ) error { bytes, err := bcrypt.GenerateFromPassword([]byte (password), 12 ) if err != nil { return err } user.PasswordDigest = string (bytes) return nil }func (user *User) CheckPassword(password string ) bool { err := bcrypt.CompareHashAndPassword([]byte (user.PasswordDigest), []byte (password)) return err == nil }
④最后在main.go
主函数中启动gin
服务,并用postman进行测试
1 2 3 4 5 6 7 8 9 10 11 12 13 package mainimport ( "todoList.com/todoList/config" "todoList.com/todoList/routes" )func main () { config.Init() r := routes.NewRouter() _ = r.Run(config.HttpPort) }
运行结果
2、用户登录 和上面用户注册流程一样
①完善路由
1 v1.POST("user/login" , api.UserLogin)
②api.user.go
中实现路由方法
1 2 3 4 5 6 7 8 9 10 func UserLogin (ctx *gin.Context) { var userLogin service.UserService if err := ctx.ShouldBind(&userLogin); err == nil { res := userLogin.Login() ctx.JSON(200 , res) } else { ctx.JSON(400 , err) } }
③service.user.go
中实现用户登录方法userLogin.Login()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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 err == gorm.ErrRecordNotFound { return serializer.Response{ Status: 400 , Msg: "用户不存在,请先注册" , } } return serializer.Response{ Status: 400 , Msg: "数据库错误" , } } if user.CheckPassword(service.Password) == false { return serializer.Response{ Status: 400 , Msg: "密码错误" , } } token, err := util.GenerateToken(user.ID, service.UserName, 0 ) 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: "登录成功" , } }
中间涉及token的创建和返回Response中绑定token
①token的生成与验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package utilimport ( "github.com/dgrijalva/jwt-go" "time" )var JWTsecret = []byte ("ABAB" )type Claims struct { Id uint `json:"id"` Username string `json:"username"` Authority int `json:"authority"` jwt.StandardClaims }func GenerateToken (id uint , username string , authority int ) (string , error ) { nowTime := time.Now() expireTime := nowTime.Add(24 * time.Hour) claims := Claims{ Id: id, Username: username, Authority: authority, StandardClaims: jwt.StandardClaims{ ExpiresAt: expireTime.Unix(), Issuer: "to-do-list" , }, } tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) token, err := tokenClaims.SignedString(JWTsecret) return token, err }func ParseToken (token string ) (*Claims, error ) { tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func (token *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 }
②返回结果中绑定token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package serializerimport "todoList.com/todoList/model" type User struct { ID uint `json:"id" form:"id" example:"1"` UserName string `json:"user_name" form:"user_name" example:"FanOne"` 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(), } }
运行结果
五、JWT中间件 随着前后端分离的发展,以及数据中心的建立,越来越多的公司会创建一个中心服务器,服务于各种产品线。
而这些产品线上的产品,它们可能有着各种终端设备,包括但不仅限于浏览器、桌面应用、移动端应用、平板应用、甚至智能家居
实际上,不同的产品线通常有自己的服务器,产品内部的数据一般和自己的服务器交互。
但中心服务器仍然有必要存在,因为同一家公司的产品总是会存在共享的数据,比如用户数据
这些设备与中心服务器之间会进行http通信
一般来说,中心服务器至少承担着认证和授权的功能,例如登录:各种设备发送消息到中心服务器,然后中心服务器响应一个身份令牌
当这种结构出现后,就出现一个问题:它们之间还能使用传统的cookie方式传递令牌信息吗?
其实,也是可以的,因为cookie在传输中无非是一个消息头而已,只不过浏览器对这个消息头有特殊处理罢了。
但浏览器之外的设备肯定不喜欢cookie,因为浏览器有着对cookie完善的管理机制,但是在其他设备上,就需要开发者自己手动处理了
jwt的出现就是为了解决这个问题
jwt全称Json Web Token
,强行翻译过来就是json格式的互联网令牌
它要解决的问题,就是为多种终端设备,提供统一的、安全的 令牌格式
jwt只是一个令牌格式而已,你可以把它存储到cookie,也可以存储到localstorage,没有任何限制!
同样的,对于传输,你可以使用任何传输方式来传输jwt,一般来说,我们会使用消息头来传输它
比如,当登录成功后,服务器可以给客户端响应一个jwt:
1 2 3 4 5 6 7 HTTP/1.1 200 OK ... set-cookie:token=jwt令牌 authorization:jwt令牌 ...{..., token:jwt令牌}
可以看到,jwt令牌可以出现在响应的任何一个地方,客户端和服务器自行约定即可。
当然,它也可以出现在响应的多个地方,比如为了充分利用浏览器的cookie,同时为了照顾其他设备,也可以让jwt出现在set-cookie和authorization或body中,尽管这会增加额外的传输量。
当客户端拿到令牌后,它要做的只有一件事:存储它。
你可以存储到任何位置,比如手机文件、PC文件、localstorage、cookie
当后续请求发生时,你只需要将它作为请求的一部分发送到服务器即可。
虽然jwt没有明确要求应该如何附带到请求中,但通常我们会使用如下的格式:
1 2 3 4 GET /api/resources HTTP/1.1 ...authorization : bearer jwt令牌 ...
引入JWT模块,要实现用户身份认证
① 在routes.routes.go
中进行请求的拦截
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package routesimport ( "github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" "todoList.com/todoList/api" "todoList.com/todoList/middleware" )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) authed := v1.Group("/" ) authed.Use(middleware.JWT()) { authed.POST("task" , api.CreateTask) } } return r }
②JWT()
实现的具体方法:验证Token
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package middlewareimport ( "github.com/gin-gonic/gin" "time" "todoList.com/todoList/pkg/util" )func JWT () gin.HandlerFunc { return func (c *gin.Context) { var code int token := c.GetHeader("Authorization" ) if token == "" { code = 400 } else { claim, err := util.ParseToken(token) if err != nil { code = 403 } else if time.Now().Unix() > claim.ExpiresAt { code = 401 } } if code != 200 { c.JSON(400 , gin.H{ "status" : code, "msg" : "Token解析错误" , }) c.Abort() return } c.Next() } }
六、创建备忘录(增) 创建过程和user的创建过程相同
① routers.routers.go
1 2 3 4 authed.Use(middleware.JWT()) { authed.POST("task" , api.CreateTask) }
② api.tasks.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package apiimport ( "github.com/gin-gonic/gin" "log" "todoList.com/todoList/pkg/util" "todoList.com/todoList/service" )func CreateTask (c *gin.Context) { var createTaskservice service.CreateTaskService claim, _ := util.ParseToken(c.GetHeader("Authorization" )) if err := c.ShouldBind(&createTaskservice); err == nil { res := createTaskservice.Create(claim.Id) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("createTask err : " , err) } }
③service.task.go
创建一个接收参数的服务类,然后实现Create方法
这里犯了一个错误: 在给结构体属性加tag
的时候json:"title"
的:
中间不能有空格,也就是tag
部分不能是灰色的,不然属性就赋值不上,而且err
还是为nil
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package serviceimport ( "time" "todoList.com/todoList/model" "todoList.com/todoList/serializer" )type CreateTaskService struct { Title string `json:"title" form:"title"` Content string `json:"content" form:"content"` Status int `json:"status" form:"status"` }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, Content: service.Content, Status: 0 , StartTime: time.Now().Unix(), } code := 200 err := model.DB.Create(&task).Error if err != nil { code = 500 return serializer.Response{ Status: code, Msg: "创建备忘录失败" , Error: err.Error(), } } return serializer.Response{ Status: code, Data: serializer.BuildTask(task), Msg: "创建备忘录成功" , } }
运行结果
请求头中要设置Authorization:{{token}}
,其中token
是postman
中设置的环境变量,是用户登录成功后返回给浏览器的token
错误的结果:title
和content
没有内容,gin
框架没有成功对结构体进行赋值
七、展示一条备忘录(查) ① 建立路由:routes.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 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) authed := v1.Group("/" ) authed.Use(middleware.JWT()) { authed.POST("task" , api.CreateTask) authed.GET("task/:id" , api.GetTaskById) } } return r }
②实现处理路由的方法 api.tasks.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func GetTaskById (c *gin.Context) { var GetTaskservice service.ShowTaskService claim, _ := util.ParseToken(c.GetHeader("Authorization" )) if err := c.ShouldBind(&GetTaskservice); err == nil { res := GetTaskservice.GetById(claim.Id, c.Param("id" )) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("createTask err : " , err) } }
③实现一个GetTaskservice
服务类,然后定义该类的方法,该类中没有任何属性(因为是get
请求)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 type ShowTaskService struct { }func (service *ShowTaskService) GetById(uid uint , 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: "查询备忘录失败" , } } if task.Uid != uid { code = 500 return serializer.Response{ Status: code, Msg: "未查询到当前用户的备忘录信息" , } } return serializer.Response{ Status: code, Data: serializer.BuildTask(task), Msg: "查询成功" , } }
涉及到一个序列化 Data: serializer.BuildTask(task),
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package serializerimport "todoList.com/todoList/model" type Task struct { ID uint `json:"id" example:"1"` Title string `json:"title" example:"吃饭"` Content string `json:"content" example:"睡觉"` Status int `json:"status" example:"0"` CreatedAt int64 `json:"created_at"` StartTime int64 `json:"start_time"` EndTime int64 `json:"end_time"` }func BuildTask (item model.Task) Task { return Task{ ID: item.ID, Title: item.Title, Content: item.Content, Status: item.Status, CreatedAt: item.CreatedAt.Unix(), StartTime: item.StartTime, EndTime: item.EndTime, } }
八、展示所有的备忘录(查) ①routes.go
写路由
1 authed.GET("tasks" , api.GetAllTaskById)
② api.tasks.go
写处理函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 func GetAllTaskById (c *gin.Context) { var getListTaskService service.GetListTaskService claim, _ := util.ParseToken(c.GetHeader("Authorization" )) if err := c.ShouldBind(&getListTaskService); err == nil { res := getListTaskService.GetAllById(claim.Id) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("GetAllTaskById err : " , err) } }
③ service.task.go 实现查询服务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type GetListTaskService struct { PageNum int `json:"page_num" form:"page_num"` PageSize int `json:"page_size" form:"page_size"` }func (service *GetListTaskService) GetAllById(uid uint ) serializer.Response { var tasks []model.Task var count int64 = 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)) }
涉及到带数量的列表响应
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func BuildListResponse (items interface {}, total uint ) Response { return Response{ Status: 200 , Data: DataList{ Item: items, Total: total, }, Msg: "ok" , } }func BuildTasks (items []model.Task) (tasks []Task) { for _, item := range items { task := BuildTask(item) tasks = append (tasks, task) } return tasks }
运行结果
九、更新备忘录(改) 和前面相同的步骤
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 authed.PUT("task/:id" , api.UpdateTaskById)func UpdateTaskById (c *gin.Context) { var updateTaskservice service.UpdateTaskService if err := c.ShouldBind(&updateTaskservice); err == nil { res := updateTaskservice.UpdateById(c.Param("id" )) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("createTask err : " , err) } }type UpdateTaskService struct { Title string `json:"title" form:"title"` Content string `json:"content" form:"content"` Status int `json:"status" form:"status"` }func (service *UpdateTaskService) UpdateById(id string ) serializer.Response { var task model.Task model.DB.First(&task, id) 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: 400 , Msg: "更新失败" , } } return serializer.Response{ Status: 200 , Msg: "更新成功" , } }
执行结果:
十、查询备忘录(查) 和查询单条备忘录差不多,主要这便是模糊查询
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 authed.POST("search" , api.SearchTask)func SearchTask (c *gin.Context) { var searchTaskservice service.SearchTaskService claim, _ := util.ParseToken(c.GetHeader("Authorization" )) if err := c.ShouldBind(&searchTaskservice); err == nil { res := searchTaskservice.Search(claim.Id) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("SearchTask err : " , err) } }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(id uint ) serializer.Response { if service.PageSize == 0 { service.PageSize = 15 } var tasks []model.Task var count int64 model.DB.Model(&model.Task{}).Preload("User" ).Where("uid = ?" , id). 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)) }
执行结果:
十一、删除备忘录(删) 和前面一样
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 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) authed := v1.Group("/" ) authed.Use(middleware.JWT()) { authed.POST("task" , api.CreateTask) authed.GET("task/:id" , api.GetTaskById) authed.GET("tasks" , api.GetAllTaskById) authed.PUT("task/:id" , api.UpdateTaskById) authed.POST("search" , api.SearchTask) authed.DELETE("task/:id" , api.DeleteTask) } } return r }func DeleteTask (c *gin.Context) { var deleteTaskservice service.DeleteTaskService if err := c.ShouldBind(&deleteTaskservice); err == nil { res := deleteTaskservice.Delete(c.Param("id" )) c.JSON(200 , res) } else { c.JSON(400 , err) log.Println("DeleteTask err : " , err) } }func (service *DeleteTaskService) Delete(id string ) serializer.Response { var task model.Task err := model.DB.Delete(&task, id).Error if err != nil { return serializer.Response{ Status: 400 , Msg: "删除失败" , } } return serializer.Response{ Status: 200 , Msg: "删除成功" , } }