From a1bd181212df73c4fdc6d7442dd1a8f2500d7098 Mon Sep 17 00:00:00 2001 From: yumaojun03 <719118794@qq.com> Date: Sun, 11 May 2025 16:23:14 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=8E=A7=E5=88=B6=E5=99=A8?= =?UTF-8?q?=E7=9A=84=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- book/v3/application.yaml | 10 +++++ book/v3/config/README.md | 69 +++++++++++++++++++++++++++++++- book/v3/config/config.go | 56 ++++++++++++++++++++++++++ book/v3/config/load.go | 5 +++ book/v3/controllers/README.md | 5 +++ book/v3/controllers/arch.drawio | 25 +++++++----- book/v3/controllers/book.go | 26 +++++++++++- book/v3/controllers/book_test.go | 40 ++++++++++++++++++ book/v3/controllers/comment.go | 2 +- book/v3/handlers/book.go | 27 +++++-------- book/v3/main.go | 7 ++++ book/v3/models/book.go | 6 +++ go.mod | 2 + go.sum | 12 ++++++ 14 files changed, 263 insertions(+), 29 deletions(-) create mode 100644 book/v3/application.yaml create mode 100644 book/v3/controllers/book_test.go diff --git a/book/v3/application.yaml b/book/v3/application.yaml new file mode 100644 index 0000000..9bd730f --- /dev/null +++ b/book/v3/application.yaml @@ -0,0 +1,10 @@ +app: + host: 127.0.0.1 + port: 8080 +mysql: + host: 127.0.0.1 + port: 3306 + database: go18 + username: "root" + password: "123456" + debug: true diff --git a/book/v3/config/README.md b/book/v3/config/README.md index f3d7d23..76aa30b 100644 --- a/book/v3/config/README.md +++ b/book/v3/config/README.md @@ -1,6 +1,5 @@ # 程序的配置管理 - ## 配置的加载 ```go // 用于加载配置 @@ -18,3 +17,71 @@ config.C().MySQL.Host 如何验证我们这个包的 业务逻辑是正确 +```go +func TestLoadConfigFromYaml(t *testing.T) { + err := config.LoadConfigFromYaml(fmt.Sprintf("%s/book/v2/application.yaml", os.Getenv("workspaceFolder"))) + if err != nil { + t.Fatal(err) + } + t.Log(config.C()) +} + +func TestLoadConfigFromEnv(t *testing.T) { + os.Setenv("DATASOURCE_HOST", "localhost") + err := config.LoadConfigFromEnv() + if err != nil { + t.Fatal(err) + } + t.Log(config.C()) +} +``` + +## 补充日志配置 + +```go +// 如果是文件,结合该库使用"gopkg.in/natefinch/lumberjack.v2" +// 自己的作业: 添加日志轮转配置,结合 gopkg.in/natefinch/lumberjack.v2 使用 +// 可以参考: https://github.com/infraboard/mcube/blob/master/ioc/config/log/logger.go +type Log struct { + Level zerolog.Level `json:"level" yaml:"level" toml:"level" env:"LOG_LEVEL"` + + logger *zerolog.Logger + lock sync.Mutex +} + +func (l *Log) SetLogger(logger zerolog.Logger) { + l.logger = &logger +} + +func (l *Log) Logger() *zerolog.Logger { + l.lock.Lock() + defer l.lock.Unlock() + + if l.logger == nil { + l.SetLogger(zerolog.New(l.ConsoleWriter()).Level(l.Level).With().Caller().Timestamp().Logger()) + } + + return l.logger +} + +func (c *Log) ConsoleWriter() io.Writer { + output := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.NoColor = false + w.TimeFormat = time.RFC3339 + }) + + output.FormatLevel = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%-6s", i)) + } + output.FormatMessage = func(i interface{}) string { + return fmt.Sprintf("%s", i) + } + output.FormatFieldName = func(i interface{}) string { + return fmt.Sprintf("%s:", i) + } + output.FormatFieldValue = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%s", i)) + } + return output +} +``` \ No newline at end of file diff --git a/book/v3/config/config.go b/book/v3/config/config.go index 12981aa..96bf3d0 100644 --- a/book/v3/config/config.go +++ b/book/v3/config/config.go @@ -2,10 +2,14 @@ package config import ( "fmt" + "io" + "strings" "sync" + "time" "122.51.31.227/go-course/go18/book/v3/models" "github.com/infraboard/mcube/v2/tools/pretty" + "github.com/rs/zerolog" "gorm.io/driver/mysql" "gorm.io/gorm" ) @@ -24,6 +28,9 @@ func Default() *Config { Password: "123456", Debug: true, }, + Log: &Log{ + Level: zerolog.DebugLevel, + }, } } @@ -32,6 +39,7 @@ func Default() *Config { type Config struct { Application *application `toml:"app" yaml:"app" json:"app"` MySQL *mySQL `toml:"mysql" yaml:"mysql" json:"mysql"` + Log *Log `toml:"log" yaml:"log" json:"log"` } func (c *Config) String() string { @@ -73,6 +81,7 @@ func (m *mySQL) GetDB() *gorm.DB { m.Port, m.DB, ) + L().Info().Msgf("Database: %s", m.DB) db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) if err != nil { @@ -80,7 +89,54 @@ func (m *mySQL) GetDB() *gorm.DB { } db.AutoMigrate(&models.Book{}) // 自动迁移 m.db = db + } return m.db } + +// 如果是文件,结合该库使用"gopkg.in/natefinch/lumberjack.v2" +// 自己的作业: 添加日志轮转配置,结合 gopkg.in/natefinch/lumberjack.v2 使用 +// 可以参考: +type Log struct { + Level zerolog.Level `json:"level" yaml:"level" toml:"level" env:"LOG_LEVEL"` + + logger *zerolog.Logger + lock sync.Mutex +} + +func (l *Log) SetLogger(logger zerolog.Logger) { + l.logger = &logger +} + +func (l *Log) Logger() *zerolog.Logger { + l.lock.Lock() + defer l.lock.Unlock() + + if l.logger == nil { + l.SetLogger(zerolog.New(l.ConsoleWriter()).Level(l.Level).With().Caller().Timestamp().Logger()) + } + + return l.logger +} + +func (c *Log) ConsoleWriter() io.Writer { + output := zerolog.NewConsoleWriter(func(w *zerolog.ConsoleWriter) { + w.NoColor = false + w.TimeFormat = time.RFC3339 + }) + + output.FormatLevel = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%-6s", i)) + } + output.FormatMessage = func(i interface{}) string { + return fmt.Sprintf("%s", i) + } + output.FormatFieldName = func(i interface{}) string { + return fmt.Sprintf("%s:", i) + } + output.FormatFieldValue = func(i interface{}) string { + return strings.ToUpper(fmt.Sprintf("%s", i)) + } + return output +} diff --git a/book/v3/config/load.go b/book/v3/config/load.go index 8edcf84..bd44fb6 100644 --- a/book/v3/config/load.go +++ b/book/v3/config/load.go @@ -4,6 +4,7 @@ import ( "os" "github.com/caarlos0/env/v6" + "github.com/rs/zerolog" "gopkg.in/yaml.v3" "gorm.io/gorm" ) @@ -29,6 +30,10 @@ func DB() *gorm.DB { return C().MySQL.GetDB() } +func L() *zerolog.Logger { + return C().Log.Logger() +} + // 加载配置 把外部配置读到 config全局变量里面来 // yaml 文件yaml --> conf func LoadConfigFromYaml(configPath string) error { diff --git a/book/v3/controllers/README.md b/book/v3/controllers/README.md index 1b8ac10..fe68ca7 100644 --- a/book/v3/controllers/README.md +++ b/book/v3/controllers/README.md @@ -1,3 +1,8 @@ # 控制器 业务处理 + + +## 单元测试 (TDD) + + diff --git a/book/v3/controllers/arch.drawio b/book/v3/controllers/arch.drawio index 796cc84..cf97add 100644 --- a/book/v3/controllers/arch.drawio +++ b/book/v3/controllers/arch.drawio @@ -15,14 +15,6 @@ - - - - - - - - @@ -39,7 +31,7 @@ - + @@ -47,6 +39,21 @@ + + + + + + + + + + + + + + + diff --git a/book/v3/controllers/book.go b/book/v3/controllers/book.go index c40cb32..e3b4daf 100644 --- a/book/v3/controllers/book.go +++ b/book/v3/controllers/book.go @@ -12,14 +12,14 @@ var Book = &BookController{} type BookController struct { } -func NewGetBookRequest(bookNumber string) *GetBookRequest { +func NewGetBookRequest(bookNumber int) *GetBookRequest { return &GetBookRequest{ BookNumber: bookNumber, } } type GetBookRequest struct { - BookNumber string + BookNumber int // RequestId string // ... } @@ -32,6 +32,8 @@ func (c *BookController) GetBook(ctx context.Context, in *GetBookRequest) (*mode // context.WithValue(ctx, "request_id", 111) // ctx.Value("request_id") + config.L().Debug().Msgf("get book: %d", in.BookNumber) + bookInstance := &models.Book{} // 需要从数据库中获取一个对象 if err := config.DB().Where("id = ?", in.BookNumber).Take(bookInstance).Error; err != nil { @@ -40,3 +42,23 @@ func (c *BookController) GetBook(ctx context.Context, in *GetBookRequest) (*mode return bookInstance, nil } + +func (c *BookController) CreateBook(ctx context.Context, in *models.BookSpec) (*models.Book, error) { + // 有没有能够检查某个字段是否是必须填 + // Gin 集成 validator这个库, 通过 struct tag validate 来表示这个字段是否允许为空 + // validate:"required" + // 在数据Bind的时候,这个逻辑会自动运行 + // if bookSpecInstance.Author == "" { + // ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) + // return + // } + + bookInstance := &models.Book{BookSpec: *in} + + // 数据入库(Grom), 补充自增Id的值 + if err := config.DB().Save(bookInstance).Error; err != nil { + return nil, err + } + + return bookInstance, nil +} diff --git a/book/v3/controllers/book_test.go b/book/v3/controllers/book_test.go new file mode 100644 index 0000000..4a5fcac --- /dev/null +++ b/book/v3/controllers/book_test.go @@ -0,0 +1,40 @@ +package controllers_test + +import ( + "context" + "fmt" + "os" + "testing" + + "122.51.31.227/go-course/go18/book/v3/config" + "122.51.31.227/go-course/go18/book/v3/controllers" + "122.51.31.227/go-course/go18/book/v3/models" +) + +func TestGetBook(t *testing.T) { + book, err := controllers.Book.GetBook(context.Background(), controllers.NewGetBookRequest(3)) + if err != nil { + t.Fatal(err) + } + t.Log(book) +} + +func TestCreateBook(t *testing.T) { + book, err := controllers.Book.CreateBook(context.Background(), &models.BookSpec{ + Title: "unit test for go controller obj", + Author: "will", + Price: 99.99, + }) + if err != nil { + t.Fatal(err) + } + t.Log(book) +} + +func init() { + // 执行配置的加载 + err := config.LoadConfigFromYaml(fmt.Sprintf("%s/book/v3/application.yaml", os.Getenv("workspaceFolder"))) + if err != nil { + panic(err) + } +} diff --git a/book/v3/controllers/comment.go b/book/v3/controllers/comment.go index b13189e..6b537d6 100644 --- a/book/v3/controllers/comment.go +++ b/book/v3/controllers/comment.go @@ -13,7 +13,7 @@ type CommentController struct { } type AddCommentRequest struct { - BookNumber string + BookNumber int } func (c *CommentController) AddComment(ctx context.Context, in *AddCommentRequest) (*models.Comment, error) { diff --git a/book/v3/handlers/book.go b/book/v3/handlers/book.go index 27901a3..ab835f5 100644 --- a/book/v3/handlers/book.go +++ b/book/v3/handlers/book.go @@ -114,29 +114,24 @@ func (h *BookApiHandler) createBook(ctx *gin.Context) { return } - // 有没有能够检查某个字段是否是必须填 - // Gin 集成 validator这个库, 通过 struct tag validate 来表示这个字段是否允许为空 - // validate:"required" - // 在数据Bind的时候,这个逻辑会自动运行 - // if bookSpecInstance.Author == "" { - // ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) - // return - // } - - bookInstance := &models.Book{BookSpec: *bookSpecInstance} - - // 数据入库(Grom), 补充自增Id的值 - if err := config.DB().Save(bookInstance).Error; err != nil { - ctx.JSON(400, gin.H{"code": 500, "message": err.Error()}) + book, err := controllers.Book.CreateBook(ctx.Request.Context(), bookSpecInstance) + if err != nil { + ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return } // 返回响应 - ctx.JSON(http.StatusCreated, bookInstance) + ctx.JSON(http.StatusCreated, book) } func (h *BookApiHandler) getBook(ctx *gin.Context) { - book, err := controllers.Book.GetBook(ctx, controllers.NewGetBookRequest(ctx.Param("bn"))) + bnInt, err := strconv.ParseInt(ctx.Param("bn"), 10, 64) + if err != nil { + ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) + return + } + + book, err := controllers.Book.GetBook(ctx, controllers.NewGetBookRequest(int(bnInt))) if err != nil { ctx.JSON(400, gin.H{"code": 400, "message": err.Error()}) return diff --git a/book/v3/main.go b/book/v3/main.go index 4798731..b863f4b 100644 --- a/book/v3/main.go +++ b/book/v3/main.go @@ -10,6 +10,13 @@ import ( ) func main() { + // 加载配置 + path := os.Getenv("CONFIG_PATH") + if path == "" { + path = "application.yaml" + } + config.LoadConfigFromYaml(path) + server := gin.Default() handlers.Book.Registry(server) diff --git a/book/v3/models/book.go b/book/v3/models/book.go index 4ccc402..618bd7c 100644 --- a/book/v3/models/book.go +++ b/book/v3/models/book.go @@ -1,5 +1,7 @@ package models +import "github.com/infraboard/mcube/v2/tools/pretty" + type BookSet struct { // 总共多少个 Total int64 `json:"total"` @@ -14,6 +16,10 @@ type Book struct { BookSpec } +func (b *Book) String() string { + return pretty.ToJSON(b) +} + type BookSpec struct { // type 用于要使用gorm 来自动创建和更新表的时候 才需要定义 Title string `json:"title" gorm:"column:title;type:varchar(200)" validate:"required"` diff --git a/go.mod b/go.mod index 3975f7f..7d5aec0 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/caarlos0/env/v6 v6.10.1 github.com/gin-gonic/gin v1.10.0 github.com/infraboard/mcube/v2 v2.0.59 + github.com/rs/zerolog v1.34.0 github.com/stretchr/testify v1.10.0 gopkg.in/yaml.v3 v3.0.1 gorm.io/driver/mysql v1.5.7 @@ -29,6 +30,7 @@ require ( github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect diff --git a/go.sum b/go.sum index 3e3d291..aa990a8 100644 --- a/go.sum +++ b/go.sum @@ -8,6 +8,7 @@ github.com/caarlos0/env/v6 v6.10.1/go.mod h1:hvp/ryKXKipEkcuYjs9mI4bBCg+UI0Yhgm5 github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -29,6 +30,7 @@ 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.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= 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= @@ -46,6 +48,10 @@ github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQe github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -55,8 +61,12 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0= +github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY= +github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -79,7 +89,9 @@ golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8= golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw= golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=