This commit is contained in:
2025-06-20 17:13:51 +08:00
parent 1b55403cd6
commit fd0345a034
472 changed files with 52560 additions and 77 deletions

9
modules/task/README.md Normal file
View File

@@ -0,0 +1,9 @@
# task
任务管理模块,提供基于`coolfun`c 的任务管理功能
## 资源打包命令
```bash
gf pack modules/task/resource modules/task/packed/packed.go -p modules/task/resource
```

View File

@@ -0,0 +1 @@
package admin

View File

@@ -0,0 +1,94 @@
package admin
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/cool-team-official/cool-admin-go/modules/task/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
"github.com/gogf/gf/v2/util/gconv"
)
type TaskInfoController struct {
*cool.Controller
}
func init() {
var task_info_controller = &TaskInfoController{
&cool.Controller{
Prefix: "/admin/task/info",
Api: []string{"Add", "Delete", "Update", "Info", "List", "Page", "Start", "Stop"},
Service: service.NewTaskInfoService(),
},
}
// 注册路由
cool.RegisterController(task_info_controller)
}
// TaskInfoStopReq 请求参数
type TaskInfoStopReq struct {
g.Meta `path:"/stop" method:"GET"`
ID int64 `json:"id" v:"required#请输入id"`
}
// Stop 停止任务
func (c *TaskInfoController) Stop(ctx g.Ctx, req *TaskInfoStopReq) (res *cool.BaseRes, err error) {
err = cool.ClusterRunFunc(ctx, "TaskStopFunc("+gconv.String(req.ID)+")")
if err != nil {
return cool.Fail(err.Error()), err
}
res = cool.Ok("停止成功")
return
}
// TaskInfoStartReq 请求参数
type TaskInfoStartReq struct {
g.Meta `path:"/start" method:"GET"`
ID int64 `json:"id" v:"required#请输入id"`
}
// Start 启动任务
func (c *TaskInfoController) Start(ctx g.Ctx, req *TaskInfoStartReq) (res *cool.BaseRes, err error) {
err = cool.ClusterRunFunc(ctx, "TaskStartFunc("+gconv.String(req.ID)+")")
if err != nil {
return cool.Fail(err.Error()), err
}
res = cool.Ok("启动成功")
return
}
// TaskInfoOnceReq 请求参数
type TaskInfoOnceReq struct {
g.Meta `path:"/once" method:"POST"`
ID int64 `json:"id" v:"required#请输入id"`
}
// Once 执行一次
func (c *TaskInfoController) Once(ctx g.Ctx, req *TaskInfoOnceReq) (res *cool.BaseRes, err error) {
err = c.Service.(*service.TaskInfoService).Once(ctx, req.ID)
if err != nil {
return cool.Fail(err.Error()), err
}
res = cool.Ok("执行成功")
return
}
// TaskInfoLogReq 请求参数
type TaskInfoLogReq struct {
g.Meta `path:"/log" method:"GET"`
ID int64 `json:"id"`
Status int `json:"status"`
}
// Log 任务日志
func (c *TaskInfoController) Log(ctx g.Ctx, req *TaskInfoLogReq) (res *cool.BaseRes, err error) {
r := ghttp.RequestFromCtx(ctx)
param := r.GetQueryMapStrStr()
data, err := c.Service.(*service.TaskInfoService).Log(ctx, param)
if err != nil {
return cool.Fail(err.Error()), err
}
res = cool.Ok(data)
return
}

View File

@@ -0,0 +1 @@
package app

View File

@@ -0,0 +1,7 @@
package controller
import (
_ "github.com/cool-team-official/cool-admin-go/modules/task/controller/admin"
_ "github.com/cool-team-official/cool-admin-go/modules/task/controller/app"
_ "github.com/cool-team-official/cool-admin-go/modules/task/service"
)

View File

