Files
ppgo_job/controller/home.go
T
2026-07-10 11:28:16 +08:00

180 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,
}
// 即将执行的任务(Vue 前端期望字段名)
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 {
if t, _ := dao.Task.GetById(ctx, taskId); t != nil && len(upcomingJobs) < 10 {
nextTime := ""
if entry.RegisterTime.Unix() > 0 {
nextTime = entry.RegisterTime.Format("2006-01-02 15:04:05")
}
upcomingJobs = append(upcomingJobs, g.Map{
"task_name": t.TaskName,
"next_time": nextTime,
})
}
}
}
// 过去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)
}