引言
1.开发环境搭建
2.项目环境搭建
3.golang项目基础框架-前篇
续上之前的节奏,继续框架整合,本篇将形成一个正常可运行golang web 项目。
从数据库开始,引入xorm框架,xorm封装了基本单表操作,又提供可定制化的SQL编写API。
mysql与xorm库导入
cd core
go get -u github.com/go-sql-driver/mysql
go get -u github.com/go-xorm/xorm
cd config
touch database.go
database.go
定义配置类与初始化函数
package config
import (
"core/system"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/go-xorm/xorm"
)
type Database struct {
Driver string `yaml:"driver"`
Url string `yaml:"url"`
Username string `yaml:"username"`
Password string `yaml:"password"`
MaxIdleConns int `yaml:"maxIdleConns"`
MaxOpenConns int `yaml:"maxOpenConns"`
ShowSQL bool `yaml:"showSQL"`
}
func (e *Database) Init() {
if e == nil {
return
}
engine, err := xorm.NewEngine(e.Driver, fmt.Sprintf("%s:%s@%s", e.Username, e.Password, e.Url))
if err != nil {
fmt.Println(err.Error())
}
err = engine.Ping()
if err != nil {
fmt.Printf("connect ping failed: %v", err)
return
}
//最大空闲连接数
engine.SetMaxIdleConns(e.MaxIdleConns)
//最大连接数
engine.SetMaxOpenConns(e.MaxOpenConns)
//是否打印SQL
engine.ShowSQL(e.ShowSQL)
system.SetXormEngine(engine)
}
config.go 与 application.go
对应增加database配置类与xorm客户端类
package config
import "core/system"
type Config struct {
Nacos *Nacos `mapstructure:""`
Logger *Logger `yaml:"logger"`
Dasebase *Database `yaml:"database"`
}
var SystemConfig = new(Config)
// 设置参数
func Setup() {
env := system.GetEnv()
//nacos配置
profile := profiles[env]
SystemConfig.Nacos = new(Nacos)
SystemConfig.Nacos.Username = profile.Username
SystemConfig.Nacos.Password = profile.Password
SystemConfig.Nacos.ServerAddr = profile.ServerAddr
SystemConfig.Nacos.Namespace = profile.Namespace
SystemConfig.Nacos.SharedDataIds = profile.SharedDataIds
SystemConfig.Nacos.RefreshableDataIds = profile.RefreshableDataIds
//配置中心
SystemConfig.Nacos.Init()
//日志
SystemConfig.Logger.Init()
//数据库
SystemConfig.Dasebase.Init()
}
package system
import (
"github.com/go-xorm/xorm"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/sirupsen/logrus"
)
type Application struct {
env string
name string
iConfigClient config_client.IConfigClient
logger *logrus.Logger
xormEngine *xorm.Engine
}
func SetEnv(env string) {
if len(application.env) == 0 {
application.env = env
}
}
func GetEnv() string {
return application.env
}
func SetName(name string) {
if len(application.name) == 0 {
application.name = name
}
}
func GetName() string {
return application.name
}
func SetIConfigClient(configClient config_client.IConfigClient) {
if application.iConfigClient == nil {
application.iConfigClient = configClient
}
}
func GetIConfigClient() config_client.IConfigClient {
return application.iConfigClient
}
func SetLogger(logger *logrus.Logger) {
if application.logger == nil {
application.logger = logger
}
}
func GetLogger() *logrus.Logger {
return application.logger
}
func SetXormEngine(engine *xorm.Engine) {
if application.xormEngine == nil {
application.xormEngine = engine
}
}
func GetXormEngine() *xorm.Engine {
return application.xormEngine
}
var application = new(Application)
xorm的代码片段示例
示例中e.db代表xorm.Engine
// 新增/更新
func (e *AdminMenuDb) SaveOrUpdateAdminMenu(adminMenu *model.AdminMenu) error {
var now = types.Time(time.Now())
menuId := adminMenu.MenuId
name := adminMenu.Name
path := adminMenu.Path
if menuId == 0 {
count, err := e.db.Where("name = ? and path = ?", name, path).Count(&model.AdminMenu{})
if err != nil {
return err
}
if count == 0 {
adminMenu.CreatedAt = now
_, err := e.db.Insert(adminMenu)
if err != nil {
return err
}
}
} else {
adminMenu.UpdatedAt = now
_, err := e.db.ID(menuId).Update(adminMenu)
if err != nil {
return err
}
}
_, err := e.db.Where("name = ? and path = ?", name, path).Get(adminMenu)
if err != nil {
return err
}
return nil
}
// 指定字段更新
func (e *UserDb) UpdateUser(userId int64, avatar string, nickname string, phone string, email string, gender int8, description string) error {
var now = types.Time(time.Now())
_, err := e.db.ID(userId).Cols("avatar", "nickname", "phone", "email", "gender", "description").Update(&model.User{
Avatar: avatar,
Nickname: nickname,
Phone: phone,
Email: email,
Gender: gender,
Description: description,
UpdatedAt: now,
})
return err
}
// 删除
func (e *AdminMenuDb) DelAdminMenu(menuId int64) error {
_, err := e.db.ID(menuId).Delete(&model.AdminMenu{})
if err != nil {
return err
}
return nil
}
// 动态条件查询
func (e *AssetDb) GetAssetByCategoryId(assetType int8, categoryId int64, pageNum int32, pageSize int32) ([]model.Asset, error) {
var assets []model.Asset
session := e.db.Where("find_in_set(?, category_ids)", categoryId)
if assetType != 0 {
session = session.And("asset_type = ?", assetType)
}
err := session.Limit(int(pageSize), int((pageNum-1)*pageSize)).OrderBy("updated_at desc").Find(&assets)
if err != nil {
return nil, err
}
return assets, nil
}
// 复杂sql查询
func (e *AssetDb) GetAssetForUpgrade(limit int64) ([]model.Asset, error) {
var assets []model.Asset
err := e.db.SQL(
`
select
a.asset_id,
a.asset_key,
a.asset_type,
a.title,
a.cover,
a.upgrade_chapter,
a.category_ids,
a.author_ids,
a.chapter_id,
a.obj_id,
a.created_at,
a.updated_at
from asset as a
where
a.created_at between date_sub(now(), interval 7 day) and now()
group by a.obj_id
order by a.updated_at desc
limit ?
`, limit).Find(&assets)
if err != nil {
return nil, err
}
return assets, nil
}
// 单表拼接sql查询
func (e *ComicDb) GetComicMapByIds(comicIds []int64) (map[int64]model.Comic, error) {
if len(comicIds) == 0 {
return nil, nil
}
var comics []model.Comic
var builder strings.Builder
var params = make([]interface{}, 0)
builder.WriteString("comic_id in (")
for i, length := 0, len(comicIds); i < length; i++ {
builder.WriteString("?")
params = append(params, comicIds[i])
if i != length-1 {
builder.WriteString(",")
}
}
builder.WriteString(")")
err := e.db.Where(builder.String(), params...).Find(&comics)
if err != nil {
return nil, err
}
if len(comics) == 0 {
return nil, nil
}
var comicMap = make(map[int64]model.Comic, 0)
for _, v := range comics {
if v.ComicId != 0 {
comicMap[v.ComicId] = v
}
}
return comicMap, nil
}
// count
func (e *AccountDb) IsExistForUsername(username string, password string) (bool, error) {
count, err := e.db.Where("(username = ? or phone = ? or email = ?) and password = ? and status = ?", username, username, username, password, enums.ACCOUNT_STATUS_OF_ENABLE).Count(&model.Account{})
if err != nil {
return false, err
}
if count == 0 {
return false, nil
}
return count == 1, nil
}
minio整合
cd core
go get -u github.com/minio/minio-go/v7
cd config
touch minio.go
创建配置类与初始化函数
minio.go
package config
import (
"core/system"
"core/utils"
"fmt"
"github.com/minio/minio-go/v7"
"github.com/minio/minio-go/v7/pkg/credentials"
)
type Minio struct {
Endpoint string `yaml:"endpoint"`
AccessKeyID string `yaml:"accessKeyId"`
SecretAccessKey string `yaml:"secretAccessKey"`
Secure bool `yaml:"secure"`
}
func (e *Minio) Init() {
if e == nil {
return
}
client, err := minio.New(e.Endpoint, &minio.Options{
Creds: credentials.NewStaticV4(e.AccessKeyID, e.SecretAccessKey, utils.EMPTY),
Secure: e.Secure,
})
if err != nil {
fmt.Println(err.Error())
return
}
system.SetMinioClient(client)
}
config.go 与 application.go
package config
import "core/system"
type Config struct {
Nacos *Nacos `mapstructure:""`
Logger *Logger `yaml:"logger"`
Dasebase *Database `yaml:"database"`
Minio *Minio `yaml:"minio"`
}
var SystemConfig = new(Config)
// 设置参数
func Setup() {
env := system.GetEnv()
//nacos配置
profile := profiles[env]
SystemConfig.Nacos = new(Nacos)
SystemConfig.Nacos.Username = profile.Username
SystemConfig.Nacos.Password = profile.Password
SystemConfig.Nacos.ServerAddr = profile.ServerAddr
SystemConfig.Nacos.Namespace = profile.Namespace
SystemConfig.Nacos.SharedDataIds = profile.SharedDataIds
SystemConfig.Nacos.RefreshableDataIds = profile.RefreshableDataIds
//配置中心
SystemConfig.Nacos.Init()
//日志
SystemConfig.Logger.Init()
//数据库
SystemConfig.Dasebase.Init()
//文件储存
SystemConfig.Minio.Init()
}
package system
import (
"github.com/go-xorm/xorm"
"github.com/minio/minio-go/v7"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/sirupsen/logrus"
)
type Application struct {
env string
name string
iConfigClient config_client.IConfigClient
logger *logrus.Logger
xormEngine *xorm.Engine
minioClient *minio.Client
}
func SetEnv(env string) {
if len(application.env) == 0 {
application.env = env
}
}
func GetEnv() string {
return application.env
}
func SetName(name string) {
if len(application.name) == 0 {
application.name = name
}
}
func GetName() string {
return application.name
}
func SetIConfigClient(configClient config_client.IConfigClient) {
if application.iConfigClient == nil {
application.iConfigClient = configClient
}
}
func GetIConfigClient() config_client.IConfigClient {
return application.iConfigClient
}
func SetLogger(logger *logrus.Logger) {
if application.logger == nil {
application.logger = logger
}
}
func GetLogger() *logrus.Logger {
return application.logger
}
func SetXormEngine(engine *xorm.Engine) {
if application.xormEngine == nil {
application.xormEngine = engine
}
}
func GetXormEngine() *xorm.Engine {
return application.xormEngine
}
func SetMinioClient(client *minio.Client) {
if application.minioClient == nil {
application.minioClient = client
}
}
func GetMinioClient() *minio.Client {
return application.minioClient
}
var application = new(Application)
因对象存储存在变更情况,定义一套通用的数据结构与api。
创建常量类 storage.go
cd core
mkdir constant
cd constant
touch storage.go
package constant
const (
MINIO = "minio"
OSS = "oss"
COS = "cos"
)
开始定义文件api结构
cd core
mkdir file
cd file
touch file_template.go
touch minio_template.go
file_template.go
package file
import (
"core/constant"
"core/system"
"time"
)
type FileTemplate interface {
CreateBucket(string) error
ListObjects(string) ([]FileObjectInfo, error)
PutObject(string, string, []byte) (*FileObjectInfo, error)
PresignedGetObject(string, string, map[string]string, time.Duration) (string, error)
}
type FileObjectInfo struct {
Name string `json:"name"`
LastModified time.Time `json:"lastModified"`
Size int64 `json:"size"`
Expires time.Time `json:"expires"`
ContentType string `json:"contentType"`
}
func NewFileTemplate(storage string) FileTemplate {
var fileTemplate FileTemplate
if storage == constant.MINIO {
fileTemplate = NewMinioTemplate(system.GetMinioClient())
}
return fileTemplate
}
minio_template.go
package file
import (
"bytes"
"context"
"net/url"
"time"
"github.com/minio/minio-go/v7"
)
type MinioTemplate struct {
minioClient *minio.Client
}
var _ FileTemplate = &MinioTemplate{}
func NewMinioTemplate(minioClient *minio.Client) *MinioTemplate {
return &MinioTemplate{minioClient}
}
func (e *MinioTemplate) CreateBucket(bucketName string) error {
ok, err := e.minioClient.BucketExists(context.Background(), bucketName)
if err != nil {
return err
}
if !ok {
err := e.minioClient.MakeBucket(context.Background(), bucketName, minio.MakeBucketOptions{})
if err != nil {
return err
}
}
return nil
}
func (e *MinioTemplate) ListObjects(bucketName string) ([]FileObjectInfo, error) {
objectInfos := e.minioClient.ListObjects(context.Background(), bucketName, minio.ListObjectsOptions{})
var fileInfos = make([]FileObjectInfo, 0)
for objectInfo := range objectInfos {
fileInfos = append(fileInfos, FileObjectInfo{
Name: objectInfo.Key,
ContentType: objectInfo.ContentType,
Size: objectInfo.Size,
LastModified: objectInfo.LastModified,
Expires: objectInfo.Expires,
})
}
return fileInfos, nil
}
func (e *MinioTemplate) PutObject(bucketName string, objectName string, data []byte) (*FileObjectInfo, error) {
e.CreateBucket(bucketName)
uploadInfo, err := e.minioClient.PutObject(context.Background(), bucketName, objectName, bytes.NewBuffer(data), int64(len(data)), minio.PutObjectOptions{})
if err != nil {
return nil, err
}
return &FileObjectInfo{
Name: uploadInfo.Key,
Size: uploadInfo.Size,
LastModified: uploadInfo.LastModified,
Expires: uploadInfo.Expiration,
}, nil
}
func (e *MinioTemplate) PresignedGetObject(bucketName string, objectName string, headers map[string]string, expires time.Duration) (string, error) {
var reqParams = make(url.Values)
for k, v := range headers {
reqParams.Set(k, v)
}
presignedURL, err := e.minioClient.PresignedGetObject(context.Background(), bucketName, objectName, expires, reqParams)
if err != nil {
return "", err
}
return presignedURL.RequestURI(), err
}
minio使用代码示例
// 请求
func (e *FileItemHandler) UploadFile(wc *web.WebContext) interface{} {
var bucketName = wc.Param("bucketName")
if len(bucketName) == 0 {
return response.Success()
}
file, header, err := wc.FormFile("file")
if err != nil {
return response.Fail(err.Error())
}
defer file.Close()
data, err := io.ReadAll(file)
if err != nil {
return response.Fail(err.Error())
}
fileName := header.Filename
fileInfo, err := e.fileTemplate.PutObject(bucketName, fileName, data)
if err != nil {
wc.AbortWithError(err)
}
return response.ReturnOK(fileInfo)
}
web框架整合
web框架采用gin,但笔者原先为了处理requestId传递问题,为了处理requestId传递问题,给gin套了层壳(不知道对还是错?)
cd core
go get -u github.com/gin-gonic/gin
cd config
touch server.go
server.go
package config
import (
"core/system"
"github.com/gin-gonic/gin"
)
type Server struct {
Port int `yaml:"port"`
}
func (e *Server) Init() {
if e == nil {
return
}
engine := gin.New()
engine.SetTrustedProxies([]string{"127.0.0.1"})
system.SetGinEngine(engine)
}
config.go 与 application.go
package config
import "core/system"
type Config struct {
Nacos *Nacos `mapstructure:""`
Logger *Logger `yaml:"logger"`
Dasebase *Database `yaml:"database"`
Minio *Minio `yaml:"minio"`
Server *Server `yaml:"server"`
}
var SystemConfig = new(Config)
// 设置参数
func Setup() {
env := system.GetEnv()
//nacos配置
profile := profiles[env]
SystemConfig.Nacos = new(Nacos)
SystemConfig.Nacos.Username = profile.Username
SystemConfig.Nacos.Password = profile.Password
SystemConfig.Nacos.ServerAddr = profile.ServerAddr
SystemConfig.Nacos.Namespace = profile.Namespace
SystemConfig.Nacos.SharedDataIds = profile.SharedDataIds
SystemConfig.Nacos.RefreshableDataIds = profile.RefreshableDataIds
//配置中心
SystemConfig.Nacos.Init()
//日志
SystemConfig.Logger.Init()
//数据库
SystemConfig.Dasebase.Init()
//文件储存
SystemConfig.Minio.Init()
//http
SystemConfig.Server.Init()
}
package system
import (
"github.com/gin-gonic/gin"
"github.com/go-xorm/xorm"
"github.com/minio/minio-go/v7"
"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
"github.com/sirupsen/logrus"
)
type Application struct {
env string
name string
iConfigClient config_client.IConfigClient
logger *logrus.Logger
xormEngine *xorm.Engine
minioClient *minio.Client
ginEngine *gin.Engine
}
func SetEnv(env string) {
if len(application.env) == 0 {
application.env = env
}
}
func GetEnv() string {
return application.env
}
func SetName(name string) {
if len(application.name) == 0 {
application.name = name
}
}
func GetName() string {
return application.name
}
func SetIConfigClient(configClient config_client.IConfigClient) {
if application.iConfigClient == nil {
application.iConfigClient = configClient
}
}
func GetIConfigClient() config_client.IConfigClient {
return application.iConfigClient
}
func SetLogger(logger *logrus.Logger) {
if application.logger == nil {
application.logger = logger
}
}
func GetLogger() *logrus.Logger {
return application.logger
}
func SetXormEngine(engine *xorm.Engine) {
if application.xormEngine == nil {
application.xormEngine = engine
}
}
func GetXormEngine() *xorm.Engine {
return application.xormEngine
}
func SetMinioClient(client *minio.Client) {
if application.minioClient == nil {
application.minioClient = client
}
}
func GetMinioClient() *minio.Client {
return application.minioClient
}
func SetGinEngine(engine *gin.Engine) {
if application.ginEngine == nil {
application.ginEngine = engine
}
}
func GetGinEngine() *gin.Engine {
return application.ginEngine
}
var application = new(Application)
gin的api套壳
定义常量类
cd core/constant
touch logger.go
touch http_header.go
logger.go
package constant
const (
REQUEST_ID string = "requestId"
DB_ERROR string = "db_error"
)
http_header.go
package constant
const (
AUTHORIZATION = "Authorization"
USER_AGENT = "User-Agent"
X_TIMESTAMP = "X-Timestamp"
X_REQUEST_ID = "X-Request-Id"
X_USER_ID = "X-User-Id"
)
cd core
mkdir response
touch response/response.go
mkdir web
cd web
touch context.go
touch group.go
touch web.go
response.go
package response
import (
"net/http"
"time"
)
type Response struct {
Code int `json:"code"` // 响应编码
Message string `json:"message"` // 响应信息
Data interface{} `json:"data,omitempty"` // 响应体
Timestamp int64 `json:"timestamp"` // 当前时间戳
}
// constructor
func NewResponse(code int, message string, data interface{}) *Response {
var response = new(Response)
response.Code = code
response.Message = message
response.Data = data
response.Timestamp = time.Now().UnixMilli()
return response
}
// success
func Success() *Response {
return ReturnOK(nil)
}
// fail
func Fail(message string) *Response {
code := http.StatusInternalServerError
if len(message) == 0 {
message = http.StatusText(code)
}
return NewResponse(code, message, nil)
}
// return ok
func ReturnOK(data interface{}) *Response {
code := http.StatusOK
message := http.StatusText(code)
return NewResponse(code, message, data)
}
// return error
func ReturnError(code int, message string) *Response {
return NewResponse(code, message, nil)
}
context.go
package web
import (
"context"
"core/constant"
"core/system"
"mime/multipart"
"net/http"
"strconv"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)
type WebContext struct {
context *gin.Context
}
func NewWebContext(context *gin.Context) *WebContext {
return &WebContext{context}
}
func (e *WebContext) Trace(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Tracef(format, args...)
}
func (e *WebContext) Debug(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Debugf(format, args...)
}
func (e *WebContext) Info(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Infof(format, args...)
}
func (e *WebContext) Warn(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Warnf(format, args...)
}
func (e *WebContext) Error(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Errorf(format, args...)
}
func (ctx *WebContext) Fatal(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: ctx.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Fatalf(format, args...)
}
func (e *WebContext) Panic(format string, args ...interface{}) {
system.GetLogger().WithFields(logrus.Fields{
constant.REQUEST_ID: e.context.Request.Header.Get(constant.X_REQUEST_ID),
}).Panicf(format, args...)
}
func (e *WebContext) Background() context.Context {
return e.context.Request.Context()
}
func (e *WebContext) AbortWithError(err error) {
e.Error(err.Error())
e.context.AbortWithError(http.StatusInternalServerError, err)
}
func (e *WebContext) ClientIP() string {
return e.context.ClientIP()
}
func (e *WebContext) GetHeader(key string) string {
return e.context.Request.Header.Get(key)
}
func (e *WebContext) PostForm(key string) string {
return e.context.PostForm(key)
}
func (e *WebContext) Param(key string) string {
return e.context.Param(key)
}
func (e *WebContext) Query(key string) string {
return e.context.Query(key)
}
func (e *WebContext) DefaultQuery(key string, defaultValue string) string {
return e.context.DefaultQuery(key, defaultValue)
}
func (e *WebContext) ShouldBindJSON(obj any) error {
return e.context.ShouldBindJSON(obj)
}
func (e *WebContext) BindJSON(obj any) error {
return e.context.BindJSON(obj)
}
func (e *WebContext) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
return e.context.Request.FormFile(key)
}
func (e *WebContext) MultipartForm() (*multipart.Form, error) {
return e.context.MultipartForm()
}
func GetUserId(wc *WebContext) int64 {
userIdStr := wc.GetHeader(constant.X_USER_ID)
if len(userIdStr) == 0 {
return 0
}
userId, err := strconv.ParseInt(userIdStr, 10, 64)
if err != nil {
wc.Error(err.Error())
return 0
}
return userId
}
group.go
package web
import (
"core/system"
"net/http"
"github.com/gin-gonic/gin"
)
type WebHandlerFunc func(*WebContext) interface{}
type IWebRoutes interface {
GET(string, ...WebHandlerFunc) IWebRoutes
POST(string, ...WebHandlerFunc) IWebRoutes
DELETE(string, ...WebHandlerFunc) IWebRoutes
PATCH(string, ...WebHandlerFunc) IWebRoutes
PUT(string, ...WebHandlerFunc) IWebRoutes
OPTIONS(string, ...WebHandlerFunc) IWebRoutes
HEAD(string, ...WebHandlerFunc) IWebRoutes
}
type IWebController interface {
Router(iWebRoutes IWebRoutes)
}
type WebRouterGroup struct {
group *gin.RouterGroup
iRoutes gin.IRoutes
}
var _ IWebRoutes = &WebRouterGroup{}
func (e *WebRouterGroup) buildHandlersChain(handlers []WebHandlerFunc) []gin.HandlerFunc {
var handlersChain = make([]gin.HandlerFunc, 0)
if len(handlers) > 0 {
for _, h := range handlers {
handlersChain = append(handlersChain, func(ctx *gin.Context) {
var res = h(NewWebContext(ctx))
if len(ctx.Errors) == 0 && res != nil {
ctx.JSON(http.StatusOK, res)
}
})
}
}
return handlersChain
}
func (e *WebRouterGroup) Use(handlers ...gin.HandlerFunc) IWebRoutes {
system.GetGinEngine().RouterGroup.Use(handlers...)
return e
}
func (e *WebRouterGroup) Group(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.group.Group(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) POST(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.POST(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) GET(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.GET(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) DELETE(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.DELETE(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) PATCH(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.PATCH(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) PUT(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.PUT(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) OPTIONS(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.OPTIONS(relativePath, e.buildHandlersChain(handlers)...),
}
}
func (e *WebRouterGroup) HEAD(relativePath string, handlers ...WebHandlerFunc) IWebRoutes {
return &WebRouterGroup{
group: e.group,
iRoutes: e.iRoutes.HEAD(relativePath, e.buildHandlersChain(handlers)...),
}
}
web.go
package web
import (
"github.com/gin-gonic/gin"
)
var (
routers = make([]func(*WebRouterGroup), 0)
)
// 添加路由
func AddRouter(handlers ...func(*WebRouterGroup)) {
routers = append(routers, handlers...)
}
// 初始化路由
func InitRouter(engine *gin.Engine) {
var wrg = new(WebRouterGroup)
wrg.group = &engine.RouterGroup
for _, h := range routers {
h(wrg)
}
}
gin使用代码示例
因笔者封装一层,写法略微不同
cd admin
mkdir router
mkdir controller
mkdir handler
touch router.go
touch hello_world_handler.go
touch hello_world_controller.go
hello_world_handler.go
业务处理层
package handler
import (
"core/response"
"core/web"
)
type HelloWorldHandler struct {
}
func (e *HelloWorldHandler) Hello(wc *web.WebContext) interface{} {
return response.ReturnOK("hello,world!")
}
hello_world_controller.go
接口层
package controller
import (
"admin/handler"
"core/web"
)
type HelloWorldController struct {
}
var _ web.IWebController = &HelloWorldController{}
func (e *HelloWorldController) Router(iWebRoutes web.IWebRoutes) {
var helloWorldHandler = handler.HelloWorldHandler{}
iWebRoutes.GET("/hello/world", helloWorldHandler.Hello)
}
router.go
接口路由层,通过golang的init方法自动加入到全局的list里
package router
import (
"admin/controller"
"core/web"
)
func init() {
//添加路由
web.AddRouter(func(wrg *web.WebRouterGroup) {
r := wrg.Group("/admin")
{
new(controller.HelloWorldController).Router(r)
}
})
}
因整套gin的api都经过封装,若有扩展,则需根据自身的需求,在web目录下增加。
在cmd.go增加web启动函数
添加startServer函数
package cmd
import (
"core/config"
"core/system"
"core/web"
"errors"
"fmt"
"os"
"strconv"
"github.com/spf13/cobra"
)
var (
env, name string
rootCmd = &cobra.Command{
Use: system.GetName(),
Short: system.GetName(),
SilenceUsage: true,
Long: system.GetName(),
Args: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("requires at least one arg")
}
return nil
},
}
StartCmd = &cobra.Command{
Use: "start",
Short: "Start application",
Example: system.GetName() + " start -e dev",
Run: func(cmd *cobra.Command, args []string) {
run()
},
}
)
func init() {
rootCmd.AddCommand(StartCmd)
StartCmd.PersistentFlags().StringVarP(&env, "start", "e", "dev", "Setting up the running environment")
}
func run() {
setupConfig()
startServer()
}
func setupConfig() {
fmt.Printf("The profile active is %s\n", env)
system.SetEnv(env)
if len(name) > 0 {
system.SetName(name)
}
config.Setup()
}
func startServer() {
server := config.SystemConfig.Server
if server == nil {
return
}
fmt.Println("Start server...")
engine := system.GetGinEngine()
//初始化
web.InitRouter(engine)
//端口
port := strconv.Itoa(server.Port)
if len(port) > 0 {
os.Setenv("PORT", port)
}
engine.Run()
}
func Execute(applicationName string) {
system.SetName(applicationName)
if err := rootCmd.Execute(); err != nil {
fmt.Printf("Execute err : %s\n", err.Error())
os.Exit(-1)
}
}
项目结构
.
├── LICENSE
├── Makefile
├── README.md
├── admin
│ ├── controller
│ │ └── hello_world_controller.go
│ ├── go.mod
│ ├── handler
│ │ └── hello_world_handler.go
│ ├── main.go
│ └── router
│ └── router.go
├── basic
│ └── go.mod
├── business
│ └── go.mod
├── core
│ ├── cmd
│ │ └── cmd.go
│ ├── config
│ │ ├── config.go
│ │ ├── database.go
│ │ ├── logger.go
│ │ ├── minio.go
│ │ ├── nacos.go
│ │ └── server.go
│ ├── constant
│ │ ├── http_header.go
│ │ ├── logger.go
│ │ └── storage.go
│ ├── file
│ │ ├── file_template.go
│ │ └── minio_template.go
│ ├── go.mod
│ ├── go.sum
│ ├── response
│ │ └── response.go
│ ├── system
│ │ └── application.go
│ ├── utils
│ │ ├── date.go
│ │ └── string.go
│ └── web
│ ├── context.go
│ ├── group.go
│ └── web.go
├── go.work
├── go.work.sum
└── oauth
└── go.mod
总结
综合前面几篇文章,基础框架已搭建完成,可形成正常的api回路,一个正常、可以跑起来的golang项目。
当前项目:
https://github.com/liaz-repo/liaz-server.git
照旧附上app的gitee地址(漫画/轻小说app):
https://gitee.com/liaz-app/liaz-android/releases/download/1.0.0/app-arm64-v8a-release.apk