gin

2025-07-20 01:49:54阅读 52评论 0

项目结构

|-- config
|   `-- config.go
|-- config.yml
|-- controllers
|   |-- common.go
|   |-- jobInfo.go
|   |-- myInfo.go
|   |-- task.go
|   `-- test.go
|-- dao
|   `-- dao.go
|-- docs
|   |-- docs.go
|   |-- swagger.json
|   `-- swagger.yaml
|-- dto
|   `-- myInfo.go
|-- go.mod
|-- go.sum
|-- internal
|   `-- middleware
|       `-- logging.go
|-- log
|   |-- app.log
|   |-- app.log.20250718.log
|   |-- app.log.20250719.log
|   |-- app.log.20250720.log
|   `-- app.log.20250721.log
|-- main.go
|-- models
|   |-- jobInfo.go
|   |-- jsonObj.go
|   |-- migrate.go
|   `-- task.go
|-- router
|   `-- router.go
|-- service
|   |-- jobInfoService.go
|   |-- jobMatchService.go
|   |-- jsonObjService.go
|   `-- taskService.go
`-- test.db

入口文件

package main

import (
    "boss-server/dao"
    _ "boss-server/docs"
    "boss-server/internal/middleware"
    "boss-server/models"
    "boss-server/router"
    "gorm.io/gorm"
)

var db *gorm.DB

// 初始化数据库
func init() {
    db = dao.Init()
    models.AutoMigrate(db)
}

// @title boss-server Swagger 标题
// @version 1.0 版本
// @description boss-server Service 描述
// @BasePath /api 基础路径
// @query.collection.format multi
func main() {
    // 初始化路由
    r := router.Router()
    // 日志中间件
    r.Use(middleware.LoggerToFile())
    r.Run(":9999")
}

路由

package router

import (
    "boss-server/controllers"
    docs "boss-server/docs"
    "boss-server/internal/middleware"
    "fmt"
    "github.com/gin-gonic/gin"
    swaggerfiles "github.com/swaggo/files"
    ginSwagger "github.com/swaggo/gin-swagger"
    "runtime"
)

func customRecovery() gin.HandlerFunc {
    return func(c *gin.Context) {
        defer func() {
            if err := recover(); err != nil {
                middleware.Log.Error(fmt.Sprintf("%v", err))
                var buf [4096]byte
                n := runtime.Stack(buf[:], false)
                fmt.Printf("==== Stack trace ====\n%s\n", buf[:n])
                c.JSON(200, gin.H{
                    "code": 500,
                    "msg":  fmt.Sprintf("%v", err),
                })
                c.Abort()
            }
        }()
        c.Next()
    }
}

func Router() *gin.Engine {
    r := gin.Default()
    r.Use(customRecovery())

    docs.SwaggerInfo.BasePath = "/api"
    r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))

    base := r.Group("/api")
    {
        jobInfo := base.Group("/jobInfo")
        {
            jobInfo.GET("/getJobInfoPage", func(ctx *gin.Context) {
                controllers.JobInfoController{}.GetJobInfoPage(ctx)
            })

            jobInfo.POST("/addAndUpdateFrequent", func(ctx *gin.Context) {
                controllers.JobInfoController{}.AddAndUpdateFrequent(ctx)
            })
            jobInfo.GET("/getJobInfoByTaskIdAndNum", func(ctx *gin.Context) {
                controllers.JobInfoController{}.GetJobInfoByTaskIdAndNum(ctx)
            })
            jobInfo.POST("/aiMatchJob", func(ctx *gin.Context) {
                controllers.JobInfoController{}.AiMatchJob(ctx)
            })
        }
        task := base.Group("/task")
        {
            task.POST("/addTask", func(ctx *gin.Context) {
                controllers.TaskController{}.AddTask(ctx)
            })
            task.GET("/getById", func(ctx *gin.Context) {
                controllers.TaskController{}.GetById(ctx)
            })
        }

        myInfo := base.Group("/myInfo")
        {
            myInfo.GET("/getMyInfo", func(ctx *gin.Context) {
                controllers.MyInfoController{}.GetMyInfo(ctx)
            })
            myInfo.POST("/saveMyInfo", func(ctx *gin.Context) {
                controllers.MyInfoController{}.SaveMyInfo(ctx)
            })
        }

        test := base.Group("/test")
        {
            test.POST("/test", func(ctx *gin.Context) {
                controllers.TestController{}.Test(ctx)
            })
        }
    }

    return r
}

标准返回定义

package controllers

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

type JsonStruct struct{
    Code int `json:"code"`
    Msg interface{} `json:"msg"`
    Data interface{} `json:"data"`
}

func ReturnSuccess(c *gin.Context,data interface{}){
    json := &JsonStruct{Code:200,Msg:"操作成功",Data:data}
    c.JSON(200,json)
}

func ReturnError(c *gin.Context,msg interface{}){
    json := &JsonStruct{Code:500,Msg:msg}
    c.JSON(200,json)
}

