gin
项目结构
|-- 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(¶ms)
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还没有评论,来说两句吧