@@ -0,0 +1,46 @@
package funcs
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/cool-team-official/cool-admin-go/modules/task/model"
"github.com/cool-team-official/cool-admin-go/modules/task/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type TaskAddTask struct {
}
func (t *TaskAddTask) Func(ctx g.Ctx, id string) error {
taskInfo := model.NewTaskInfo()
result, err := cool.DBM(taskInfo).Where("id = ?", id).One()
if err != nil {
return err
}
if result["taskType"].Int() == 1 {
every := result["every"].Uint() / 1000
cron := "@every " + gconv.String(every) + "s"
funcstring := result["service"].String()
startDate := result["startDate"].String()
err = service.EnableTask(ctx, id, funcstring, cron, startDate)
} else {
cron := result["cron"].String()
funcstring := result["service"].String()
startDate := result["startDate"].String()
err = service.EnableTask(ctx, id, funcstring, cron, startDate)
}
return err
}
func (t *TaskAddTask) IsSingleton() bool {
return false
}
func (t *TaskAddTask) IsAllWorker() bool {
return true
}
func init() {
cool.RegisterFunc("TaskAddTask", &TaskAddTask{})
}

View File

@@ -0,0 +1,33 @@
package funcs
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/cool-team-official/cool-admin-go/modules/task/model"
"github.com/cool-team-official/cool-admin-go/modules/task/service"
"github.com/gogf/gf/v2/frame/g"
)
type TaskStopFunc struct {
}
func (t *TaskStopFunc) Func(ctx g.Ctx, id string) error {
taskInfo := model.NewTaskInfo()
_, err := cool.DBM(taskInfo).Where("id = ?", id).Update(g.Map{"status": 0})
if err != nil {
return err
}
return service.DisableTask(ctx, id)
}
func (t *TaskStopFunc) IsSingleton() bool {
return false
}
func (t *TaskStopFunc) IsAllWorker() bool {
return true
}
func init() {
cool.RegisterFunc("TaskStopFunc", &TaskStopFunc{})
}

View File

@@ -0,0 +1,24 @@
package funcs
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/gogf/gf/v2/frame/g"
)
type TaskTest struct {
}
func (t *TaskTest) Func(ctx g.Ctx, param string) error {
g.Log().Info(ctx, "TaskTest Run ~~~~~~~~~~~~~~~~", param)
return nil
}
func (t *TaskTest) IsSingleton() bool {
return false
}
func (t *TaskTest) IsAllWorker() bool {
return true
}
func init() {
cool.RegisterFunc("TaskTest", &TaskTest{})
}

View File

@@ -0,0 +1,49 @@
package funcs
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/cool-team-official/cool-admin-go/modules/task/model"
"github.com/cool-team-official/cool-admin-go/modules/task/service"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
type TaskStartFunc struct {
}
func (t *TaskStartFunc) Func(ctx g.Ctx, id string) error {
taskInfo := model.NewTaskInfo()
_, err := cool.DBM(taskInfo).Where("id = ?", id).Update(g.Map{"status": 1})
if err != nil {
return err
}
result, err := cool.DBM(taskInfo).Where("id = ?", id).One()
if err != nil {
return err
}
if result["taskType"].Int() == 1 {
every := result["every"].Uint() / 1000
cron := "@every " + gconv.String(every) + "s"
funcstring := result["service"].String()
startDate := result["startDate"].String()
err = service.EnableTask(ctx, id, funcstring, cron, startDate)
} else {
cron := result["cron"].String()
funcstring := result["service"].String()
startDate := result["startDate"].String()
err = service.EnableTask(ctx, id, funcstring, cron, startDate)
}
return err
}
func (t *TaskStartFunc) IsSingleton() bool {
return false
}
func (t *TaskStartFunc) IsAllWorker() bool {
return true
}
func init() {
cool.RegisterFunc("TaskStartFunc", &TaskStartFunc{})
}

40
modules/task/go.mod Normal file
View File

@@ -0,0 +1,40 @@
module github.com/cool-team-official/cool-admin-go/modules/task
go 1.18
require (
github.com/cool-team-official/cool-admin-go/cool v1.5.9
github.com/gogf/gf/v2 v2.6.3
github.com/robfig/cron v1.2.0
)
require (
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rogpeppe/go-internal v1.6.1 // indirect
go.opentelemetry.io/otel v1.24.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/sdk v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gorm.io/gorm v1.25.7 // indirect
)

