Go+Vue3开发博客系统之一 | 字数总计: 5.2k | 阅读时长: 24分钟 | 阅读量: |
注意:这个项目烂尾了,请谨慎观看!
开发环境:
Windows server 2022+GoLand2024.1+MySQL8.3Winx64+Redis7.0Winx64
Go代理: go env -w GOPROXY=https://goproxy.cn
(由于用的是远程桌面连接虚拟机,故截图比较糊,主要看代码)
项目创建 创建如下的目录结构:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 GOBLOG-ADMIN ├── go.mod # go mod文件 ├── main.go # 程序入口 ├── config.yaml # 配置文件 ├── api/ # api ├── config/ # 配置项 ├── core/ # 核心库 ├── result/ # 接口访问集 ├── global/ # 全局配置文件 ├── middleware/ # 中间件 ├── model/ # 模型 ├── router/ # 路由 ├── utils/ # 工具类 └── constant/ # 系统常量
测试一下环境:
go.mod
:
1 2 3 model goblog-admin go 1.22
main.go
:
1 2 3 4 5 6 7 8 9 10 package mainimport "fmt" func main () { fmt.Println("The GoBlog-Admin has been started..." ) }
输出:
系统配置 首先引入一下 yaml
支持的包,项目根目录执行:
查看 go.mod
文件已加载其依赖:
配置 config.yaml
文件:
1 2 3 4 5 6 7 8 system: host: "0.0.0.0" port: 5001 env: debug
创建 /config/config.go
文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package configimport ( "gopkg.in/yaml.v2" "io/ioutil" ) type config struct { System system `yaml:"system"` } type system struct { Host string `yaml:"host"` Port int `yaml:"port"` Env string `yaml:"env"` } var Config *configfunc init () { yamlFile, err := ioutil.ReadFile("./config.yaml" ) if err != nil { return } yaml.Unmarshal(yamlFile, &Config) }
GoLand格式化代码快捷键 Ctrl+Alt+L
回到 main.go
文件中,打印一下配置文件测试是否读取到:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package mainimport ( "fmt" "goblog-admin/config" ) func main () { fmt.Println("The GoBlog-Admin has been started..." ) fmt.Println("系统配置文件:" , config.Config.System) }
输出:
日志配置 首先添加日志依赖:(项目根目录执行)
1 go get github.com/sirupsen/logrus
回到 config.yaml
文件中,继续写日志配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 system: host: "0.0.0.0" port: 5001 env: debug logger: level: info prefix: '[goblog-admin]' director: logger show_line: true log_in_console: true
在 /config/config.go
结构体中增加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package configimport ( "gopkg.in/yaml.v2" "io/ioutil" ) type config struct { System system `yaml:"system"` Logger logger `yaml:"logger"` } type system struct { Host string `yaml:"host"` Port int `yaml:"port"` Env string `yaml:"env"` } type logger struct { Level string `yaml:"level"` Prefix string `yaml:"prefix"` Director string `yaml:"director"` ShowLine bool `yaml:"show_line"` LogInConsole bool `yaml:"log_in_console"` } var Config *configfunc init () { yamlFile, err := ioutil.ReadFile("./config.yaml" ) if err != nil { return } yaml.Unmarshal(yamlFile, &Config) }
在 main.go
文件中打印一下测试是否读取到:
1 fmt.Println("日志配置文件:" , config.Config.Logger)
输出:
没有问题哈,接下来在核心组件目录中创建处理日志的包 /core/logrus.go
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 package coreimport ( "bytes" "fmt" "github.com/sirupsen/logrus" "goblog-admin/config" "os" "path" ) const ( red = 31 yellow = 33 blue = 36 gray = 37 ) type LogFormatter struct {}func (t *LogFormatter) Format(entry *logrus.Entry) ([]byte , error ) { var levelColor int switch entry.Level { case logrus.DebugLevel, logrus.TraceLevel: levelColor = gray case logrus.WarnLevel: levelColor = yellow case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel: levelColor = red default : levelColor = blue } var b *bytes.Buffer if entry.Buffer != nil { b = entry.Buffer } else { b = &bytes.Buffer{} } log := config.Config.Logger timestamp := entry.Time.Format("2006-01-02 15:04:05" ) if entry.HasCaller() { funcVal := entry.Caller.Function fileVal := fmt.Sprintf("%s:%d" , path.Base(entry.Caller.File), entry.Caller.Line) fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m %s %s: %s\n" , log.Prefix, timestamp, levelColor, entry.Level, fileVal, funcVal, entry.Message) } else { fmt.Fprintf(b, "%s[%s] \x1b[%dm[%s]\x1b[0m: %s\n" , log.Prefix, timestamp, levelColor, entry.Level, entry.Message) } return b.Bytes(), nil } func InitLogger () *logrus.Logger { mlog := logrus.New() mlog.SetOutput(os.Stdout) mlog.SetReportCaller(config.Config.Logger.ShowLine) mlog.SetFormatter(&LogFormatter{}) level, err := logrus.ParseLevel(config.Config.Logger.Level) if err != nil { level = logrus.InfoLevel } mlog.SetLevel(level) InitDefaultLogger() return mlog } func InitDefaultLogger () { logrus.SetOutput(os.Stdout) logrus.SetReportCaller(config.Config.Logger.ShowLine) logrus.SetFormatter(&LogFormatter{}) level, err := logrus.ParseLevel(config.Config.Logger.Level) if err != nil { level = logrus.InfoLevel } logrus.SetLevel(level) }
创建全局共享配置, global/global.go
:
1 2 3 4 5 6 7 8 9 10 package globalimport "github.com/sirupsen/logrus" var ( Log *logrus.Logger )
返回 main.go
文件,设置初始化和输出日志:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package mainimport ( "goblog-admin/core" "goblog-admin/global" ) func main () { global.Log = core.InitLogger() global.Log.Warnln("GoBlog 日志" ) global.Log.Error("GoBlog 日志" ) global.Log.Infof("GoBlog 日志" ) }
输出:
MySQL配置 首先配置MySQL的驱动和依赖以及gorm:(项目根目录执行)
1 2 go get gorm.io/driver/mysql go get gorm.io/gorm
在 config.yaml
文件中添加MySQL配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 system: host: "0.0.0.0" port: 5001 env: debug logger: level: info prefix: '[goblog-admin]' director: logger show_line: true log_in_console: true mysql: host: 127.0 .0 .1 port: 3306 db_name: goblogtest username: goblog password: goblog log_level: dev charset: utf8mb4 maxIdle: 50 maxOpen: 150
然后在 /config/config.go
文件中配置一下结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 package configimport ( "gopkg.in/yaml.v2" "io/ioutil" ) type config struct { System system `yaml:"system"` Logger logger `yaml:"logger"` Mysql mysql `yaml:"mysql"` } type system struct { Host string `yaml:"host"` Port int `yaml:"port"` Env string `yaml:"env"` } type logger struct { Level string `yaml:"level"` Prefix string `yaml:"prefix"` Director string `yaml:"director"` ShowLine bool `yaml:"show_line"` LogInConsole bool `yaml:"log_in_console"` } type mysql struct { Host string `yaml:"host"` Port int `yaml:"port"` DbName string `yaml:"db_name"` Username string `yaml:"username"` Password string `yaml:"password"` LogLevel string `yaml:"log_level"` Charset string `yaml:"charset"` MaxIdle int `yaml:"maxIdle"` MaxOpen int `yaml:"maxOpen"` } var Config *configfunc init () { yamlFile, err := ioutil.ReadFile("./config.yaml" ) if err != nil { return } yaml.Unmarshal(yamlFile, &Config) }
写好之后在 main.go
文件中尝试打印一下,看看是否读取到了配置信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport ( "goblog-admin/config" "goblog-admin/core" "goblog-admin/global" ) func main () { global.Log = core.InitLogger() global.Log.Infof("MySQL配置:%s" , config.Config.Mysql) }
输出:
配置信息虽然输出了,但是有数据没有正常显示,这是因为全部用的 %s
字符串输出,先不用管,继续配置MySQL的连接
创建 /core/mysql.go
文件,作为MySQL的启动和连接组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package coreimport ( "fmt" "goblog-admin/config" "goblog-admin/global" "gorm.io/driver/mysql" "gorm.io/gorm" "gorm.io/gorm/logger" ) var Db *gorm.DBfunc MysqlInit () error { var err error var dbConfig = config.Config.Mysql url := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=Local" , dbConfig.Username, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.DbName, dbConfig.Charset) Db, err = gorm.Open(mysql.Open(url), &gorm.Config{ Logger: logger.Default.LogMode(logger.Info), DisableForeignKeyConstraintWhenMigrating: true , }) if err != nil { return err } if Db.Error != nil { return Db.Error } sqlDb, err := Db.DB() sqlDb.SetMaxIdleConns(dbConfig.MaxIdle) sqlDb.SetMaxOpenConns(dbConfig.MaxOpen) global.Log.Infof("[MySQL] 数据库连接成功" ) return nil }
然后在 main.go
中增加数据库初始化语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "goblog-admin/core" "goblog-admin/global" ) func main () { global.Log = core.InitLogger() core.MysqlInit() }
然后创建一个数据库,用户名和密码与配置文件中的信息保持一致,运行主程序测试是否连接成功:
Redis配置 添加一下redis的依赖:(项目根目录执行)
1 go get github.com/go-redis/redis/v8
在 config.yaml
文件中增加redis的配置信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 system: host: "0.0.0.0" port: 5001 env: debug logger: level: info prefix: '[goblog-admin]' director: logger show_line: true log_in_console: true mysql: host: 127.0 .0 .1 port: 3306 db_name: goblogtest username: goblogtest password: goblogtest log_level: dev charset: utf8mb4 maxIdle: 50 maxOpen: 150 redis: address: 127.0 .0 .1 :6379 password: db: 0
继续在 /config/config.go
文件中创建redis配置信息的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 package configimport ( "gopkg.in/yaml.v2" "io/ioutil" ) type config struct { System system `yaml:"system"` Logger logger `yaml:"logger"` Mysql mysql `yaml:"mysql"` Redis redis `yaml:"redis"` } type system struct { Host string `yaml:"host"` Port int `yaml:"port"` Env string `yaml:"env"` } type logger struct { Level string `yaml:"level"` Prefix string `yaml:"prefix"` Director string `yaml:"director"` ShowLine bool `yaml:"show_line"` LogInConsole bool `yaml:"log_in_console"` } type mysql struct { Host string `yaml:"host"` Port int `yaml:"port"` DbName string `yaml:"db_name"` Username string `yaml:"username"` Password string `yaml:"password"` LogLevel string `yaml:"log_level"` Charset string `yaml:"charset"` MaxIdle int `yaml:"maxIdle"` MaxOpen int `yaml:"maxOpen"` } type redis struct { Address string `yaml:"address"` Password string `yaml:"password"` Db int `yaml:"db"` } var Config *configfunc init () { yamlFile, err := ioutil.ReadFile("./config.yaml" ) if err != nil { return } yaml.Unmarshal(yamlFile, &Config) }
然后在 main.go
中打印配置信息测试一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package mainimport ( "goblog-admin/config" "goblog-admin/core" "goblog-admin/global" ) func main () { global.Log = core.InitLogger() core.MysqlInit() global.Log.Infof("Redis配置:" , config.Config.Redis) }
输出:
在 /global/global.go
文件里拿到redis的上下文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package globalimport ( "context" "github.com/sirupsen/logrus" ) var ( Log *logrus.Logger Ctx = context.Background() )
注册一个 /core/redis.go
组件文件,完成redis初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package coreimport ( "github.com/go-redis/redis/v8" "goblog-admin/config" "goblog-admin/global" ) var ( RedisDb *redis.Client ) func RedisInit () error { RedisDb = redis.NewClient(&redis.Options{ Addr: config.Config.Redis.Address, Password: config.Config.Redis.Password, DB: config.Config.Redis.Db}) _, err := RedisDb.Ping(global.Ctx).Result() if err != nil { return err } global.Log.Infof("[Redis] Redis 连接成功" ) return nil }
在 main.go
中初始化redis:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package mainimport ( "goblog-admin/core" "goblog-admin/global" ) func main () { global.Log = core.InitLogger() core.MysqlInit() core.RedisInit() }
运行一下,输出:
初始化路由 首先添加 gin 框架支持:(项目根目录下执行)
1 go get github.com/gin-gonic/gin
创建路由配置文件 /router/router.go
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package routerimport ( "github.com/gin-gonic/gin" "goblog-admin/config" ) func RouterInit () *gin.Engine { gin.SetMode(config.Config.System.Env) router := gin.New() router.Use(gin.Recovery()) return router } func register (router *gin.Engine) { }
然后在 main.go
中启动路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package mainimport ( "fmt" "goblog-admin/config" "goblog-admin/core" "goblog-admin/global" "goblog-admin/router" ) func main () { global.Log = core.InitLogger() core.MysqlInit() core.RedisInit() router := router.RouterInit() address := fmt.Sprintf("%s:%d" , config.Config.System.Host, config.Config.System.Port) global.Log.Infof("系统启动成功,运行在: %s" , address) router.Run(address) }
运行测试一下,输出:
通用返回结构 所谓的返回结构就是指请求返回的数据结构,一般来说由三部分组成,code、data以及message组成
code就是状态码,data就是数据,message就是提示信息
创建 /result/code.go
文件,先实现状态码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package resulttype Codes struct { Message map [uint ]string Success uint Failed uint } var ApiCode = &Codes{ Success: 200 , Failed: 501 , } func init () { ApiCode.Message = map [uint ]string { ApiCode.Success: "成功" , ApiCode.Failed: "失败" , } } func (c *Codes) GetMessage(code uint ) string { message, ok := c.Message[code] if !ok { return "" } return message }
再创建一个 /result/result.go
文件,用来定义通用返回结构的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package resultimport ( "github.com/gin-gonic/gin" "net/http" ) type Result struct { Code int `json:"code"` Message string `json:"message"` Data interface {} `json:"data"` } func Success (c *gin.Context, data interface {}) { if data == nil { data = gin.H{} } res := Result{} res.Code = int (ApiCode.Success) res.Message = ApiCode.GetMessage(ApiCode.Success) res.Data = data c.JSON(http.StatusOK, res) } func Failed (c *gin.Context, code int , message string ) { res := Result{} res.Code = code res.Message = message res.Data = gin.H{} c.JSON(http.StatusOK, res) }
写好之后创建一个test的API,来测试一下 /api/test.go
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package apiimport ( "github.com/gin-gonic/gin" "goblog-admin/result" ) func Success (c *gin.Context) { result.Success(c, 200 ) } func Failed (c *gin.Context) { result.Failed(c, int (result.ApiCode.Failed), result.ApiCode.GetMessage(result.ApiCode.Failed)) }
然后在 router/router.go
文件中增加我们的测试api,同时注册路由:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package routerimport ( "github.com/gin-gonic/gin" "goblog-admin/api" "goblog-admin/config" ) func RouterInit () *gin.Engine { gin.SetMode(config.Config.System.Env) router := gin.New() router.Use(gin.Recovery()) register(router) return router } func register (router *gin.Engine) { router.GET("/api/success" , api.Success) router.GET("/api/failed" , api.Failed) }
然后运行程序测试一下:
后台输出了api的相关信息:
可以看到两个api都没问题
接入SWAG 首先先加载依赖:(根目录下执行)
1 2 go get github.com/swaggo/files go get github.com/swaggo/gin-swagger
在 main.go
中加入swag信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package mainimport ( "fmt" "goblog-admin/config" "goblog-admin/core" "goblog-admin/global" "goblog-admin/router" ) func main () { global.Log = core.InitLogger() core.MysqlInit() core.RedisInit() router := router.RouterInit() address := fmt.Sprintf("%s:%d" , config.Config.System.Host, config.Config.System.Port) global.Log.Infof("系统启动成功,运行在: %s" , address) router.Run(address) }
然后在路由文件 router/router.go
中引入swag:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 package routerimport ( "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" ginSwagger "github.com/swaggo/gin-swagger" "goblog-admin/api" "goblog-admin/config" ) func RouterInit () *gin.Engine { gin.SetMode(config.Config.System.Env) router := gin.New() router.Use(gin.Recovery()) register(router) return router } func register (router *gin.Engine) { router.GET("/swagger/*any" , ginSwagger.WrapHandler(swaggerFiles.Handler)) router.GET("/api/success" , api.Success) router.GET("/api/failed" , api.Failed) }
然后在刚才写的test路由文件 /api/test.go
中加入注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package apiimport ( "github.com/gin-gonic/gin" "goblog-admin/result" ) func Success (c *gin.Context) { result.Success(c, 200 ) } func Failed (c *gin.Context) { result.Failed(c, int (result.ApiCode.Failed), result.ApiCode.GetMessage(result.ApiCode.Failed)) }
然后在项目根目录下执行swag初始化:
如果失败请尝试 go get -u github.com/swaggo/swag/cmd/swag
命令安装swag命令行工具
如果还不行,请更换安装方式:go install github.com/swaggo/swag/cmd/swag@latest
后再添加系统环境变量 %GOPATH%\bin
(实测这个可以解决问题)
初始化完成之后发现根目录中多了一个 docs
目录,回到 main.go
文件中,将该目录加入import扫描目录:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package mainimport ( "fmt" "goblog-admin/config" "goblog-admin/core" _ "goblog-admin/docs" "goblog-admin/global" "goblog-admin/router" ) func main () { global.Log = core.InitLogger() core.MysqlInit() core.RedisInit() router := router.RouterInit() address := fmt.Sprintf("%s:%d" , config.Config.System.Host, config.Config.System.Port) global.Log.Infof("系统启动成功,运行在: %s" , address) router.Run(address) }
然后启动项目测试一下:(访问 127.0.0.1:5001/swagger/index.html
)
如果接口显示不出来,检查刚才加入的那些注释是不是不对,然后重新 swag init
一下!
点击 Try it out
, Execute
测试一下,没毛病:
后续 后续的文章我放在了文档集里:
地址:https://docs.frp.imbhj.com
但是由于教程里代码不全,所以写了一半烂尾了,不过过程中还是可以学习到一些有用的东西