You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

170 lines
4.0 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package action
import (
"context"
"joybuywork/internal/app"
"joybuywork/internal/consts"
"joybuywork/internal/plugins/printer"
"joybuywork/internal/repository/dao"
"joybuywork/internal/repository/model"
"joybuywork/internal/utils"
"joybuywork/internal/utils/merr"
"fmt"
"time"
"github.com/rs/xid"
rl "gits.branchcn.com/backend/microcode/plugins/derivative/lock"
)
const (
loggerModPrint = "print"
loggerModDb = "db"
)
type OnceKey struct {
exp time.Duration //锁过期时间
key string //锁key
release bool //是否直接完毕释放,(有一些可能需要一直锁着)
}
type SyncProgress struct {
total int64 //任务总数
sp chan int64 //当前已处理的任务数
tf func() int64 //计算任务总数的方法
}
type IAction interface {
Init(ctx context.Context) //基于ctx的初始化处理
Exec() error //执行方法
OnceKey() *OnceKey //锁,防止多服务器并发操作
Desc() string //描述,用来做关键字搜索
Alert() bool //推送消息
LoggerMod() string //日志模式
DbExec() (total, successNum, failNum int64, err error) //Db执行方法
}
type Action struct {
ctx context.Context
be *merr.BusinessError
}
func (a *Action) Exec() error {
return nil
}
func (a *Action) DbExec() (total, successNum, failNum int64, err error) {
return
}
func (a *Action) BE() *merr.BusinessError {
return a.be
}
func (a *Action) Alert() bool {
return false
}
func (a *Action) LoggerMod() string {
return loggerModPrint
}
func (a *Action) Init(ctx context.Context) {
a.ctx = ctx
a.be = merr.NewBE(ctx)
}
type Job struct {
R IAction
}
func (s *Job) Run() {
taskId := xid.New().String()
ctx, cancel := context.WithCancel(context.WithValue(context.Background(), consts.TraceIdKey, taskId))
defer cancel()
s.R.Init(ctx)
switch s.R.LoggerMod() {
case loggerModPrint:
s.print(ctx, taskId)
case loggerModDb:
s.db(ctx)
}
}
func (s *Job) db(ctx context.Context) {
//防止多服务器同时执行,用redis上锁
if s.R.OnceKey() != nil {
lock := app.NewRedisLock(ctx, s.R.OnceKey().key, rl.SetExpire(s.R.OnceKey().exp))
if !lock.SoftLock() {
return
}
if s.R.OnceKey().release {
defer lock.Release()
}
}
//执行任务
t := time.Now()
total, successNum, failNum, err := s.R.DbExec()
if total == 0 {
return
}
dao.CronLog.WithContext(ctx).Create(&model.CronLog{
ExecAt: t.Unix(),
Desc: s.R.Desc(),
Machine: app.GetLocalIP(),
Total: total,
SuccessNum: successNum,
FailNum: failNum,
ExecStatus: utils.Ternary[int8](err == nil, 1, 2),
ExecFail: utils.Ternary[string](err == nil, "", fmt.Sprintf("%v", err)),
EndAt: t.Unix(),
})
}
func (s *Job) print(ctx context.Context, taskId string) {
mark := s.R.Desc()
//捕捉运行panic,不用默认的捕捉,因为没有链路追踪
defer func() {
if err := recover(); err != nil {
app.Logger.Error(ctx, fmt.Sprintf("[%s] 执行错误: %v", mark, err))
}
if s.R.Alert() {
app.FeishuNormalSend("task_id: %s, [%s]正常执行", taskId, mark)
}
}()
//防止多服务器同时执行,用redis上锁
if s.R.OnceKey() != nil {
lock := app.NewRedisLock(ctx, s.R.OnceKey().key, rl.SetExpire(s.R.OnceKey().exp))
if !lock.SoftLock() {
return
}
if s.R.OnceKey().release {
defer lock.Release()
}
}
//打印日志,知道这个定时任务执行了
printer.Print(ctx, fmt.Sprintf("执行定时任务:%s", mark), nil)
//执行任务
t := time.Now()
err := s.R.Exec()
if err != nil {
app.Logger.Error(ctx, fmt.Sprintf("[%s] 执行错误: %v", mark, err))
return
}
//打印日志,知道这个定时任务执行了
printer.Print(ctx, fmt.Sprintf("[%s]执行完成,执行时长: %12v", mark, time.Since(t)), err)
}
func NewJob(r IAction) *Job {
return &Job{R: r}
}