81
modules/task/go.sum Normal file
View File

@@ -0,0 +1,81 @@
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/cool-team-official/cool-admin-go/cool v1.5.9 h1:mvZkckumdnhkr8BGRbB+FKmUeP3tbxmyvSxfNyZAlhE=
github.com/cool-team-official/cool-admin-go/cool v1.5.9/go.mod h1:kle9oSJM+yl8ZtQwZFL8PWbz7ByI8Glj1431njGbWPo=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gogf/gf/v2 v2.6.3 h1:DoqeuwU98wotpFoDSQEx8RZbmJdK8KdGiJtzJeqpyIo=
github.com/gogf/gf/v2 v2.6.3/go.mod h1:x2XONYcI4hRQ/4gMNbWHmZrNzSEIg20s2NULbzom5k0=
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/robfig/cron v1.2.0 h1:ZjScXvvxeQ63Dbyxy76Fj3AT3Ut0aKsyd2/tl3DTMuQ=
github.com/robfig/cron v1.2.0/go.mod h1:JGuDeoQd7Z6yL4zQhZ3OPEVHB7fL6Ka6skscFHfmt2k=
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
go.opentelemetry.io/otel/sdk v1.24.0 h1:YMPPDNymmQN3ZgczicBY3B6sf9n62Dlj9pWD3ucgoDw=
go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg=
go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/gorm v1.25.7 h1:VsD6acwRjz2zFxGO50gPO6AkNs7KKnvfzUjHQhZDz/A=
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=

View File

@@ -0,0 +1 @@
package middleware

View File

@@ -0,0 +1 @@
package model

View File

@@ -0,0 +1,50 @@
package model
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/gogf/gf/v2/os/gtime"
)
const TableNameTaskInfo = "task_info"
// TaskInfo mapped from table <task_info>
type TaskInfo struct {
*cool.Model
JobId string `json:"jobId" gorm:"column:jobId;type:varchar(255);comment:任务ID"`
RepeatConf string `json:"repeatConf" gorm:"column:repeatConf;comment:重复配置"`
Name string `json:"name" gorm:"column:name;type:varchar(255);comment:任务名称"`
Cron string `json:"cron" gorm:"column:cron;type:varchar(255);comment:cron表达式"`
Limit int `json:"limit" gorm:"column:limit;comment:限制次数 不传为不限制"`
Every int `json:"every" gorm:"column:every;comment:间隔时间 单位秒"`
Remark string `json:"remark" gorm:"column:remark;type:varchar(255);comment:备注"`
Status int `json:"status" gorm:"column:status;comment:状态 0:关闭 1:开启"`
StartDate gtime.Time `json:"startDate" gorm:"column:startDate;comment:开始时间"`
EndDate gtime.Time `json:"endDate" gorm:"column:endDate;comment:结束时间"`
Data string `json:"data" gorm:"column:data;type:varchar(255);comment:数据"`
Service string `json:"service" gorm:"column:service;type:varchar(255);comment:执行的服务"`
Type int `json:"type" gorm:"column:type;comment:类型 0:系统 1:用户"`
NextRunTime gtime.Time `json:"nextRunTime" gorm:"column:nextRunTime;comment:下次执行时间"`
TaskType int `json:"taskType" gorm:"column:taskType;comment:任务类型 0:cron 1:时间间隔"`
}
// TableName TaskInfo's table name
func (*TaskInfo) TableName() string {
return TableNameTaskInfo
}
// GroupName TaskInfo's table group
func (*TaskInfo) GroupName() string {
return "default"
}
// NewTaskInfo create a new TaskInfo
func NewTaskInfo() *TaskInfo {
return &TaskInfo{
Model: cool.NewModel(),
}
}
// init 创建表
func init() {
cool.CreateTable(&TaskInfo{})
}

