From df9ff23c8174b11712589ca3d695764513af66b8 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sat, 23 Nov 2024 10:55:52 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85mvc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/api/book.go | 186 ++++++++++++++++++++++++++++++++++++++ book/api/comment.go | 1 + book/api/user.go | 1 + book/main.go | 114 +---------------------- book/model/book.go | 22 +++++ book/model/comment.go | 1 + book/model/user.go | 1 + book/response/README.md | 47 ++++++++++ book/response/response.go | 15 +++ skills/gin/main.go | 1 + 10 files changed, 277 insertions(+), 112 deletions(-) create mode 100644 book/api/book.go create mode 100644 book/api/comment.go create mode 100644 book/api/user.go create mode 100644 book/model/book.go create mode 100644 book/model/comment.go create mode 100644 book/model/user.go create mode 100644 book/response/README.md create mode 100644 book/response/response.go diff --git a/book/api/book.go b/book/api/book.go new file mode 100644 index 0000000..e054c63 --- /dev/null +++ b/book/api/book.go @@ -0,0 +1,186 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "gitlab.com/go-course-project/go17/book/config" + "gitlab.com/go-course-project/go17/book/model" + "gitlab.com/go-course-project/go17/book/response" + "gorm.io/gorm" +) + +// 构造函数, 用户初始化这个结构体 +func NewBookHandler() *BookApiHandler { + return &BookApiHandler{ + db: config.Get().MySQL.DB(), + } +} + +// 面向对象 +// BookApiHandler 他来实现接口的功能 +type BookApiHandler struct { + db *gorm.DB +} + +// 提供注册功能, 提供一个Group +// book := server.Group("/api/books") +func (h *BookApiHandler) Registry(r *gin.Engine) { + book := r.Group("/api/books") + book.POST("", h.CreateBook) + book.GET("", h.ListBook) + book.GET("/:isbn", h.GetBook) + book.PUT("/:isbn", h.UpdateBook) + book.DELETE("/:isbn", h.DeleteBook) +} + +// 配置业务路有, Book类型的资源的 一套简单的CRUD +// 创建Book -> Book +// POST, Body +func (h *BookApiHandler) CreateBook(ctx *gin.Context) { + // 获取Book用户传达的参数 + ins := new(model.Book) + if err := ctx.ShouldBindJSON(ins); err != nil { + response.Failed(ctx, err) + return + } + + // book, save + if err := h.db.Save(ins).Error; err != nil { + response.Failed(ctx, err) + return + } + + response.Success(ctx, ins) +} + +// 查询Book列表 -> []*Book +// SELECT * FROM `books` +func (h *BookApiHandler) ListBook(ctx *gin.Context) { + var books []model.Book + if err := h.db.Find(&books).Error; err != nil { + response.Failed(ctx, err) + return + } + ctx.JSON(http.StatusOK, books) +} + +// 查询Book详情 +func (h *BookApiHandler) GetBook(ctx *gin.Context) { + var ins model.Book + id := ctx.Param("isbn") + + if err := h.db.Where("isbn = ?", id).Take(&ins).Error; err != nil { + response.Failed(ctx, err) + return + } + ctx.JSON(http.StatusOK, ins) +} + +// 更新Book +func (h *BookApiHandler) UpdateBook(ctx *gin.Context) { + var ins model.Book + id := ctx.Param("isbn") + if err := h.db.Where("isbn = ?", id).Take(&ins).Error; err != nil { + response.Failed(ctx, err) + return + } + ctx.JSON(http.StatusOK, ins) +} + +// 删除Book +func (h *BookApiHandler) DeleteBook(ctx *gin.Context) { + id := ctx.Param("isbn") + if err := h.db.Where("isbn = ?", id).Delete(model.Book{}).Error; err != nil { + response.Failed(ctx, err) + return + } +} + +func Registry(r *gin.Engine) { + // // 配置业务路有, Book类型的资源的 一套简单的CRUD + // // 创建Book -> Book + // // POST, Body + // book := server.Group("/api/books") + // book.POST("", func(ctx *gin.Context) { + // // 获取Book用户传达的参数 + // ins := new(Book) + // if err := ctx.ShouldBindJSON(ins); err != nil { + // Failed(ctx, err) + // return + // } + + // // book, save + // if err := db.Save(ins).Error; err != nil { + // Failed(ctx, err) + // return + // } + + // ctx.JSON(http.StatusOK, ins) + // }) + // // 查询Book列表 -> []*Book + // // SELECT * FROM `books` + // book.GET("", func(ctx *gin.Context) { + // var books []Book + // if err := db.Find(&books).Error; err != nil { + // Failed(ctx, err) + // return + // } + // ctx.JSON(http.StatusOK, books) + // }) + // // 查询Book详情 + // book.GET("/:isbn", func(ctx *gin.Context) { + // var ins Book + // id := ctx.Param("isbn") + // if err := db.Where("isbn = ?", id).Take(&ins).Error; err != nil { + // Failed(ctx, fmt.Errorf("Book not found")) + // return + // } + // ctx.JSON(http.StatusOK, ins) + // }) + // // 更新Book + // book.PUT("/:isbn", func(ctx *gin.Context) { + // id := ctx.Param("isbn") + + // // 获取用户参数, 读取用户的更新参数 + // req := BookSpec{} + // if err := ctx.ShouldBindJSON(&req); err != nil { + // Failed(ctx, err) + // return + // } + + // // gorm更新是,如果字段为零值 就不更新该字段, is_sale 没办法更新为false + // // 如果越到零值,也需要更新,则需要转化为 指针类型 + // if err := db.Where("isbn = ?", id).Model(&Book{}).Updates(req).Error; err != nil { + // Failed(ctx, err) + // return + // } + + // // 针对有零值的字段独立更新 + // // if err := db.Where("isbn = ?", id).Model(&Book{}).Update("is_sale", req.IsSale).Error; err != nil { + // // Failed(ctx, err) + // // return + // // } + // // // ...... + + // // 再次查询出来 + // var ins Book + // if err := db.Where("isbn = ?", id).Take(&ins).Error; err != nil { + // Failed(ctx, fmt.Errorf("Book not found")) + // return + // } + + // // 查询出更新后的数据 + // ctx.JSON(http.StatusOK, ins) + // }) + // + // // 删除Book + // + // book.DELETE("/:isbn", func(ctx *gin.Context) { + // id := ctx.Param("isbn") + // if err := db.Where("isbn = ?", id).Delete(&Book{}).Error; err != nil { + // Failed(ctx, err) + // return + // } + // }) +} diff --git a/book/api/comment.go b/book/api/comment.go new file mode 100644 index 0000000..778f64e --- /dev/null +++ b/book/api/comment.go @@ -0,0 +1 @@ +package api diff --git a/book/api/user.go b/book/api/user.go new file mode 100644 index 0000000..778f64e --- /dev/null +++ b/book/api/user.go @@ -0,0 +1 @@ +package api diff --git a/book/main.go b/book/main.go index d339f46..ff151bd 100644 --- a/book/main.go +++ b/book/main.go @@ -3,17 +3,13 @@ package main import ( "fmt" "log" - "net/http" "os" "github.com/gin-gonic/gin" + "gitlab.com/go-course-project/go17/book/api" "gitlab.com/go-course-project/go17/book/config" ) -func Failed(ctx *gin.Context, err error) { - ctx.JSON(http.StatusBadRequest, gin.H{"code": 0, "msg": err.Error()}) -} - // 规定好风格: JSON Restful Api func main() { // 从配置文件中加载配置 @@ -26,120 +22,14 @@ func main() { fmt.Printf("加载配置错误: %s\n", err) os.Exit(1) } - // 访问加载后的配置 conf := config.Get() // gin Engine, 它包装了http server server := gin.Default() - - db := conf.MySQL.DB() - // 配置业务路有, Book类型的资源的 一套简单的CRUD - // 创建Book -> Book - // POST, Body - book := server.Group("/api/books") - book.POST("", func(ctx *gin.Context) { - // 获取Book用户传达的参数 - ins := new(Book) - if err := ctx.ShouldBindJSON(ins); err != nil { - Failed(ctx, err) - return - } - - // book, save - if err := db.Save(ins).Error; err != nil { - Failed(ctx, err) - return - } - - ctx.JSON(http.StatusOK, ins) - }) - // 查询Book列表 -> []*Book - // SELECT * FROM `books` - book.GET("", func(ctx *gin.Context) { - var books []Book - if err := db.Find(&books).Error; err != nil { - Failed(ctx, err) - return - } - ctx.JSON(http.StatusOK, books) - }) - // 查询Book详情 - book.GET("/:isbn", func(ctx *gin.Context) { - var ins Book - id := ctx.Param("isbn") - if err := db.Where("isbn = ?", id).Take(&ins).Error; err != nil { - Failed(ctx, fmt.Errorf("Book not found")) - return - } - ctx.JSON(http.StatusOK, ins) - }) - // 更新Book - book.PUT("/:isbn", func(ctx *gin.Context) { - id := ctx.Param("isbn") - - // 获取用户参数, 读取用户的更新参数 - req := BookSpec{} - if err := ctx.ShouldBindJSON(&req); err != nil { - Failed(ctx, err) - return - } - - // gorm更新是,如果字段为零值 就不更新该字段, is_sale 没办法更新为false - // 如果越到零值,也需要更新,则需要转化为 指针类型 - if err := db.Where("isbn = ?", id).Model(&Book{}).Updates(req).Error; err != nil { - Failed(ctx, err) - return - } - - // 针对有零值的字段独立更新 - // if err := db.Where("isbn = ?", id).Model(&Book{}).Update("is_sale", req.IsSale).Error; err != nil { - // Failed(ctx, err) - // return - // } - // // ...... - - // 再次查询出来 - var ins Book - if err := db.Where("isbn = ?", id).Take(&ins).Error; err != nil { - Failed(ctx, fmt.Errorf("Book not found")) - return - } - - // 查询出更新后的数据 - ctx.JSON(http.StatusOK, ins) - }) - // 删除Book - book.DELETE("/:isbn", func(ctx *gin.Context) { - id := ctx.Param("isbn") - if err := db.Where("isbn = ?", id).Delete(&Book{}).Error; err != nil { - Failed(ctx, err) - return - } - }) + api.NewBookHandler().Registry(server) if err := server.Run(conf.App.Address()); err != nil { log.Println(err) } } - -// Book 结构体定义 -type Book struct { - // grom:"column:isbn;", 具体文档: https://gorm.io/docs/models.html#Fields-Tags - IsBN uint `json:"isbn" gorm:"primaryKey;column:isbn"` - BookSpec -} - -type BookSpec struct { - Title string `json:"title" gorm:"column:title;type:varchar(200)"` - Author string `json:"author" gorm:"column:author;type:varchar(200);index"` - Price float64 `json:"price" gorm:"column:price"` - // bool false - // nil 是零值, false - IsSale *bool `json:"is_sale" gorm:"column:is_sale"` -} - -// 定义该对象映射到数据里 表的名称 -func (t *Book) TableName() string { - return "books" -} diff --git a/book/model/book.go b/book/model/book.go new file mode 100644 index 0000000..6ef487f --- /dev/null +++ b/book/model/book.go @@ -0,0 +1,22 @@ +package model + +// Book 结构体定义 +type Book struct { + // grom:"column:isbn;", 具体文档: https://gorm.io/docs/models.html#Fields-Tags + IsBN uint `json:"isbn" gorm:"primaryKey;column:isbn"` + BookSpec +} + +type BookSpec struct { + Title string `json:"title" gorm:"column:title;type:varchar(200)"` + Author string `json:"author" gorm:"column:author;type:varchar(200);index"` + Price float64 `json:"price" gorm:"column:price"` + // bool false + // nil 是零值, false + IsSale *bool `json:"is_sale" gorm:"column:is_sale"` +} + +// 定义该对象映射到数据里 表的名称 +func (t *Book) TableName() string { + return "books" +} diff --git a/book/model/comment.go b/book/model/comment.go new file mode 100644 index 0000000..8b53790 --- /dev/null +++ b/book/model/comment.go @@ -0,0 +1 @@ +package model diff --git a/book/model/user.go b/book/model/user.go new file mode 100644 index 0000000..8b53790 --- /dev/null +++ b/book/model/user.go @@ -0,0 +1 @@ +package model diff --git a/book/response/README.md b/book/response/README.md new file mode 100644 index 0000000..6d7af3d --- /dev/null +++ b/book/response/README.md @@ -0,0 +1,47 @@ +# api 接口响应 + +接口要区分 成功/失败 + +## 经典 + +成功和响应 使用一套机制 +```json +{ + "code": 0, + "data": , + "message": "" +} +``` + +成功: +```json +{ + "code": 0, + "data": any, +} +``` +失败 +```json +{ + "code": 10000, + "message": "" +} +``` + +## 根据贴合HTTP协议语义 + +通过 HTTP Code来定义 API接口返回是否成功 + +200 +成功: 直接返回业务数据,不是再包装一层 +```json + +``` + +失败之间返回失败信息, 400/401/403/500 +```json +{ + "code": 10000, + "message": "" +} +``` \ No newline at end of file diff --git a/book/response/response.go b/book/response/response.go new file mode 100644 index 0000000..4959f8a --- /dev/null +++ b/book/response/response.go @@ -0,0 +1,15 @@ +package response + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func Failed(ctx *gin.Context, err error) { + ctx.JSON(http.StatusBadRequest, gin.H{"code": 0, "msg": err.Error()}) +} + +func Success(ctx *gin.Context, data any) { + ctx.JSON(http.StatusOK, data) +} diff --git a/skills/gin/main.go b/skills/gin/main.go index a10d15a..d00354a 100644 --- a/skills/gin/main.go +++ b/skills/gin/main.go @@ -18,6 +18,7 @@ func setupDatabase() *gorm.DB { panic("failed to connect database") } db.AutoMigrate(&Book{}) // 自动迁移 + return db.Debug() }