控制器

package controllers

import (
    "boss-server/internal/middleware"
    "boss-server/models"
    "boss-server/service"
    "github.com/gin-gonic/gin"
    "strconv"
)

type JobInfoController struct{}

func (j JobInfoController) GetJobInfoPage(ctx *gin.Context) {
    pageStr := ctx.Query("page")
    limitStr := ctx.Query("limit")
    taskIdStr := ctx.Query("taskId")

    // 默认值设置
    page := 0
    limit := 10
    taskId := 0

    if pageStr != "" {
        pageInt, err := strconv.Atoi(pageStr)
        if err == nil && pageInt > 0 {
            page = (pageInt - 1) * limit
        }
    }

    if limitStr != "" {
        limitInt, err := strconv.Atoi(limitStr)
        if err == nil && limitInt > 0 {
            limit = limitInt
        }
    }

    if taskIdStr != "" {
        taskIdInt, err := strconv.Atoi(taskIdStr)
        if err == nil && taskIdInt > 0 {
            taskId = taskIdInt
        }
    }

    jobInfoService := service.NewJobInfoService()
    jobInfos, total := jobInfoService.GetJobInfoPage(page, limit, taskId)

    //创建一个map接受返回的两个值jobInfos和taskId
    var resultMap = make([]map[string]interface{}, 2)
    resultMap[0] = map[string]interface{}{"jobInfos": jobInfos}
    resultMap[1] = map[string]interface{}{"total": total}

    ReturnSuccess(ctx, resultMap)
}

func (j JobInfoController) AddAndUpdateFrequent(ctx *gin.Context) {
    var jobInfo models.JobInfo
    err := ctx.BindJSON(&jobInfo)
    if err != nil {
        middleware.Log.Error(err)
    }
    service.NewJobInfoService().AddAndUpdateFrequent(&jobInfo)
    ReturnSuccess(ctx, "")
}

func (j JobInfoController) GetJobInfoByTaskIdAndNum(ctx *gin.Context) {
    taskIdStr := ctx.Query("taskId")
    numStr := ctx.Query("num")
    taskIdInt, err := strconv.Atoi(taskIdStr)
    numInt, err := strconv.Atoi(numStr)
    if err != nil {
        ReturnError(ctx, "参数错误")
    }
    jobInfo := service.NewJobInfoService().GetJobInfoByTaskIdAndNum(taskIdInt, numInt)
    ReturnSuccess(ctx, jobInfo)
}

func (j JobInfoController) AiMatchJob(ctx *gin.Context) {
    type AiMatchParams struct {
        TaskId int `json:"taskId"`
        Num    int `json:"num"`
    }
    var params AiMatchParams
    err := ctx.BindJSON(&params)
    if err != nil {
        return
    }
    service.NewJobInfoService().AiMatchJob(params.TaskId, params.Num)
    ReturnSuccess(ctx, "")
}

数据库配置

package dao

import (
    "boss-server/internal/middleware"
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
    "time"
)

var Db *gorm.DB

func Init() *gorm.DB {
    fmt.Println("open database success")
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        middleware.Log.Error("failed to connect database")
    }
    if db.Error != nil {
        middleware.Log.Error(db.Error)
    }

    sqlDb, _ := db.DB()
    sqlDb.SetMaxIdleConns(10)
    sqlDb.SetMaxOpenConns(100)
    sqlDb.SetConnMaxLifetime(time.Hour)
    Db = db

    return db
}

自动迁移

package models

import "gorm.io/gorm"

// AutoMigrate 自动迁移
func AutoMigrate(db *gorm.DB) {
    err := db.AutoMigrate(&User{})
    err = db.AutoMigrate(&JobInfo{})
    err = db.AutoMigrate(&Task{})
    if err != nil {
        return
    }
}

服务

package service

import (
    "boss-server/dao"
    "boss-server/models"
)

type TaskService interface {
    AddTask(task *models.Task)
    UpdateTask(task *models.Task)
    GetById(id int) *models.Task
}

type taskServiceImpl struct {
}

func NewTaskServiceImpl() TaskService {
    return &taskServiceImpl{}
}

func (s *taskServiceImpl) AddTask(task *models.Task) {
    dao.Db.Create(task)
}
func (s *taskServiceImpl) UpdateTask(task *models.Task) {
    dao.Db.Model(task).Where("id = ?", task.Id).Updates(task)
}

func (s *taskServiceImpl) GetById(id int) *models.Task {
    task := &models.Task{}
    dao.Db.Where("id = ?", id).First(task)
    return task
}

日志配置

package middleware

import (
    "fmt"
    "github.com/gin-gonic/gin"
    rotatelogs "github.com/lestrrat-go/file-rotatelogs"
    "github.com/rifflock/lfshook"
    "github.com/sirupsen/logrus"
    "io"
    "os"
    "path"
    "time"
)

var Log *logrus.Logger