View File

@@ -0,0 +1,37 @@
package model
import (
"github.com/cool-team-official/cool-admin-go/cool"
)
const TableNameTaskLog = "task_log"
// TaskLog mapped from table <task_log>
type TaskLog struct {
*cool.Model
TaskId uint64 `gorm:"column:taskId;comment:任务ID" json:"taskId"`
Status uint8 `gorm:"column:status;not null;comment:状态 0:失败 1:成功" json:"status"`
Detail string `gorm:"column:detail;comment:详情" json:"detail"`
}
// TableName TaskLog's table name
func (*TaskLog) TableName() string {
return TableNameTaskLog
}
// GroupName TaskLog's table group
func (*TaskLog) GroupName() string {
return "default"
}
// NewTaskLog create a new TaskLog
func NewTaskLog() *TaskLog {
return &TaskLog{
Model: cool.NewModel(),
}
}
// init 创建表
func init() {
cool.CreateTable(&TaskLog{})
}

View File

@@ -0,0 +1,9 @@
package packed
import "github.com/gogf/gf/v2/os/gres"
func init() {
if err := gres.Add("H4sIAAAAAAAC/wrwZmYRYeBgYGDYGxccyoAE5Bg4GXLzU0pzUov1SxKLs/WLUovzS4uSU/Uz8zJLsorz80JDWBkYw576Jwd4s3Mga4UZysFwHM1QXYKGgoXjM/PS8vXgdqQ/9U8umXjWy9hQpO17sWh7ZO38CM+grhlLRR0LVO92nthoOXXjZR/PcyLdO0ICWFjFS6MDjrTuENWqMPZ7nepR37ZNeVLovD17/P02yTPMYWVgSExx4l3IE7789i7llFWuulLJqlO0PAMnSStc37omIzBO2OnJwy25/3i0l2oXGeZoqKkVd8hkbciUOf/Ds2hL8rSJi80Uw4VvRzwTW77s4e+jL94V3ag+ZVf9uVu9zIhTQ+PwqYqn7oVXuriCXk14LbZUt+Ss59/bud9+KJ2K0tB98uHVz931xqF9du82X29ff1Bh97vnb+dNDJbPmlD8oIJ7JpdXdp1zc8/pI5Nfu3w9OWP2i0zrrV+zfF6UTX5cz9tj+/f80Tu5/clK9TJWT/deOVeXvHDiRfPszTkMBXIyf1gYGP7/B0XFYkkJG3VGBob/jLCoAIHzaFEhhjMqwGFeBo5XWOJA1yyKSzOSXuxpAtMsHjSzyDCCHWEEHt2MTCLMuNM6BAgwvHUE0USnfJihkLSOHPS6cEMZGJY0hhI0FFfKh9mBKxJhDv/v+ICRgXCUIkIBW6QiDJNlYiAYxbjN4kExKxPNLKKMYEcxYg3CCCTdrGwgeTYGNobLjAwMr5hAPEAAAAD//+HOjP3TBAAA"); err != nil {
panic("add binary content to resource manager failed: " + err.Error())
}
}

View File

@@ -0,0 +1,23 @@
[
{
"id": "1",
"createTime": "2022-10-19 17:15:03.000",
"updateTime": "2022-10-19 17:22:51.000",
"deleted_at": null,
"jobId": null,
"repeatConf": null,
"name": "清理日志",
"cron": "1 2 3 * * *",
"limit": null,
"every": null,
"remark": "每天03:02:01执行清理缓存任务",
"status": 1,
"startDate": null,
"endDate": null,
"data": null,
"service": "BaseFuncClearLog(false)",
"type": 0,
"nextRunTime": null,
"taskType": 0
}
]

View File

