开发环境:

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
// 启动程序
// @author DaBaiLuoBo

package main

import "fmt"

func main() {
fmt.Println("The GoBlog-Admin has been started...")
}

输出:

系统配置

首先引入一下 yaml 支持的包,项目根目录执行:

1
go get gopkg.in/yaml.v2

查看 go.mod 文件已加载其依赖:

配置 config.yaml 文件:

1
2
3
4
5
6
7
8
# 系统配置
system:
# 运行地址:本地
host: "0.0.0.0"
# 端口
port: 5001
# 启动环境:debug/release
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
// 读取配置文件
// @author DaBaiLuoBo

package config

import (
"gopkg.in/yaml.v2"
"io/ioutil"
)

// config 总配置文件
type config struct {
System system `yaml:"system"`
}

// 系统配置
type system struct {
Host string `yaml:"host"`
Port int `yaml:"port"`
Env string `yaml:"env"`
}

var Config *config

// init 初始化配置
func init() {
yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
// 如果为空,直接返回错误内容
if err != nil {
return
}
// 接收配置文件,存入 Config 中
yaml.Unmarshal(yamlFile, &Config)
}

GoLand格式化代码快捷键 Ctrl+Alt+L

回到 main.go 文件中,打印一下配置文件测试是否读取到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 启动程序
// @author DaBaiLuoBo

package main

import (
"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
# 启动环境:debug/release
env: debug

# logger 日志配置
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
// 读取配置文件
// @author DaBaiLuoBo

package config

import (
"gopkg.in/yaml.v2"
"io/ioutil"
)

// config 总配置文件
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"`
}

// logger 日志配置
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 *config

// init 初始化配置
func init() {
yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
// 如果为空,直接返回错误内容
if err != nil {
return
}
// 接收配置文件,存入 Config 中
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
// 日志处理
// @author DaBaiLuoBo

package core

import (
"bytes"
"fmt"
"github.com/sirupsen/logrus"
"goblog-admin/config"
"os"
"path"
)

// 颜色
const (
red = 31
yellow = 33
blue = 36
gray = 37
)

type LogFormatter struct{}

// Format 实现 Formatter (entry *logrus.Entry) ([]byte, error) 接口
func (t *LogFormatter) Format(entry *logrus.Entry) ([]byte, error) {
// 根据不同的 level 展示不同的颜色
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
}

// InitLogger 初始化
func InitLogger() *logrus.Logger {
mlog := logrus.New() // 新建一个实例
mlog.SetOutput(os.Stdout) // 设置输出类型
mlog.SetReportCaller(config.Config.Logger.ShowLine) // 开启返回函数名和行号
mlog.SetFormatter(&LogFormatter{}) // 设置自己定义的 Formatter
level, err := logrus.ParseLevel(config.Config.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
mlog.SetLevel(level) // 设置最低的 level
InitDefaultLogger() // 不注释的话即启用全局 log
return mlog
}

// InitDefaultLogger 全局 log
func InitDefaultLogger() {
logrus.SetOutput(os.Stdout) // 设置输出类型
logrus.SetReportCaller(config.Config.Logger.ShowLine) // 开启返回函数名和行号
logrus.SetFormatter(&LogFormatter{}) // 设置自己定义的 Formatter
level, err := logrus.ParseLevel(config.Config.Logger.Level)
if err != nil {
level = logrus.InfoLevel
}
logrus.SetLevel(level) // 设置最低的 level
}

创建全局共享配置, global/global.go

1
2
3
4
5
6
7
8
9
10
// 全局共享配置
// @author DaBaiLuoBo

package global

import "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
// 启动程序
// @author DaBaiLuoBo

package main

import (
"goblog-admin/core"
"goblog-admin/global"
)

func main() {
//fmt.Println("The GoBlog-Admin has been started...")
//fmt.Println("系统配置文件:", config.Config.System)
//fmt.Println("日志配置文件:", config.Config.Logger)

// 初始化 logger
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
# 启动环境:debug/release
env: debug

# logger 日志配置
logger:
# 日志等级
level: info
# 日志前缀
prefix: '[goblog-admin]'
# 日志文件路径
director: logger
# 显示行号
show_line: true
# 是否打印在控制台
log_in_console: true

# MySQL 配置
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
// 读取配置文件
// @author DaBaiLuoBo

package config

import (
"gopkg.in/yaml.v2"
"io/ioutil"
)

// config 总配置文件
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"`
}

// logger 日志配置
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"`
}

// MySQL 配置
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 *config

// init 初始化配置
func init() {
yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
// 如果为空,直接返回错误内容
if err != nil {
return
}
// 接收配置文件,存入 Config 中
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
// 启动程序
// @author DaBaiLuoBo

package main

import (
"goblog-admin/config"
"goblog-admin/core"
"goblog-admin/global"
)

func main() {
//fmt.Println("The GoBlog-Admin has been started...")
//fmt.Println("系统配置文件:", config.Config.System)
//fmt.Println("日志配置文件:", config.Config.Logger)

// 初始化 logger
global.Log = core.InitLogger()
// 输出日志
//global.Log.Warnln("GoBlog 日志")
//global.Log.Error("GoBlog 日志")
//global.Log.Infof("GoBlog 日志")
// 测试一下输出MySQL配置信息
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
// MySQL 启动和连接组件(初始化配置)
// @author DaBaiLuoBo

package core

import (
"fmt"
"goblog-admin/config"
"goblog-admin/global"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)

// 创建一个数据库,指针指向 gorm 组件
var Db *gorm.DB

// 数据库初始化
func MysqlInit() error {
var err error
// 拿到 MySQL 配置信息
var dbConfig = config.Config.Mysql
// 格式化 url 字符串,含 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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"goblog-admin/core"
"goblog-admin/global"
)

func main() {
// 初始化 logger
global.Log = core.InitLogger()

// 初始化 MySQL
core.MysqlInit()

//fmt.Println("The GoBlog-Admin has been started...")
//fmt.Println("系统配置文件:", config.Config.System)
//fmt.Println("日志配置文件:", config.Config.Logger)

// 测试输出日志
//global.Log.Warnln("GoBlog 日志")
//global.Log.Error("GoBlog 日志")
//global.Log.Infof("GoBlog 日志")

// 测试一下输出MySQL配置信息
//global.Log.Infof("MySQL配置:%s", config.Config.Mysql)
}

然后创建一个数据库,用户名和密码与配置文件中的信息保持一致,运行主程序测试是否连接成功:

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
# 启动环境:debug/release
env: debug

# logger 日志配置
logger:
# 日志等级
level: info
# 日志前缀
prefix: '[goblog-admin]'
# 日志文件路径
director: logger
# 显示行号
show_line: true
# 是否打印在控制台
log_in_console: true

# MySQL 配置
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 配置
redis:
# redis 访问地址
address: 127.0.0.1:6379
# redis 连接密码
password:
# 数据库(默认为0,第一个)
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
// 读取配置文件
// @author DaBaiLuoBo

package config

import (
"gopkg.in/yaml.v2"
"io/ioutil"
)

// config 总配置文件
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"`
}

// logger 日志配置
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"`
}

// MySQL 配置
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"`
}

