package main import ( "fmt" "net/http" "os" "strconv" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" ) type BookSet struct { // 总共多少个 Total int64 `json:"total"` // book清单 Items []*Book `json:"items"` } type Book struct { // 对象Id Id uint `json:"id" gorm:"primaryKey;column:id"` BookSpec } type BookSpec struct { // type 用于要使用gorm 来自动创建和更新表的时候 才需要定义 Title string `json:"title" gorm:"column:title;type:varchar(200)" validate:"required"` Author string `json:"author" gorm:"column:author;type:varchar(200);index" validate:"required"` Price float64 `json:"price" gorm:"column:price" validate:"required"` // bool false // nil 是零值, false IsSale *bool `json:"is_sale" gorm:"column:is_sale"` } // books func (b *Book) TableName() string { return "books" } // 初始化数据库 func setupDatabase() *gorm.DB { // 变量更新 dsn := "root:123456@tcp(127.0.0.1:3306)/go18?charset=utf8mb4&parseTime=True&loc=Local" db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { panic("failed to connect database") } db.AutoMigrate(&Book{}) // 自动迁移 return db.Debug() } var db = setupDatabase() var h = &BookApiHandler{} type BookApiHandler struct { } // 实现后端分页的 func (h *BookApiHandler) ListBook(ctx *gin.Context) { set := &BookSet{} // List<*Book> // *Set[T] // types.New[*Book]() // 给默认值 pn, ps := 1, 20 // /api/books?page_number=1&page_size=20 pageNumber := ctx.Query("page_number") if pageNumber != "" { pnInt, err := strconv.ParseInt(pageNumber, 10, 64) if err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } pn = int(pnInt) } pageSize := ctx.Query("page_size") if pageSize != "" { psInt, err := strconv.ParseInt(pageSize, 10, 64) if err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } ps = int(psInt) } query := db.Model(&Book{}) // 关键字过滤 kws := ctx.Query("keywords") if kws != "" { // where title like %kws% query = query.Where("title LIKE ?", "%"+kws+"%") } // 其他过滤条件 // select * from books // 通过sql的offset limte 来实现分页 // offset (page_number -1) * page_size, limit page_size // 2 offset 20, 20 // 3 offset 40, 20 // 4 offset 3 * 20, 20 offset := (pn - 1) * ps if err := query.Count(&set.Total).Offset(int(offset)).Limit(int(ps)).Find(&set.Items).Error; err != nil { ctx.JSON(500, gin.H{"code": 500, "message": err.Error()}) return } // 获取总数, 总共多少个, 总共有多少页 ctx.JSON(200, set) } func (h *BookApiHandler) CreateBook(ctx *gin.Context) { // payload, err := io.ReadAll(ctx.Request.Body) // if err != nil { // ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) // return // } // defer ctx.Request.Body.Close() // // {"title": "Go语言"} // c.Request.Header.Get(key) // ctx.GetHeader("Authincation") // new(Book) bookSpecInstance := &BookSpec{} // // 通过JSON的 Struct Tag // // bookInstance.Title = "Go语言" // if err := json.Unmarshal(payload, bookInstance); err != nil { // ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) // return // } // 获取到bookInstance // 参数是不是为空 if err := ctx.BindJSON(bookSpecInstance); err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } // 有没有能够检查某个字段是否是必须填 // Gin 集成 validator这个库, 通过 struct tag validate 来表示这个字段是否允许为空 // validate:"required" // 在数据Bind的时候,这个逻辑会自动运行 // if bookSpecInstance.Author == "" { // ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) // return // } bookInstance := &Book{BookSpec: *bookSpecInstance} // 数据入库(Grom), 补充自增Id的值 if err := db.Save(bookInstance).Error; err != nil { ctx.JSON(400, gin.H{"code": 500, "message": err.Error()}) return } // 返回响应 ctx.JSON(http.StatusCreated, bookInstance) } func (h *BookApiHandler) GetBook(ctx *gin.Context) { bookInstance := &Book{} // 需要从数据库中获取一个对象 if err := db.Where("id = ?", ctx.Param("bn")).Take(bookInstance).Error; err != nil { ctx.JSON(400, gin.H{"code": 500, "message": err.Error()}) return } ctx.JSON(200, bookInstance) } func (h *BookApiHandler) UpdateBook(ctx *gin.Context) { bnStr := ctx.Param("bn") bn, err := strconv.ParseInt(bnStr, 10, 64) if err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } // 读取body里面的参数 bookInstance := &Book{ Id: uint(bn), } // 获取到bookInstance if err := ctx.BindJSON(&bookInstance.BookSpec); err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } if err := db.Where("id = ?", bookInstance.Id).Updates(bookInstance).Error; err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } ctx.JSON(200, bookInstance) } func (h *BookApiHandler) DeleteBook(ctx *gin.Context) { if err := db.Where("id = ?", ctx.Param("bn")).Delete(&Book{}).Error; err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } ctx.JSON(http.StatusNoContent, "ok") } func main() { server := gin.Default() // Book Restful API // List of books server.GET("/api/books", h.ListBook) // Create new book // Body: HTTP Entity server.POST("/api/books", h.CreateBook) // Get book by book number server.GET("/api/books/:bn", h.GetBook) // Update book server.PUT("/api/books/:bn", h.UpdateBook) // Delete book server.DELETE("/api/books/:bn", h.DeleteBook) if err := server.Run(":8080"); err != nil { fmt.Println(err) os.Exit(1) } }