补充controller

This commit is contained in:
yumaojun03 2024-11-23 12:11:44 +08:00
parent df9ff23c81
commit 3ca5fb7eca
9 changed files with 256 additions and 18 deletions

View File

@ -2,9 +2,11 @@ package api
import (
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"gitlab.com/go-course-project/go17/book/config"
"gitlab.com/go-course-project/go17/book/controller"
"gitlab.com/go-course-project/go17/book/model"
"gitlab.com/go-course-project/go17/book/response"
"gorm.io/gorm"
@ -13,7 +15,8 @@ import (
// 构造函数, 用户初始化这个结构体
func NewBookHandler() *BookApiHandler {
return &BookApiHandler{
db: config.Get().MySQL.DB(),
db: config.Get().MySQL.DB(),
svc: controller.NewBookController(),
}
}
@ -21,6 +24,8 @@ func NewBookHandler() *BookApiHandler {
// BookApiHandler 他来实现接口的功能
type BookApiHandler struct {
db *gorm.DB
svc *controller.BookController
}
// 提供注册功能, 提供一个Group
@ -39,14 +44,14 @@ func (h *BookApiHandler) Registry(r *gin.Engine) {
// POST, Body
func (h *BookApiHandler) CreateBook(ctx *gin.Context) {
// 获取Book用户传达的参数
ins := new(model.Book)
if err := ctx.ShouldBindJSON(ins); err != nil {
req := new(model.BookSpec)
if err := ctx.ShouldBindJSON(req); err != nil {
response.Failed(ctx, err)
return
}
// book, save
if err := h.db.Save(ins).Error; err != nil {
ins, err := h.svc.CreateBook(ctx.Request.Context(), req)
if err != nil {
response.Failed(ctx, err)
return
}
@ -67,13 +72,22 @@ func (h *BookApiHandler) ListBook(ctx *gin.Context) {
// 查询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 {
strId := ctx.Param("isbn")
id, err := strconv.ParseInt(strId, 10, 64)
if err != nil {
response.Failed(ctx, err)
return
}
// 传递HTTP请求的上下文
ins, err := h.svc.GetBook(ctx.Request.Context(), &controller.GetBookRequest{
Isbn: id,
})
if err != nil {
response.Failed(ctx, err)
return
}
ctx.JSON(http.StatusOK, ins)
}

View File

@ -1 +1,37 @@
package api
import (
"github.com/gin-gonic/gin"
"gitlab.com/go-course-project/go17/book/config"
"gorm.io/gorm"
)
// 构造函数, 用户初始化这个结构体
func NewCommentApiHandler() *CommentApiHandler {
return &CommentApiHandler{
db: config.Get().MySQL.DB(),
}
}
// 面向对象
// BookApiHandler 他来实现接口的功能
type CommentApiHandler struct {
db *gorm.DB
}
func (h *CommentApiHandler) AddComment(ctx *gin.Context) {
// book id, user a, comment
// book id
// 判断book id 是不是合法, 到底有没有这本book
// 1. NewBookHandler().GetBook(ctx)
// 2. 把这个逻辑再写一套
// if err := h.db.Where("isbn = ?", id).Take(&ins).Error; err != nil {
// response.Failed(ctx, err)
// return
// }
// 你需要什么?
// 你需要一个 功能(业务处理逻辑): GetBook() (<Book>, error)
// controller.GetBook()
}

View File

@ -1 +1,9 @@
# 业务层 (业务处理逻辑)
## 和谐业务层:
+ 复杂的业务逻辑处理(controller之间相互调用)
+ 这个内部功能(公共功能) 不需要提供接口, 需要被其他多个业务模块引用
+ 复杂的业务是需要有单元测试

62
book/controller/book.go Normal file
View File

@ -0,0 +1,62 @@
package controller
import (
"context"
"gitlab.com/go-course-project/go17/book/config"
"gitlab.com/go-course-project/go17/book/model"
"gorm.io/gorm"
)
// 构造函数, 用户初始化这个结构体
func NewBookController() *BookController {
return &BookController{
db: config.Get().MySQL.DB(),
}
}
type BookController struct {
db *gorm.DB
}
type GetBookRequest struct {
Isbn int64
// ...
// TraceId string
}
// 怎么定义你的Controller的参数
// 怎么定义你BookController的接口(interface)
// 函数名称 + 参数 + 返回 + 异常
// 参数 一定要 注意 兼容性
// 1.参数类型,个数
// 怎么设计一个 具有良好兼容性的接口, 个数,类型
// 采用一个请求类型 作为参数, 变更请求结构体, 不会变更类型,有更好的兼容性
// 1. 非功能性的需求, 要添加TraceID, 请求支持取消
// go context.Context 既能存储数据也能做Gorouteine的取消
// 这个跟接口是无关的,是纯业务处理
func (c *BookController) GetBook(ctx context.Context, req *GetBookRequest) (*model.Book, error) {
ins := &model.Book{}
if err := c.db.WithContext(ctx).Where("isbn = ?", req.Isbn).Take(ins).Error; err != nil {
return nil, err
}
return ins, nil
}
// 这一层做强教育,避免内部相互调用是,处理未校验的情况
func (c *BookController) CreateBook(ctx context.Context, req *model.BookSpec) (*model.Book, error) {
if err := req.Validate(); err != nil {
return nil, err
}
// book, save
ins := &model.Book{
BookSpec: *req,
}
if err := c.db.Save(ins).Error; err != nil {
return nil, err
}
return ins, nil
}

View File

@ -0,0 +1,42 @@
package controller_test
import (
"context"
"testing"
"gitlab.com/go-course-project/go17/book/controller"
"gitlab.com/go-course-project/go17/book/model"
)
func TestGetBook(t *testing.T) {
book := controller.NewBookController()
ins, err := book.GetBook(context.Background(), &controller.GetBookRequest{
Isbn: 5,
})
if err != nil {
t.Fatal(err)
}
t.Log(ins)
}
// INSERT INTO `books` (`title`,`author`,`price`,`is_sale`) VALUES ('Go语言','will',10,NULL)
//
// {
// "isbn": 5,
// "title": "Go语言",
// "author": "will",
// "price": 10,
// "is_sale": null
// }
func TestCreateBook(t *testing.T) {
book := controller.NewBookController()
ins, err := book.CreateBook(context.Background(), &model.BookSpec{
Author: "will",
Price: 10,
Title: "Go语言",
})
if err != nil {
t.Fatal(err)
}
t.Log(ins)
}

View File

@ -0,0 +1,39 @@
package controller
import (
"github.com/gin-gonic/gin"
"gitlab.com/go-course-project/go17/book/config"
"gorm.io/gorm"
)
// 构造函数, 用户初始化这个结构体
func NewCommentController() *CommentController {
return &CommentController{
db: config.Get().MySQL.DB(),
book: NewBookController(),
}
}
type CommentController struct {
db *gorm.DB
book *BookController
}
func (h *CommentController) AddComment(ctx *gin.Context) {
// book id, user a, comment
// book id
// 判断book id 是不是合法, 到底有没有这本book
// 1. NewBookHandler().GetBook(ctx)
// 2. 把这个逻辑再写一套
// if err := h.db.Where("isbn = ?", id).Take(&ins).Error; err != nil {
// response.Failed(ctx, err)
// return
// }
// 你需要什么?
// 你需要一个 功能(业务处理逻辑): GetBook() (<Book>, error)
// controller.GetBook()
// h.book.GetBook(ctx, isbn)
}

View File

@ -1,5 +1,16 @@
package model
import (
"fmt"
"github.com/go-playground/validator/v10"
"github.com/infraboard/mcube/v2/tools/pretty"
)
var (
v = validator.New()
)
// Book 结构体定义
type Book struct {
// grom:"column:isbn;", 具体文档: https://gorm.io/docs/models.html#Fields-Tags
@ -7,15 +18,37 @@ type Book struct {
BookSpec
}
// ret, err := json.MarshalIndent(e, "", jsonIndent)
//
// if err != nil {
// return fmt.Sprintf("%+v", e)
// }
//
// return string(ret)
func (b *Book) String() string {
// 我提炼出来功能代码工具
return pretty.ToJSON(b)
}
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"`
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"`
}
// 怎么校验 struct 参数
func (r *BookSpec) Validate() error {
if r.Author == "" {
return fmt.Errorf("author 不能为空")
}
// 通用的校验逻辑比如是否为空可以考虑使用validate包
return v.Struct(r)
}
// 定义该对象映射到数据里 表的名称
func (t *Book) TableName() string {
return "books"

4
go.mod
View File

@ -4,6 +4,8 @@ go 1.22.0
require (
github.com/gin-gonic/gin v1.10.0
github.com/go-playground/validator/v10 v10.20.0
github.com/infraboard/mcube/v2 v2.0.44
gopkg.in/yaml.v3 v3.0.1
gorm.io/driver/mysql v1.5.7
gorm.io/gorm v1.25.12
@ -18,7 +20,6 @@ require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.20.0 // indirect
github.com/go-sql-driver/mysql v1.7.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
@ -38,4 +39,5 @@ require (
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.15.0 // indirect
google.golang.org/protobuf v1.34.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

10
go.sum
View File

@ -27,9 +27,11 @@ github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/infraboard/mcube/v2 v2.0.44 h1:dspnLDspWpz5r6YgFTOmIlWE4kjog0082luhvd8AUds=
github.com/infraboard/mcube/v2 v2.0.44/go.mod h1:UkjuO7zbehNNvAsA1kZMB2ztaZlDY9XmTfBnNnilzB4=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
@ -82,8 +84,6 @@ golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
@ -98,3 +98,5 @@ gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=