// Redis 配置
type redis struct {
Address string `yaml:"address"`
Password string `yaml:"password"`
Db int `yaml:"db"`
}

var Config *config

// init 初始化配置
func init() {
yamlFile, err := ioutil.ReadFile("./config.yaml") // 注意这里的路径,是和根目录同级
// 如果为空,直接返回错误内容
if err != nil {
return
}
// 接收配置文件,存入 Config 中
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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"goblog-admin/config"
"goblog-admin/core"
"goblog-admin/global"
)

func main() {
// 初始化 logger
global.Log = core.InitLogger()

// 初始化 MySQL
core.MysqlInit()

//fmt.Println("The GoBlog-Admin has been started...")
//fmt.Println("系统配置文件:", config.Config.System)
//fmt.Println("日志配置文件:", config.Config.Logger)

// 测试输出日志
//global.Log.Warnln("GoBlog 日志")
//global.Log.Error("GoBlog 日志")
//global.Log.Infof("GoBlog 日志")

// 测试一下输出MySQL配置信息
//global.Log.Infof("MySQL配置:%s", config.Config.Mysql)
// 测试一下输出Redis配置信息
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
// 全局共享配置
// @author DaBaiLuoBo

package global

import (
"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
// Redis 启动和连接组件(初始化配置)
// @author DaBaiLuoBo

package core

import (
"github.com/go-redis/redis/v8"
"goblog-admin/config"
"goblog-admin/global"
)

// 和 MySQL 一样,先拿到 Redis 客户端
var (
RedisDb *redis.Client
)

// 初始化 Redis 连接
func RedisInit() error {
RedisDb = redis.NewClient(&redis.Options{
Addr: config.Config.Redis.Address,
Password: config.Config.Redis.Password,
DB: config.Config.Redis.Db})
// 接收 redis 连接的上下文
_, 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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"goblog-admin/core"
"goblog-admin/global"
)

func main() {
// 初始化 logger
global.Log = core.InitLogger()
// 初始化 MySQL
core.MysqlInit()
// 初始化 Redis
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
// 路由初始化以及注册
// @author DaBaiLuoBo

package router

import (
"github.com/gin-gonic/gin"
"goblog-admin/config"
)

// 先拿到Gin框架
func RouterInit() *gin.Engine {
// 设置启动模式
gin.SetMode(config.Config.System.Env)
// 新建路由
router := gin.New()
// 设置跌机时恢复
router.Use(gin.Recovery())
return router
}

// Register 路由接口
func register(router *gin.Engine) {
// todo 后续的所有接口 url 将在此配置

}

然后在 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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"fmt"
"goblog-admin/config"
"goblog-admin/core"
"goblog-admin/global"
"goblog-admin/router"
)

func main() {
// 初始化 logger
global.Log = core.InitLogger()
// 初始化 MySQL
core.MysqlInit()
// 初始化 Redis
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
// 状态码/状态信息
//@author DaiBaiLuoBo

package result

// Codes 定义状态
type Codes struct {
Message map[uint]string
Success uint
Failed uint
}

// ApiCode 状态码
var ApiCode = &Codes{
Success: 200,
Failed: 501,
}

// 状态信息初始化
func init() {
ApiCode.Message = map[uint]string{
ApiCode.Success: "成功",
ApiCode.Failed: "失败",
}
}

// GetMessage 供给外部调用状态信息
func (c *Codes) GetMessage(code uint) string {
message, ok := c.Message[code]
// 如果不 ok,返回空,ok 则返回 message
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
// 结构数据定义
// @author DaiBaiLuoBo

package result

import (
"github.com/gin-gonic/gin"
"net/http"
)

// Result 结构体
type Result struct {
Code int `json:"code"` // 状态码
Message string `json:"message"` // 提示信息
Data interface{} `json:"data"` // 返回数据
}

// Success 成功
func Success(c *gin.Context, data interface{}) {
if data == nil {
data = gin.H{}
}
// 接收 Result
res := Result{}
res.Code = int(ApiCode.Success)
res.Message = ApiCode.GetMessage(ApiCode.Success)
// data 数据直接从上面拿过来
res.Data = data
// 进一步返回给前端
c.JSON(http.StatusOK, res)
}

// Failed 失败
func Failed(c *gin.Context, code int, message string) {
// 和上面的大同小异,先接收 Result
res := Result{}
res.Code = code
res.Message = message
// 错误通过 gin 来处理
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
// 测试 API
// @author DaiBaiLuoBo

package api

import (
"github.com/gin-gonic/gin"
"goblog-admin/result"
)

// Success 成功测试
// @router /api/success [get]
func Success(c *gin.Context) {
result.Success(c, 200)
}

// Failed 失败测试
// @router /api/failed [get]
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
// 路由初始化以及注册
// @author DaBaiLuoBo

package router

import (
"github.com/gin-gonic/gin"
"goblog-admin/api"
"goblog-admin/config"
)

// RouterInit 初始化先拿到Gin框架
func RouterInit() *gin.Engine {
// 设置启动模式
gin.SetMode(config.Config.System.Env)
// 新建路由
router := gin.New()
// 设置跌机时恢复
router.Use(gin.Recovery())
// Register 注册
register(router)
// 返回路由
return router
}

// Register 路由接口
func register(router *gin.Engine) {
// todo 后续的所有接口 url 将在此配置
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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"fmt"
"goblog-admin/config"
"goblog-admin/core"
"goblog-admin/global"
"goblog-admin/router"
)

// @title GoBlog运营后台
// @version 24.04.16.1
// @description GoBlog运营后台API接口文档
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
func main() {
// 初始化 logger
global.Log = core.InitLogger()
// 初始化 MySQL
core.MysqlInit()
// 初始化 Redis
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
// 路由初始化以及注册
// @author DaBaiLuoBo

package router

import (
"github.com/gin-gonic/gin"
swaggerFiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
"goblog-admin/api"
"goblog-admin/config"
)

// RouterInit 初始化先拿到Gin框架
func RouterInit() *gin.Engine {
// 设置启动模式
gin.SetMode(config.Config.System.Env)
// 新建路由
router := gin.New()
// 设置跌机时恢复
router.Use(gin.Recovery())
// Register 注册
register(router)
// 返回路由
return router
}

// Register 路由接口
func register(router *gin.Engine) {
// todo 后续的所有接口 url 将在此配置
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
// 测试 API
// @author DaiBaiLuoBo

package api

import (
"github.com/gin-gonic/gin"
"goblog-admin/result"
)

// Success 成功测试
// @Summary 成功测试接口
// @Tags 测试相关接口
// @Produce json
// @Description 成功测试接口
// @Success 200 {object} result.Result
// @router /api/success [get]
func Success(c *gin.Context) {
result.Success(c, 200)
}

// Failed 失败测试
// @Summary 失败测试接口
// @Tags 测试相关接口
// @Produce json
// @Description 失败测试接口
// @Success 200 {object} result.Result
// @router /api/failed [get]
func Failed(c *gin.Context) {
result.Failed(c, int(result.ApiCode.Failed), result.ApiCode.GetMessage(result.ApiCode.Failed))
}

然后在项目根目录下执行swag初始化:

1
swag init

如果失败请尝试 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
// 主启动程序
// @author DaBaiLuoBo

package main

import (
"fmt"
"goblog-admin/config"
"goblog-admin/core"
_ "goblog-admin/docs" // swag 文档目录
"goblog-admin/global"
"goblog-admin/router"
)

// @title GoBlog运营后台
// @version 24.04.16.1
// @description GoBlog运营后台API接口文档
// @securityDefinitions.apikey ApiKeyAuth
// @in header
// @name Authorization
func main() {
// 初始化 logger
global.Log = core.InitLogger()
// 初始化 MySQL
core.MysqlInit()
// 初始化 Redis
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 outExecute 测试一下,没毛病: