178 lines
4.7 KiB
Go
178 lines
4.7 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"runtime"
|
|
"time"
|
|
|
|
"ppgo_job/dao"
|
|
"ppgo_job/service/scheduler"
|
|
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/net/ghttp"
|
|
)
|
|
|
|
// ApiDashboard 仪表盘数据(Vue 前端使用)
|
|
func ApiDashboard(r *ghttp.Request) {
|
|
ctx := r.GetCtx()
|
|
taskTotal, taskTotalCnt, _ := dao.Task.GetList(ctx, 1, 1000)
|
|
logNum, _ := dao.TaskLog.GetLogNum(ctx)
|
|
totalRunNum, _ := dao.Task.TotalRunNum(ctx)
|
|
|
|
// 统计卡片(使用 total 计数而非 len)
|
|
auditTaskCount := 0
|
|
for _, t := range taskTotal {
|
|
if t.Status == 2 {
|
|
auditTaskCount++
|
|
}
|
|
}
|
|
_, successTotal, _ := dao.TaskLog.GetList(ctx, 1, 1, "status", 0)
|
|
_, errorTotal, _ := dao.TaskLog.GetList(ctx, 1, 1, "status", -1)
|
|
adminTotal, _, _ := dao.Admin.GetList(ctx, 1, 1000)
|
|
|
|
// 获取正在调度的任务数
|
|
startJob := 0
|
|
for _, t := range taskTotal {
|
|
if t.Status == 1 {
|
|
startJob++
|
|
}
|
|
}
|
|
|
|
stats := g.Map{
|
|
"startJob": startJob,
|
|
"totalAuditTask": auditTaskCount,
|
|
"successNum": successTotal,
|
|
"errorNum": errorTotal,
|
|
"userNum": len(adminTotal),
|
|
"totalJob": taskTotalCnt,
|
|
"totalRunNum": totalRunNum,
|
|
"totalLog": logNum,
|
|
}
|
|
|
|
// 即将执行的任务(基于 cron 表达式计算下次执行时间)
|
|
upcomingJobs := make([]g.Map, 0)
|
|
for _, entry := range scheduler.Scheduler.GetEntries() {
|
|
var taskId int
|
|
if _, err := fmt.Sscanf(entry.Name, "task_%d", &taskId); err == nil && taskId > 0 {
|
|
t, _ := dao.Task.GetById(ctx, taskId)
|
|
if t == nil || len(upcomingJobs) >= 10 {
|
|
continue
|
|
}
|
|
upcomingJobs = append(upcomingJobs, g.Map{
|
|
"task_name": t.TaskName,
|
|
"next_time": scheduler.GetTaskNextTime(t.CronSpec),
|
|
})
|
|
}
|
|
}
|
|
|
|
// 过去7天按状态统计
|
|
weekAgo := time.Now().AddDate(0, 0, -7).Unix()
|
|
now := time.Now().Unix()
|
|
dayStatRaw, _ := g.DB().Model("pp_task_log").Ctx(ctx).
|
|
Fields("strftime('%Y-%m-%d', create_time, 'unixepoch') as date, status, COUNT(*) as cnt").
|
|
Where("create_time >= ? AND create_time < ?", weekAgo, now).
|
|
Group("date, status").
|
|
Order("date ASC").
|
|
All()
|
|
|
|
// 建立 (date → status → count) 映射
|
|
type dayKey struct {
|
|
date string
|
|
status int
|
|
}
|
|
dayMap := make(map[dayKey]int)
|
|
for _, row := range dayStatRaw {
|
|
date := row["date"].String()
|
|
status := row["status"].Int()
|
|
cnt := row["cnt"].Int()
|
|
dayMap[dayKey{date, status}] = cnt
|
|
}
|
|
|
|
daysStr := make([]string, 7)
|
|
okNums := make([]int, 7)
|
|
errNums := make([]int, 7)
|
|
expiredNums := make([]int, 7)
|
|
for i := 0; i < 7; i++ {
|
|
d := time.Now().AddDate(0, 0, -6+i)
|
|
dateStr := d.Format("2006-01-02")
|
|
daysStr[i] = dateStr
|
|
okNums[i] = dayMap[dayKey{dateStr, 0}]
|
|
errNums[i] = dayMap[dayKey{dateStr, -1}]
|
|
expiredNums[i] = dayMap[dayKey{dateStr, -2}]
|
|
}
|
|
chartData := g.Map{
|
|
"days": daysStr,
|
|
"okNum": okNums,
|
|
"errNum": errNums,
|
|
"expiredNum": expiredNums,
|
|
}
|
|
|
|
// 最近错误(转成前端期望的字段名)
|
|
recentErrList, _, _ := dao.TaskLog.GetList(ctx, 1, 10, "status", -1)
|
|
recentErrors := make([]g.Map, 0)
|
|
for _, e := range recentErrList {
|
|
taskName := ""
|
|
if t, _ := dao.Task.GetById(ctx, e.TaskId); t != nil {
|
|
taskName = t.TaskName
|
|
}
|
|
startTime := ""
|
|
if e.CreateTime > 0 {
|
|
startTime = time.Unix(e.CreateTime, 0).Format("2006-01-02 15:04:05")
|
|
}
|
|
recentErrors = append(recentErrors, g.Map{
|
|
"task_name": taskName,
|
|
"start_time": startTime,
|
|
"status": e.Status,
|
|
})
|
|
}
|
|
|
|
jsonRes(r, 0, "ok", g.Map{
|
|
"stats": stats,
|
|
"chartData": chartData,
|
|
"upcomingJobs": upcomingJobs,
|
|
"recentErrors": recentErrors,
|
|
"sysInfo": SystemInfo(ctx),
|
|
})
|
|
}
|
|
|
|
// Help Cron 帮助页面
|
|
func Help(r *ghttp.Request) {
|
|
r.Response.WriteTpl("public/help.html", g.Map{
|
|
"pageTitle": "Cron 表达式帮助",
|
|
"siteName": g.Cfg().MustGet(r.GetCtx(), "site.name", "定时任务管理器").String(),
|
|
})
|
|
}
|
|
|
|
// SystemInfo 获取系统信息
|
|
func SystemInfo(ctx context.Context) g.Map {
|
|
memStats := &runtime.MemStats{}
|
|
runtime.ReadMemStats(memStats)
|
|
|
|
return g.Map{
|
|
"version": g.Cfg().MustGet(ctx, "version", "V2.8").String(),
|
|
"goroutineNum": runtime.NumGoroutine(),
|
|
"memoryUsed": FileSize(int64(memStats.Alloc)),
|
|
"memorySys": FileSize(int64(memStats.Sys)),
|
|
"memoryPercent": fmt.Sprintf("%.1f", float64(memStats.Alloc)/float64(memStats.Sys)*100),
|
|
}
|
|
}
|
|
|
|
func toJSON(v interface{}) string {
|
|
b, _ := json.Marshal(v)
|
|
return string(b)
|
|
}
|
|
|
|
// FileSize 格式化文件大小
|
|
func FileSize(s int64) string {
|
|
if s < 1024 {
|
|
return fmt.Sprintf("%d B", s)
|
|
} else if s < 1024*1024 {
|
|
return fmt.Sprintf("%.1f KB", float64(s)/1024)
|
|
} else if s < 1024*1024*1024 {
|
|
return fmt.Sprintf("%.1f MB", float64(s)/1024/1024)
|
|
}
|
|
return fmt.Sprintf("%.1f GB", float64(s)/1024/1024/1024)
|
|
}
|