From 4cb4207d41bdbe0e52e75a213ca701fc269a9a39 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sat, 26 Apr 2025 17:51:38 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85book=20api=20crud?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/v1/README.md | 51 ++++++++++++++++++++ book/v1/main.go | 118 ++++++++++++++++++++++++++++++++-------------- 2 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 book/v1/README.md diff --git a/book/v1/README.md b/book/v1/README.md new file mode 100644 index 0000000..412da26 --- /dev/null +++ b/book/v1/README.md @@ -0,0 +1,51 @@ +# 演示 + +## 启动 + +```sh +➜ go run v1/main.go +[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. + +[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. + - using env: export GIN_MODE=release + - using code: gin.SetMode(gin.ReleaseMode) + +[GIN-debug] GET /api/books --> main.(*BookApiHandler).ListBook-fm (3 handlers) +[GIN-debug] POST /api/books --> main.(*BookApiHandler).CreateBook-fm (3 handlers) +[GIN-debug] GET /api/books/:bn --> main.(*BookApiHandler).GetBook-fm (3 handlers) +[GIN-debug] PUT /api/books/:bn --> main.(*BookApiHandler).UpdateBook-fm (3 handlers) +[GIN-debug] DELETE /api/books/:bn --> main.(*BookApiHandler).DeleteBook-fm (3 handlers) +[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. +Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. +[GIN-debug] Listening and serving HTTP on :8080 +``` + +## CRUD 示例 + +- 创建书籍: +```sh +curl -X POST http://localhost:8080/api/books -H "Content-Type: application/json" -d '{"title": "Go 语言", "author": "张三", "price": 39.99}' +``` + +- 获取所有书籍: +```sh +curl http://localhost:8080/api/books +``` + + +- 根据 ID 获取书籍: +```sh +curl http://localhost:8080/api/books/1 +``` + +- 更新书籍: +```sh +curl -X PUT http://localhost:8080/api/books/1 -H "Content-Type: application/json" -d '{"title": "Go 语言进阶", "author": "张三", "price": 49.99}' +``` + +- 删除书籍: +```sh +curl -X DELETE http://localhost:8080/api/books/1 +``` + +这样就完成了一个简单的使用 MySQL 的 Book CRUD 示例。你可以根据需要进一步扩展功能。 \ No newline at end of file diff --git a/book/v1/main.go b/book/v1/main.go index 20bae3d..55f0849 100644 --- a/book/v1/main.go +++ b/book/v1/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "net/http" "os" "strconv" @@ -10,10 +11,21 @@ import ( "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"` + 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"` @@ -31,13 +43,13 @@ func (b *Book) TableName() string { // 初始化数据库 func setupDatabase() *gorm.DB { // 变量更新 - dsn := "root:123456@tcp(127.0.0.1:3306)/test?charset=utf8mb4&parseTime=True&loc=Local" + 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 + return db.Debug() } var db = setupDatabase() @@ -49,22 +61,41 @@ type BookApiHandler struct { // 实现后端分页的 func (h *BookApiHandler) ListBook(ctx *gin.Context) { + set := &BookSet{} + + // 给默认值 + pn, ps := 1, 20 // /api/books?page_number=1&page_size=20 pageNumber := ctx.Query("page_number") - pn, err := strconv.ParseInt(pageNumber, 10, 64) - if err != nil { - ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) - return - } - pageSize := ctx.Query("page_size") - ps, err := strconv.ParseInt(pageSize, 10, 64) - if err != nil { - ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) - return + 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) } - // - bookList := []Book{} + 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+"%x") + } + + // 其他过滤条件 + // select * from books // 通过sql的offset limte 来实现分页 // offset (page_number -1) * page_size, limit page_size @@ -72,14 +103,13 @@ func (h *BookApiHandler) ListBook(ctx *gin.Context) { // 3 offset 40, 20 // 4 offset 3 * 20, 20 offset := (pn - 1) * ps - if err := db.Offset(int(offset)).Limit(int(ps)).Find(&bookList).Error; err != nil { + 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, bookList) - + ctx.JSON(200, set) } func (h *BookApiHandler) CreateBook(ctx *gin.Context) { @@ -95,7 +125,7 @@ func (h *BookApiHandler) CreateBook(ctx *gin.Context) { // ctx.GetHeader("Authincation") // new(Book) - bookInstance := &Book{} + bookSpecInstance := &BookSpec{} // // 通过JSON的 Struct Tag // // bookInstance.Title = "Go语言" // if err := json.Unmarshal(payload, bookInstance); err != nil { @@ -103,11 +133,23 @@ func (h *BookApiHandler) CreateBook(ctx *gin.Context) { // return // } // 获取到bookInstance - if err := ctx.BindJSON(bookInstance); err != nil { + // 参数是不是为空 + 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()}) @@ -115,48 +157,52 @@ func (h *BookApiHandler) CreateBook(ctx *gin.Context) { } // 返回响应 - ctx.JSON(200, bookInstance) + ctx.JSON(http.StatusCreated, bookInstance) } func (h *BookApiHandler) GetBook(ctx *gin.Context) { - // URI - bnStr := ctx.Param("bn") - bn, err := strconv.ParseInt(bnStr, 10, 64) - if err != nil { - ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) + 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 } - fmt.Println(bn) + + ctx.JSON(200, bookInstance) } func (h *BookApiHandler) UpdateBook(ctx *gin.Context) { - // URI 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 } - fmt.Println(bn) // 读取body里面的参数 - bookInstance := &Book{} + bookInstance := &Book{ + Id: uint(bn), + } // 获取到bookInstance - if err := ctx.BindJSON(bookInstance); err != nil { + 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) { - // URI - bnStr := ctx.Param("bn") - bn, err := strconv.ParseInt(bnStr, 10, 64) - if err != nil { + if err := db.Where("id = ?", ctx.Param("bn")).Delete(&Book{}).Error; err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } - fmt.Println(bn) + ctx.JSON(http.StatusNoContent, "ok") } func main() {