diff --git a/server/config.yaml b/server/config.yaml index d57d69638ff3b1fd5bce1da84ae0add4029fc6e3..3cbc4bc38bd21d019c939f1311828bff4a982790 100644 --- a/server/config.yaml +++ b/server/config.yaml @@ -114,4 +114,17 @@ tencent-cos: # excel configuration excel: - dir: './resource/excel/' \ No newline at end of file + 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: "sys_operation_records" , compareField: "created_at", interval: "2160h" }, + #{ tableName: "log2" , compareField: "created_at", interval: "2160h" } + ] diff --git a/server/config/config.go b/server/config/config.go index e2e557de0bfb40545cd88e309e81c916666af165..83d256f491668c4e0d08835a7e339a5864d1bd20 100644 --- a/server/config/config.go +++ b/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"` } diff --git a/server/config/timer.go b/server/config/timer.go new file mode 100644 index 0000000000000000000000000000000000000000..f83d6bf081da9d720dbea46cfcb9ee957fe7bcff --- /dev/null +++ b/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"` +} diff --git a/server/global/global.go b/server/global/global.go index 98312d823f3eeac2497cc759a22c96d870f8b750..43577a4c128dcaa0fdcf2a411a107e0af32226f8 100644 --- a/server/global/global.go +++ b/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() ) diff --git a/server/go.mod b/server/go.mod index f42f521e4fb26484987ee630640d85f6613c54c5..e9900c0393dd67f2af476a172a49fbbcefc6a66d 100644 --- a/server/go.mod +++ b/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 diff --git a/server/initialize/timer.go b/server/initialize/timer.go new file mode 100644 index 0000000000000000000000000000000000000000..6fa2791372e4d74ede099adf0b1e1147abde1e5b --- /dev/null +++ b/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) + } + } +} diff --git a/server/main.go b/server/main.go index c772d3276dc4dabaf86f74475b539e1584ec52b5..ee9080f1b9411aaec4d2bc56e8c41e77a32ac850 100644 --- a/server/main.go +++ b/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) // 初始化表 // 程序结束前关闭数据库链接 diff --git a/server/service/sys_casbin.go b/server/service/sys_casbin.go index 457be1b71a041c11a656106fe383e6a8acae896f..4d7265a24915229d10cb00ff6023beb6a5605080 100644 --- a/server/service/sys_casbin.go +++ b/server/service/sys_casbin.go @@ -90,8 +90,7 @@ func ClearCasbin(v int, p ...string) bool { //@return: *casbin.Enforcer func Casbin() *casbin.Enforcer { - admin := global.GVA_CONFIG.Mysql - a, _ := gormadapter.NewAdapter(global.GVA_CONFIG.System.DbType, admin.Username+":"+admin.Password+"@("+admin.Path+")/"+admin.Dbname, true) + a, _ := gormadapter.NewAdapterByDB(global.GVA_DB) e, _ := casbin.NewEnforcer(global.GVA_CONFIG.Casbin.ModelPath, a) e.AddFunction("ParamsMatch", ParamsMatchFunc) _ = e.LoadPolicy() diff --git a/server/service/sys_initdb.go b/server/service/sys_initdb.go index d50993c7443fb954e24928d5e7c5d837a2efca86..58003117c0c9960b21a9411c6d1883fdf566a831 100644 --- a/server/service/sys_initdb.go +++ b/server/service/sys_initdb.go @@ -41,7 +41,12 @@ func createTable(dsn string, driver string, createSql string) error { if err != nil { return err } - defer db.Close() + defer func(db *sql.DB) { + err := db.Close() + if err != nil { + + } + }(db) if err = db.Ping(); err != nil { return err } @@ -82,7 +87,6 @@ func InitDB(conf request.InitDB) error { conf.Port = "3306" } dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/", conf.UserName, conf.Password, conf.Host, conf.Port) - fmt.Println(dsn) createSql := fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci;", conf.DBName) if err := createTable(dsn, "mysql", createSql); err != nil { return err @@ -117,6 +121,7 @@ func InitDB(conf request.InitDB) error { //global.GVA_LOG.Error("MySQL启动异常", zap.Any("err", err)) //os.Exit(0) //return nil + _ = writeConfig(global.GVA_VP, BaseMysql) return nil } else { sqlDB, _ := db.DB() @@ -142,6 +147,7 @@ func InitDB(conf request.InitDB) error { model.SysOperationRecord{}, ) if err != nil { + _ = writeConfig(global.GVA_VP, BaseMysql) return err } err = initDB( diff --git a/server/utils/db_automation.go b/server/utils/db_automation.go new file mode 100644 index 0000000000000000000000000000000000000000..c3743b92451c7021334adec4840574940992c919 --- /dev/null +++ b/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 +} diff --git a/server/utils/timer/timed_task.go b/server/utils/timer/timed_task.go new file mode 100644 index 0000000000000000000000000000000000000000..881fddeb46e51805457dc377e0549f142fe632b4 --- /dev/null +++ b/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)} +}