// 日志记录到文件
func LoggerToFile() gin.HandlerFunc {

    logFilePath := "./log"
    logFileName := "app.log"

    // 日志文件
    fileName := path.Join(logFilePath, logFileName)

    // 写入文件

    src, err := os.OpenFile(fileName, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        fmt.Println("err", err)
    }

    // 实例化
    Log = logrus.New()

    multiWriter := io.MultiWriter(os.Stdout, src)

    // 设置输出
    Log.Out = multiWriter

    // 设置日志级别
    Log.SetLevel(logrus.DebugLevel)

    // 设置 rotatelogs
    logWriter, err := rotatelogs.New(
        // 分割后的文件名称
        fileName+".%Y%m%d.log",

        // 生成软链,指向最新日志文件
        rotatelogs.WithLinkName(fileName),

        // 设置最大保存时间(7天)
        rotatelogs.WithMaxAge(7*24*time.Hour),

        // 设置日志切割时间间隔(1天)
        rotatelogs.WithRotationTime(24*time.Hour),
    )

    writeMap := lfshook.WriterMap{
        logrus.InfoLevel:  logWriter,
        logrus.FatalLevel: logWriter,
        logrus.DebugLevel: logWriter,
        logrus.WarnLevel:  logWriter,
        logrus.ErrorLevel: logWriter,
        logrus.PanicLevel: logWriter,
    }

    lfHook := lfshook.NewHook(writeMap, &logrus.JSONFormatter{
        TimestampFormat: "2006-01-02 15:04:05",
    })

    // 新增 Hook
    Log.AddHook(lfHook)

    return func(c *gin.Context) {
        // 开始时间
        startTime := time.Now()

        // 处理请求
        c.Next()

        // 结束时间
        endTime := time.Now()

        // 执行时间
        latencyTime := endTime.Sub(startTime)

        fmt.Println(latencyTime)

        // 请求方式
        reqMethod := c.Request.Method

        // 请求路由
        reqUri := c.Request.RequestURI

        // 状态码
        statusCode := c.Writer.Status()

        // 请求IP
        clientIP := c.ClientIP()

        // 日志格式
        Log.WithFields(logrus.Fields{
            "status_code":  statusCode,
            "latency_time": 1000,
            "client_ip":    clientIP,
            "req_method":   reqMethod,
            "req_uri":      reqUri,
        }).Info()
    }
}

时间格式化

package utils

import (
    "database/sql/driver"
    "fmt"
    "time"
)

const timeFormat = "2006-01-02 15:04:05"
const timezone = "Asia/Shanghai"

type Time time.Time

func (t Time) MarshalJSON() ([]byte, error) {
    b := make([]byte, 0, len(timeFormat)+2)
    b = append(b, '"')
    b = time.Time(t).AppendFormat(b, timeFormat)
    b = append(b, '"')
    return b, nil
}

func (t *Time) UnmarshalJSON(data []byte) (err error) {
    now, err := time.ParseInLocation(`"`+timeFormat+`"`, string(data), time.Local)
    *t = Time(now)
    return
}

func (t Time) String() string {
    return time.Time(t).Format(timeFormat)
}

func (t Time) local() time.Time {
    loc, _ := time.LoadLocation(timezone)
    return time.Time(t).In(loc)
}

func (t Time) Value() (driver.Value, error) {
    var zeroTime time.Time
    var ti = time.Time(t)
    if ti.UnixNano() == zeroTime.UnixNano() {
        return nil, nil
    }
    return ti, nil
}

func (t *Time) Scan(v interface{}) error {
    value, ok := v.(time.Time)
    if ok {
        *t = Time(value)
        return nil
    }
    return fmt.Errorf("can not convert %v to timestamp", v)
}

自定义线程池

package pool

import (
    "fmt"
    "github.com/panjf2000/ants/v2"
    "github.com/sirupsen/logrus"
    "sync"
)

// GlobalPool 是全局线程池的实例
var GlobalPool *ants.Pool
var once sync.Once

const maxWorkers = 5

// 初始化全局线程池
func initGlobalPool() {
    once.Do(func() {
        var err error
        GlobalPool, err = ants.NewPool(maxWorkers, ants.WithMaxBlockingTasks(100))
        if err != nil {
            logrus.Fatalf("Failed to create global pool: %v", err)
        }
        fmt.Println("初始化pool")
    })
}

// Submit 提交任务到全局线程池
func Submit(task func()) error {
    initGlobalPool()
    return GlobalPool.Submit(task)
}

// Release 释放全局线程池资源
func Release() {
    if GlobalPool != nil {
        GlobalPool.Release()
    }
}

库推荐

1.json处理

go get github.com/bitly/go-simplejson

2.数据库

go get -u gorm.io/gorm

3.日志

go get github.com/sirupsen/logrus
go get github.com/lestrrat-go/file-rotatelogs
github.com/rifflock/lfshook

4.线程池

go get -u github.com/panjf2000/ants/v2

还没有评论,来说两句吧