@@ -0,0 +1,63 @@
package service
import (
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gcron"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gstr"
)
// EnableTask 启用任务
func EnableTask(ctx g.Ctx, cronId string, funcstring string, cron string, startDate string) (err error) {
funcName := gstr.SubStr(funcstring, 0, gstr.Pos(funcstring, "("))
if _, ok := cool.FuncMap[funcName]; !ok {
err = gerror.New("函数不存在" + funcName)
return
}
taskInfoService := NewTaskInfoService()
if cool.FuncMap[funcName].IsSingleton() {
gcron.Remove(cronId)
_, err = gcron.AddSingleton(ctx, cron, func(ctx g.Ctx) {
nowDate := gtime.Now().Format("Y-m-d H:i:s")
if nowDate < startDate {
g.Log().Debug(ctx, "当前时间小于启用时间, 不执行单例函数", funcName)
return
}
err := cool.RunFunc(ctx, funcstring)
if err != nil {
g.Log().Error(ctx, err)
taskInfoService.Record(ctx, cronId, 0, err.Error())
} else {
taskInfoService.Record(ctx, cronId, 1, "任务执行成功")
}
}, cronId)
} else {
gcron.Remove(cronId)
_, err = gcron.Add(ctx, cron, func(ctx g.Ctx) {
nowDate := gtime.Now().Format("Y-m-d H:i:s")
if nowDate < startDate {
g.Log().Debug(ctx, "当前时间小于启用时间, 不执行函数", funcName)
return
}
err := cool.RunFunc(ctx, funcstring)
if err != nil {
g.Log().Error(ctx, err)
taskInfoService.Record(ctx, cronId, 0, gstr.AddSlashes(err.Error()))
} else {
taskInfoService.Record(ctx, cronId, 1, gstr.AddSlashes("任务执行成功"))
}
taskInfoService.SetNextRunTime(ctx, cronId, cron)
}, cronId)
}
taskInfoService.SetNextRunTime(ctx, cronId, cron)
return
}
// DisableTask 禁用任务
func DisableTask(ctx g.Ctx, cronId string) (err error) {
gcron.Remove(cronId)
return
}

View File

