Files
utility_go/internal/logic/systemCron/systemCron.go
2025-08-22 14:31:57 +08:00

388 lines
11 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package systemCron
import (
"context"
"sync"
"time"
"github.com/ayflying/utility_go/api/system/v1"
"github.com/ayflying/utility_go/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcron"
"github.com/gogf/gf/v2/os/gctx"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/os/gtimer"
)
var (
//ctx = gctx.New()
startTime *gtime.Time
)
// sSystemCron 结构体定义了系统定时任务的秒计时器。
// 它包含了不同时间周期的任务,如秒、分钟、小时、天、周、月、年以及特定的工作日任务。
type sSystemCron struct {
//互斥锁
Lock sync.Mutex
taskChan chan func(context.Context) error
TaskTimeout time.Duration
// 每秒执行的任务
SecondlyTask []func(context.Context) error
// 每分钟执行的任务
MinutelyTask []func(context.Context) error
// 每小时执行的任务
HourlyTask []func(context.Context) error
// 每天执行的任务
DailyTask []func(context.Context) error
// 每周执行的任务
WeeklyTask []func(context.Context) error
// 每月执行的任务
MonthlyTask []func(context.Context) error
// 每年执行的任务
YearlyTask []func(context.Context) error
// 每周一执行的任务
MondayTask []func(context.Context) error
// 每周二执行的任务
TuesdayTask []func(context.Context) error
// 每周三执行的任务
WednesdayTask []func(context.Context) error
// 每周四执行的任务
ThursdayTask []func(context.Context) error
// 每周五执行的任务
FridayTask []func(context.Context) error
// 每周六执行的任务
SaturdayTask []func(context.Context) error
// 每周日执行的任务
SundayTask []func(context.Context) error
}
func New() *sSystemCron {
return &sSystemCron{
taskChan: make(chan func(context.Context) error, 2),
TaskTimeout: time.Minute * 30,
}
}
func init() {
service.RegisterSystemCron(New())
}
// AddCron 添加一个定时任务到相应的调度列表中。
//
// @Description: 根据指定的类型将函数添加到不同的任务列表中,以供后续执行。
// 确保自定义任务正确处理上下文取消信号,即可充分发挥超时打断功能。
// @receiver s: sSystemCron的实例代表一个调度系统。
// @param typ: 任务的类型,决定该任务将被添加到哪个列表中。对应不同的时间间隔。
// @param _func: 要添加的任务函数该函数执行时应该返回一个error。
// deprecated: 弃用,请使用 AddCronV2
func (s *sSystemCron) AddCron(typ v1.CronType, _func func() error) {
//转换为带上下文的,提供打断
var _func2 = func(ctx context.Context) error {
return _func()
}
// 老版本计划任务全都是主服务器唯一执行
s.AddCronV2(typ, _func2, true)
}
// AddCronV2 添加一个定时任务到相应的调度列表中。
//
// @Description: 根据指定的类型将函数添加到不同的任务列表中,以供后续执行。
// @receiver s: sSystemCron的实例代表一个调度系统。
// @param typ: 任务的类型,决定该任务将被添加到哪个列表中。对应不同的时间间隔。
// @param _func: 要添加的任务函数该函数执行时应该返回一个error。
// @param _onlyMain: 是否只在主服务器上执行一次,true 唯一执行false 全局执行不判断唯一
func (s *sSystemCron) AddCronV2(typ v1.CronType, _func func(context.Context) error, _onlyMain ...bool) {
//如果传过来的任务是需要主服务器执行一次
if len(_onlyMain) > 0 && _onlyMain[0] {
//判断当前是否为主服务器
if !g.Cfg().MustGet(gctx.New(), "game.cron_main").Bool() {
return
}
}
//加锁
s.Lock.Lock()
defer s.Lock.Unlock()
//
//ctx := gctx.New()
//newFunc := func()
switch typ {
case v1.CronType_SECOND:
s.SecondlyTask = append(s.SecondlyTask, _func) // 将函数添加到每秒执行的任务列表中
case v1.CronType_MINUTE:
s.MinutelyTask = append(s.MinutelyTask, _func) // 将函数添加到每分钟执行的任务列表中
case v1.CronType_HOUR:
s.HourlyTask = append(s.HourlyTask, _func) // 将函数添加到每小时执行的任务列表中
case v1.CronType_DAILY:
s.DailyTask = append(s.DailyTask, _func) // 将函数添加到每日执行的任务列表中
case v1.CronType_WEEK:
s.WeeklyTask = append(s.WeeklyTask, _func) // 将函数添加到每周执行的任务列表中
case v1.CronType_MONTH:
s.MonthlyTask = append(s.MonthlyTask, _func) // 将函数添加到每月执行的任务列表中
case v1.CronType_YEAR:
s.YearlyTask = append(s.YearlyTask, _func) // 将函数添加到每年执行的任务列表中
case v1.CronType_MONDAY:
s.MondayTask = append(s.MondayTask, _func) // 将函数添加到每周一执行的任务列表中
case v1.CronType_TUESDAY:
s.TuesdayTask = append(s.TuesdayTask, _func) // 将函数添加到每周二的任务列表中
case v1.CronType_WEDNESDAY:
s.WednesdayTask = append(s.WednesdayTask, _func) // 将函数添加到每周三执行的任务列表中
case v1.CronType_THURSDAY:
s.ThursdayTask = append(s.ThursdayTask, _func) // 将函数添加到每周四执行的任务列表中
case v1.CronType_FRIDAY:
s.FridayTask = append(s.FridayTask, _func) // 将函数添加到每周五执行的任务列表中
case v1.CronType_SATURDAY:
s.SaturdayTask = append(s.SaturdayTask, _func) // 将函数添加到每周六执行的任务列表中
case v1.CronType_SUNDAY:
s.SundayTask = append(s.SundayTask, _func) // 将函数添加到每周日的任务列表中
}
}
// StartCron 开始计划任务执行
//
// @Description:
// @receiver s
// @return err
func (s *sSystemCron) StartCron() (err error) {
//如果没有数据库配置,跳过计划任务执行
if g.Cfg().MustGet(gctx.New(), "database") == nil {
return
}
//预防重复启动
if startTime != nil {
return
}
startTime = gtime.Now()
g.Log().Debug(gctx.New(), "启动计划任务定时器详情")
//每秒任务
gtimer.SetInterval(gctx.New(), time.Second, func(ctx context.Context) {
//g.Log().Debug(ctx, "每秒定时器")
s.secondlyTask()
})
//每分钟任务
_, err = gcron.AddSingleton(gctx.New(), "0 * * * * *", func(ctx context.Context) {
//g.Log().Debug(ctx, "每分钟定时器")
s.minutelyTask()
})
//每小时任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 * * * *", func(ctx context.Context) {
g.Log().Debug(ctx, "每小时定时器")
s.hourlyTask()
})
//每天任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * *", func(ctx context.Context) {
g.Log().Debug(ctx, "每日定时器")
s.dailyTask()
})
//每周任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 1", func(ctx context.Context) {
g.Log().Debug(ctx, "每周一定时器")
s.weeklyTask(1)
})
//每周二的任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 2", func(ctx context.Context) {
g.Log().Debug(ctx, "每周二定时器")
s.weeklyTask(2)
})
//周三任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 3", func(ctx context.Context) {
g.Log().Debug(ctx, "周三定时器")
s.weeklyTask(3)
})
//周四任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 4", func(ctx context.Context) {
g.Log().Debug(ctx, "周四定时器")
s.weeklyTask(4)
})
//周五任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 5", func(ctx context.Context) {
g.Log().Debug(ctx, "周五定时器")
s.weeklyTask(5)
})
//周六任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 6", func(ctx context.Context) {
g.Log().Debug(ctx, "周六定时器")
s.weeklyTask(6)
})
//周日任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 * * 0", func(ctx context.Context) {
g.Log().Debug(ctx, "周日定时器")
s.weeklyTask(7)
})
//每月任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 1 * *", func(ctx context.Context) {
g.Log().Debug(ctx, "每月定时器")
s.monthlyTask()
})
//每年任务
_, err = gcron.AddSingleton(gctx.New(), "0 0 0 1 1 *", func(ctx context.Context) {
g.Log().Debug(ctx, "每年定时器")
s.yearlyTask()
})
//统一执行方法
s.RunFuncChan()
return
}
// 每妙任务
func (s *sSystemCron) secondlyTask() {
if len(s.SecondlyTask) == 0 {
return
}
s.AddFuncChan(s.SecondlyTask)
return
}
// 每分钟任务
func (s *sSystemCron) minutelyTask() {
if len(s.MinutelyTask) == 0 {
return
}
s.AddFuncChan(s.MinutelyTask)
return
}
// 每小时任务
func (s *sSystemCron) hourlyTask() {
if len(s.HourlyTask) == 0 {
return
}
s.AddFuncChan(s.HourlyTask)
return
}
// 每天任务
func (s *sSystemCron) dailyTask() {
if len(s.DailyTask) == 0 {
return
}
s.AddFuncChan(s.DailyTask)
return
}
// 每周任务
func (s *sSystemCron) weeklyTask(day int) {
var arr []func(context.Context) error
switch day {
case 1:
arr = s.MondayTask
case 2:
arr = s.TuesdayTask
case 3:
arr = s.WednesdayTask
case 4:
arr = s.ThursdayTask
case 5:
arr = s.FridayTask
case 6:
arr = s.SaturdayTask
case 7:
arr = s.SundayTask
default:
arr = s.WeeklyTask
return
}
if len(arr) == 0 {
return
}
s.AddFuncChan(arr)
return
}
// 每月任务
func (s *sSystemCron) monthlyTask() {
if len(s.MonthlyTask) == 0 {
return
}
s.AddFuncChan(s.MonthlyTask)
return
}
//每年任务
func (s *sSystemCron) yearlyTask() {
if len(s.YearlyTask) == 0 {
return
}
s.AddFuncChan(s.YearlyTask)
}
// AddFuncChan 添加方法到通道
func (s *sSystemCron) AddFuncChan(list []func(context.Context) error) {
for _, v := range list {
s.taskChan <- v
}
}
// RunFuncChan 统一执行方法
func (s *sSystemCron) RunFuncChan() {
go func() {
for task := range s.taskChan {
//ctx := gctx.New()
func() {
//超时释放资源
ctx, cancel := context.WithTimeout(context.Background(), s.TaskTimeout)
defer cancel()
// 使用匿名函数包裹来捕获 panic
defer func() {
if r := recover(); r != nil {
g.Log().Errorf(gctx.New(), "执行函数时发生 panic: %v", r)
}
}()
done := make(chan error)
go func() {
done <- task(ctx)
}()
//err := task()
//if err != nil {
// g.Log().Error(ctx, err)
//}
select {
case taskErr := <-done:
if taskErr != nil {
// 使用新上下文记录错误
g.Log().Error(gctx.New(), taskErr)
}
case <-ctx.Done(): // 监听上下文取消(包括超时)
g.Log().Errorf(gctx.New(), "task timeout:%v", ctx.Err())
}
}()
}
}()
}
// RunFunc 统一执行方法
// deprecated: 弃用会造成周期任务并发执行to service.SystemCron().AddFuncChan
func (s *sSystemCron) RunFunc(list []func() error) {
for _, _func := range list {
ctx := gctx.New()
func() {
// 使用匿名函数包裹来捕获 panic
defer func() {
if r := recover(); r != nil {
g.Log().Errorf(ctx, "执行函数时发生 panic: %v", r)
}
}()
err := _func()
if err != nil {
g.Log().Error(ctx, err)
}
}()
}
return
}