Jelajahi Sumber

Merge pull request #438 from songzhibin97/gva_gormv2_dev

新增定时任务封装以及配置自动清理数据库功能
奇淼(piexlmax 3 tahun lalu
induk
melakukan
1b0d3db0a7

+ 14 - 1
server/config.yaml

@@ -114,4 +114,17 @@ tencent-cos:
 
 # excel configuration
 excel:
-  dir: './resource/excel/'
+  dir: './resource/excel/'
+
+
+# timer task db clear table
+Timer:
+  spec: "@daily"  # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron?utm_source=godoc
+  detail: [
+    # tableName: 需要清理的表名
+    # compareField: 需要比较时间的字段
+    # interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数
+    # 2160h = 24 * 30 * 3 -> 三个月
+    { tableName: "log" , compareField: "created_at", interval: "2160h" },
+    { tableName: "log2" , compareField: "created_at", interval: "2160h" }
+  ]

+ 1 - 0
server/config/config.go

@@ -18,4 +18,5 @@ type Server struct {
 	AliyunOSS  AliyunOSS  `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"`
 	TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"`
 	Excel      Excel      `mapstructure:"excel" json:"excel" yaml:"excel"`
+	Timer      Timer      `mapstructure:"timer" json:"timer" yaml:"timer"`
 }

+ 13 - 0
server/config/timer.go

@@ -0,0 +1,13 @@
+package config
+
+type Timer struct {
+	Start  bool     `mapstructure:"start" json:"start" yaml:"start"`
+	Spec   string   `mapstructure:"spec" json:"spec" yaml:"spec"`
+	Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"`
+}
+
+type Detail struct {
+	TableName    string `mapstructure:"tableName" json:"tableName" yaml:"tableName"`
+	CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"`
+	Interval     string `mapstructure:"interval" json:"interval" yaml:"interval"`
+}

+ 5 - 1
server/global/global.go

@@ -1,9 +1,12 @@
 package global
 
 import (
+	"gin-vue-admin/utils/timer"
+
 	"go.uber.org/zap"
 
 	"gin-vue-admin/config"
+
 	"github.com/go-redis/redis"
 	"github.com/spf13/viper"
 	"gorm.io/gorm"
@@ -15,5 +18,6 @@ var (
 	GVA_CONFIG config.Server
 	GVA_VP     *viper.Viper
 	//GVA_LOG    *oplogging.Logger
-	GVA_LOG *zap.Logger
+	GVA_LOG   *zap.Logger
+	GVA_Timer timer.Timer = timer.NewTimerTask()
 )

+ 1 - 0
server/go.mod

@@ -36,6 +36,7 @@ require (
 	github.com/pelletier/go-toml v1.6.0 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/qiniu/api.v7/v7 v7.4.1
+	github.com/robfig/cron/v3 v3.0.1
 	github.com/satori/go.uuid v1.2.0
 	github.com/shirou/gopsutil v3.21.1+incompatible
 	github.com/spf13/afero v1.2.2 // indirect

+ 24 - 0
server/initialize/timer.go

@@ -0,0 +1,24 @@
+package initialize
+
+import (
+	"fmt"
+	"gin-vue-admin/config"
+	"gin-vue-admin/global"
+	"gin-vue-admin/utils"
+)
+
+func Timer() {
+	if global.GVA_CONFIG.Timer.Start {
+		for _, detail := range global.GVA_CONFIG.Timer.Detail {
+			fmt.Println(detail)
+			go func(detail config.Detail) {
+				global.GVA_Timer.AddTaskByFunc("ClearDB", global.GVA_CONFIG.Timer.Spec, func() {
+					err := utils.ClearTable(global.GVA_DB, detail.TableName, detail.CompareField, detail.Interval)
+					if err != nil {
+						fmt.Println("timer error:", err)
+					}
+				})
+			}(detail)
+		}
+	}
+}

+ 1 - 0
server/main.go

@@ -17,6 +17,7 @@ func main() {
 	global.GVA_VP = core.Viper()      // 初始化Viper
 	global.GVA_LOG = core.Zap()       // 初始化zap日志库
 	global.GVA_DB = initialize.Gorm() // gorm连接数据库
+	initialize.Timer()
 	if global.GVA_DB != nil {
 		initialize.MysqlTables(global.GVA_DB) // 初始化表
 		// 程序结束前关闭数据库链接

+ 29 - 0
server/utils/db_automation.go

@@ -0,0 +1,29 @@
+package utils
+
+import (
+	"errors"
+	"fmt"
+	"time"
+
+	"gorm.io/gorm"
+)
+
+//@author: [songzhibin97](https://github.com/songzhibin97)
+//@function: ClearTable
+//@description: 清理数据库表数据
+//@param: target db(数据库对象) *gorm.DB,tableName(表名) string,compareField(比较字段) string , interval string  间隔
+//@return: err
+
+func ClearTable(db *gorm.DB, tableName string, compareField string, interval string) error {
+	if db == nil {
+		return errors.New("db Cannot be empty")
+	}
+	duration, err := time.ParseDuration(interval)
+	if err != nil {
+		return err
+	}
+	if duration < 0 {
+		return errors.New("parse duration < 0")
+	}
+	return db.Debug().Exec(fmt.Sprintf("DELETE FROM %s WHERE %s < ?", tableName, compareField), time.Now().Add(-duration)).Error
+}

+ 109 - 0
server/utils/timer/timed_task.go

@@ -0,0 +1,109 @@
+package timer
+
+import (
+	"sync"
+
+	"github.com/robfig/cron/v3"
+)
+
+type Timer interface {
+	AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error)
+	AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error)
+	FindCron(taskName string) (*cron.Cron, bool)
+	StartTask(taskName string)
+	StopTask(taskName string)
+	Remove(taskName string, id int)
+	Clear(taskName string)
+	Close()
+}
+
+// timer 定时任务管理
+type timer struct {
+	taskList map[string]*cron.Cron
+	sync.Mutex
+}
+
+// AddTaskByFunc 通过函数的方法添加任务
+func (t *timer) AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error) {
+	t.Lock()
+	defer t.Unlock()
+	if _, ok := t.taskList[taskName]; !ok {
+		t.taskList[taskName] = cron.New()
+	}
+	id, err := t.taskList[taskName].AddFunc(spec, task)
+	t.taskList[taskName].Start()
+	return id, err
+}
+
+// AddTaskByJob 通过接口的方法添加任务
+func (t *timer) AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error) {
+	t.Lock()
+	defer t.Unlock()
+	if _, ok := t.taskList[taskName]; !ok {
+		t.taskList[taskName] = cron.New()
+	}
+	id, err := t.taskList[taskName].AddJob(spec, job)
+	t.taskList[taskName].Start()
+	return id, err
+}
+
+// FindCron 获取对应taskName的cron 可能会为空
+func (t *timer) FindCron(taskName string) (*cron.Cron, bool) {
+	t.Lock()
+	defer t.Unlock()
+	v, ok := t.taskList[taskName]
+	return v, ok
+}
+
+// StartTask 开始任务
+func (t *timer) StartTask(taskName string) {
+	t.Lock()
+	defer t.Unlock()
+	if v, ok := t.taskList[taskName]; ok {
+		v.Start()
+	}
+	return
+}
+
+// StopTask 停止任务
+func (t *timer) StopTask(taskName string) {
+	t.Lock()
+	defer t.Unlock()
+	if v, ok := t.taskList[taskName]; ok {
+		v.Stop()
+	}
+	return
+}
+
+// Remove 从taskName 删除指定任务
+func (t *timer) Remove(taskName string, id int) {
+	t.Lock()
+	defer t.Unlock()
+	if v, ok := t.taskList[taskName]; ok {
+		v.Remove(cron.EntryID(id))
+	}
+	return
+}
+
+// Clear 清除任务
+func (t *timer) Clear(taskName string) {
+	t.Lock()
+	defer t.Unlock()
+	if v, ok := t.taskList[taskName]; ok {
+		v.Stop()
+		delete(t.taskList, taskName)
+	}
+}
+
+// Close 释放资源
+func (t *timer) Close() {
+	t.Lock()
+	defer t.Unlock()
+	for _, v := range t.taskList {
+		v.Stop()
+	}
+}
+
+func NewTimerTask() Timer {
+	return &timer{taskList: make(map[string]*cron.Cron)}
+}