@@ -0,0 +1,185 @@
package service
import (
"time"
"github.com/cool-team-official/cool-admin-go/cool"
"github.com/cool-team-official/cool-admin-go/modules/task/model"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
"github.com/robfig/cron"
)
type TaskInfoService struct {
*cool.Service
}
func NewTaskInfoService() *TaskInfoService {
return &TaskInfoService{
&cool.Service{
Model: model.NewTaskInfo(),
PageQueryOp: &cool.QueryOp{
FieldEQ: []string{"status", "type"},
},
UniqueKey: map[string]string{
"name": "任务名称不能重复",
},
},
}
}
func (s *TaskInfoService) ModifyAfter(ctx g.Ctx, method string, param g.MapStrAny) (err error) {
g.Log().Info(ctx, "TaskInfoService.ModifyAfter", method, param)
if method == "Add" {
if gconv.Int(param["status"]) == 1 {
id, err := cool.DBM(s.Model).Where("name = ?", param["name"]).Value("id")
if err != nil {
return err
}
return cool.ClusterRunFunc(ctx, "TaskAddTask("+id.String()+")")
}
}
if method == "Update" {
id := gconv.String(param["id"])
if gconv.Int(param["status"]) == 1 {
return cool.ClusterRunFunc(ctx, "TaskStartFunc("+id+")")
} else {
return cool.ClusterRunFunc(ctx, "TaskStopFunc("+id+")")
}
}
if method == "Delete" {
id := gconv.String(param["id"])
return cool.ClusterRunFunc(ctx, "TaskStopFunc("+id+")")
}
return nil
}
// Record 保存任务记录,成功任务每个任务保留最新20条日志,失败日志不会删除
func (s *TaskInfoService) Record(ctx g.Ctx, id string, status int, detail string) error {
taskLog := model.NewTaskLog()
_, err := cool.DBM(taskLog).Data(g.Map{
"taskId": id,
"status": status,
"detail": detail,
}).Insert()
if err != nil {
return err
}
if status == 1 {
record, err := cool.DBM(taskLog).Where("taskId = ?", id).Where("status", 1).Order("id", "desc").Offset(19).One()
if err != nil {
return err
}
if record.IsEmpty() {
return nil
}
minId := record["id"].Int()
if err != nil {
return err
}
_, err = cool.DBM(taskLog).Where("taskId = ?", id).Where("status", 1).Where("id < ?", minId).Delete()
if err != nil {
return err
}
}
return err
}
// Once 执行一次任务
func (s *TaskInfoService) Once(ctx g.Ctx, id int64) error {
record, err := cool.DBM(s.Model).Where("id = ?", id).One()
if err != nil {
return err
}
if record.IsEmpty() {
return gerror.New("任务不存在")
}
funcString := record["service"].String()
return cool.ClusterRunFunc(ctx, funcString)
}
// Log 获取任务日志
func (s *TaskInfoService) Log(ctx g.Ctx, param g.MapStrStr) (data interface{}, err error) {
var (
Total = 0
Page = 1
Size = 10
)
taskLog := model.NewTaskLog()
m := cool.DBM(taskLog).LeftJoin("task_info", "task_info.id = task_log.taskId")
if id, ok := param["id"]; ok {
m = m.Where("taskId = ?", id)
}
if status, ok := param["status"]; ok {
m = m.Where("status = ?", status)
}
Total, err = m.Clone().Count()
m = m.Fields("task_log.*,task_info.name as taskName")
if err != nil {
return nil, err
}
if page, ok := param["page"]; ok {
Page = gconv.Int(page)
}
if size, ok := param["size"]; ok {
Size = gconv.Int(size)
}
m = m.Limit(Size).Offset((Page - 1) * Size)
result, err := m.Order("id", "desc").All()
// g.Dump(result)
if err != nil {
return nil, err
}
if result.IsEmpty() {
return g.Map{
"list": g.Slice{},
"pagination": g.Map{
"total": Total,
"size": Size,
"page": Page,
},
}, nil
}
// g.Log().Info(ctx, "TaskInfoService.Log", result)
data = g.Map{
"list": result,
"pagination": g.Map{
"total": Total,
"size": Size,
"page": Page,
},
}
return
}
// SetNextRunTime 更新下次执行时间
func (s *TaskInfoService) SetNextRunTime(ctx g.Ctx, cronId string, cron string) error {
// 更新下次执行时间
nextTime, e := getCronNextTime(cron, time.Now())
if e == nil {
_, err := cool.DBM(s.Model).Where("id = ?", cronId).Data("nextRunTime", nextTime).Update()
if err != nil {
return err
}
} else {
g.Log().Debug(ctx, "获取下次执行时间失败", e)
}
return nil
}
// getCronNextTime 获取下一次Cron的执行时间
func getCronNextTime(cronStr string, t time.Time) (nextTime time.Time, err error) {
p := cron.NewParser(cron.Second | cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor)
s, err := p.Parse(cronStr)
if err != nil {
return
}
nextTime = s.Next(t)
return
}

33
modules/task/task.go Normal file
View File

@@ -0,0 +1,33 @@
package demo
import (
_ "github.com/cool-team-official/cool-admin-go/modules/task/packed"
"github.com/cool-team-official/cool-admin-go/cool"
_ "github.com/cool-team-official/cool-admin-go/modules/task/controller"
_ "github.com/cool-team-official/cool-admin-go/modules/task/funcs"
_ "github.com/cool-team-official/cool-admin-go/modules/task/middleware"
"github.com/cool-team-official/cool-admin-go/modules/task/model"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func init() {
var (
taskInfo = model.NewTaskInfo()
ctx = gctx.GetInitCtx()
)
g.Log().Debug(ctx, "module task init start ...")
cool.FillInitData(ctx, "task", taskInfo)
result, err := cool.DBM(taskInfo).Where("status = ?", 1).All()
if err != nil {
panic(err)
}
for _, v := range result {
id := v["id"].String()
cool.RunFunc(ctx, "TaskAddTask("+id+")")
}
g.Log().Debug(ctx, "module task init finished ...")
}