diff --git a/conf/app.conf b/conf/app.conf index 97bde8f..9d5c9a8 100644 --- a/conf/app.conf +++ b/conf/app.conf @@ -10,6 +10,9 @@ jobs.pool = 1000 # 站点名称 site.name = 定时任务管理器 +#通知方式 0=邮件,1=信息 +notify.type = 0 + # 数据库配置 db.host = 127.0.0.1 @@ -19,3 +22,16 @@ db.port = 3306 db.name = ppgo_job2 db.prefix = pp_ db.timezone = Asia/Shanghai + +# 邮件通知配置 +email.host = smtp.mxhichina.com +email.port = 25 +email.from = ci@xxx.cn +email.user = ci@xxx.cn +email.password = "xxx@123" +email.pool = 10 + + +# 其他通知方式 +msg.url = http://xxx.com/sms/url?id=12&msg=12121 + diff --git a/controllers/common.go b/controllers/common.go index 4f505d6..c7316e4 100644 --- a/controllers/common.go +++ b/controllers/common.go @@ -285,6 +285,42 @@ func serverListByGroupId(groupId int) []string { return servers } +type adminInfo struct { + Id int + Email string + Phone string + RealName string +} + +func AllAdminInfo(adminIds string) []*adminInfo { + Filters := make([]interface{}, 0) + Filters = append(Filters, "status", 1) + //Filters = append(Filters, "id__gt", 1) + var notifyUserIds []int + if adminIds != "0" && adminIds != "" { + notifyUserIdsStr := strings.Split(adminIds, ",") + for _, v := range notifyUserIdsStr { + i, _ := strconv.Atoi(v) + notifyUserIds = append(notifyUserIds, i) + } + Filters = append(Filters, "id__in", notifyUserIds) + } + Result, _ := models.AdminGetList(1, 1000, Filters...) + + adminInfos := make([]*adminInfo, 0) + for _, v := range Result { + ai := adminInfo{ + Id: v.Id, + Email: v.Email, + Phone: v.Phone, + RealName: v.RealName, + } + adminInfos = append(adminInfos, &ai) + } + + return adminInfos +} + type serverList struct { GroupId int GroupName string diff --git a/controllers/role.go b/controllers/role.go index ed29055..35aa115 100644 --- a/controllers/role.go +++ b/controllers/role.go @@ -46,7 +46,6 @@ func (self *RoleController) Edit() { row["id"] = role.Id row["role_name"] = role.RoleName row["detail"] = role.Detail - row["detail"] = role.Detail row["task_group_ids"] = role.TaskGroupIds row["server_group_ids"] = role.ServerGroupIds self.Data["role"] = row diff --git a/controllers/task.go b/controllers/task.go index add29b6..958e824 100644 --- a/controllers/task.go +++ b/controllers/task.go @@ -12,6 +12,7 @@ import ( "strings" "time" + "fmt" "github.com/astaxie/beego" "github.com/george518/PPGo_Job/jobs" "github.com/george518/PPGo_Job/models" @@ -38,6 +39,9 @@ func (self *TaskController) Add() { self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId) self.Data["serverGroup"] = serverLists(self.serverGroups, self.userId) self.Data["isAdmin"] = self.userId + self.Data["adminInfo"] = AllAdminInfo("") + + fmt.Println(self.Data["adminInfo"]) self.display() } @@ -55,15 +59,28 @@ func (self *TaskController) Edit() { } self.Data["task"] = task + self.Data["adminInfo"] = AllAdminInfo("") + // 分组列表 self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId) self.Data["serverGroup"] = serverLists(self.serverGroups, self.userId) self.Data["isAdmin"] = self.userId + var notifyUserIds []int + if task.NotifyUserIds != "0" { + notifyUserIdsStr := strings.Split(task.NotifyUserIds, ",") + for _, v := range notifyUserIdsStr { + i, _ := strconv.Atoi(v) + notifyUserIds = append(notifyUserIds, i) + } + } + + self.Data["notify_user_ids"] = notifyUserIds self.display() } func (self *TaskController) Copy() { self.Data["pageTitle"] = "复制任务" + self.Data["adminInfo"] = AllAdminInfo("") id, _ := self.GetInt("id") task, err := models.TaskGetById(id) @@ -75,6 +92,15 @@ func (self *TaskController) Copy() { // 分组列表 self.Data["taskGroup"] = taskGroupLists(self.taskGroups, self.userId) self.Data["serverGroup"] = serverLists(self.serverGroups, self.userId) + var notifyUserIds []int + if task.NotifyUserIds != "0" { + notifyUserIdsStr := strings.Split(task.NotifyUserIds, ",") + for _, v := range notifyUserIdsStr { + i, _ := strconv.Atoi(v) + notifyUserIds = append(notifyUserIds, i) + } + } + self.Data["notify_user_ids"] = notifyUserIds self.display() } @@ -138,6 +164,13 @@ func (self *TaskController) Detail() { updateName = admin.RealName } } + + //是否出错通知 + self.Data["adminInfo"] = []int{0} + fmt.Println(task.NotifyUserIds) + if task.NotifyUserIds != "0" && task.NotifyUserIds != "" { + self.Data["adminInfo"] = AllAdminInfo(task.NotifyUserIds) + } self.Data["CreateName"] = createName self.Data["UpdateName"] = updateName self.Data["serverName"] = serverName @@ -157,6 +190,9 @@ func (self *TaskController) AjaxSave() { task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.Command = strings.TrimSpace(self.GetString("command")) task.Timeout, _ = self.GetInt("timeout") + task.IsNotify, _ = self.GetInt("is_notify") + task.NotifyType, _ = self.GetInt("notify_type") + task.NotifyUserIds = strings.TrimSpace(self.GetString("notify_user_ids")) msg, isBan := checkCommand(task.Command) if !isBan { @@ -194,6 +230,9 @@ func (self *TaskController) AjaxSave() { task.CronSpec = strings.TrimSpace(self.GetString("cron_spec")) task.Command = strings.TrimSpace(self.GetString("command")) task.Timeout, _ = self.GetInt("timeout") + task.IsNotify, _ = self.GetInt("is_notify") + task.NotifyType, _ = self.GetInt("notify_type") + task.NotifyUserIds = strings.TrimSpace(self.GetString("notify_user_ids")) task.UpdateId = self.userId task.Status = 2 //审核中,超级管理员不需要 if self.userId == 1 { diff --git a/jobs/job.go b/jobs/job.go index 6a39a2e..e28743e 100644 --- a/jobs/job.go +++ b/jobs/job.go @@ -18,7 +18,10 @@ import ( "github.com/astaxie/beego" "github.com/george518/PPGo_Job/models" + "github.com/george518/PPGo_Job/notify" "golang.org/x/crypto/ssh" + "strconv" + "strings" ) type Job struct { @@ -187,7 +190,6 @@ func RemoteCommandJobByPassword(id int, name string, command string, servers *mo var c bytes.Buffer session.Stdout = &b session.Stderr = &c - //session.Output(command) if err := session.Run(command); err != nil { return "", "", err, false @@ -264,6 +266,90 @@ func (j *Job) Run() { log.Status = models.TASK_ERROR log.Error = err.Error() + ":" + cmdErr } + fmt.Println() + fmt.Println() + fmt.Println(log.Status, j.task.IsNotify) + + if log.Status < 0 && j.task.IsNotify == 1 { + fmt.Println() + fmt.Println() + fmt.Println(j.task.NotifyUserIds) + + fmt.Println() + + fmt.Println(j.task.NotifyUserIds != "0") + fmt.Println(j.task.NotifyUserIds != "") + if j.task.NotifyUserIds != "0" && j.task.NotifyUserIds != "" { + admin_info := AllAdminInfo(j.task.NotifyUserIds) + fmt.Println("ADMIN:", admin_info) + phone := make([]string, 0) + toEmail := "" + for _, v := range admin_info { + if v.Phone != "0" && v.Phone != "" { + phone = append(phone, v.Phone) + } + if v.Email != "0" && v.Email != "" { + toEmail += v.Email + ";" + } + } + toEmail = strings.TrimRight(toEmail, ";") + + fmt.Println("EMAIL:", toEmail) + fmt.Println("TYPE:", j.task.NotifyType) + + TextStatus := []string{ + "超时", + "错误", + "正常", + } + + status := log.Status + 2 + + if j.task.NotifyType == 0 && toEmail != "" { + //邮件 + //SendToChan(to, subject, body, mailtype string) bool + subject := fmt.Sprintf("PPGo_Job定时任务异常:%s", j.task.TaskName) + body := fmt.Sprintf( + `Hello,定时任务出问题了: +

任务执行详情:

+

+任务 ID:%d
+任务名称:%s
+执行时间:%s
+执行耗时:%f秒
+执行状态:%s +

+

任务执行输出

+

+%s +

+
+
+

-----------------------------------------------------------------
+本邮件由PPGo_Job定时系统自动发出,请勿回复
+如果要取消邮件通知,请登录到系统进行设置
+

+`, j.task.Id, + j.task.TaskName, + beego.Date(time.Unix(log.CreateTime, 0), "Y-m-d H:i:s"), + float64(log.ProcessTime)/1000, + TextStatus[status], + log.Error) + mailtype := "html" + + ok := notify.SendToChan(toEmail, subject, body, mailtype) + if !ok { + fmt.Println("发送邮件错误", toEmail) + } + + } else if j.task.NotifyType == 1 { + //信息 + + } + + } + } + j.logId, _ = models.TaskLogAdd(log) // 更新上次执行时间 @@ -271,3 +357,40 @@ func (j *Job) Run() { j.task.ExecuteTimes++ j.task.Update("PrevTime", "ExecuteTimes") } + +//冗余代码 +type adminInfo struct { + Id int + Email string + Phone string + RealName string +} + +func AllAdminInfo(adminIds string) []*adminInfo { + Filters := make([]interface{}, 0) + Filters = append(Filters, "status", 1) + //Filters = append(Filters, "id__gt", 1) + var notifyUserIds []int + if adminIds != "0" && adminIds != "" { + notifyUserIdsStr := strings.Split(adminIds, ",") + for _, v := range notifyUserIdsStr { + i, _ := strconv.Atoi(v) + notifyUserIds = append(notifyUserIds, i) + } + Filters = append(Filters, "id__in", notifyUserIds) + } + Result, _ := models.AdminGetList(1, 1000, Filters...) + + adminInfos := make([]*adminInfo, 0) + for _, v := range Result { + ai := adminInfo{ + Id: v.Id, + Email: v.Email, + Phone: v.Phone, + RealName: v.RealName, + } + adminInfos = append(adminInfos, &ai) + } + + return adminInfos +} diff --git a/models/task.go b/models/task.go index 4603b94..80ac03b 100644 --- a/models/task.go +++ b/models/task.go @@ -21,22 +21,25 @@ const ( ) type Task struct { - Id int - GroupId int - ServerId int - TaskName string - Description string - CronSpec string - Concurrent int - Command string - Timeout int - ExecuteTimes int - PrevTime int64 - Status int - CreateId int - UpdateId int - CreateTime int64 - UpdateTime int64 + Id int + GroupId int + ServerId int + TaskName string + Description string + CronSpec string + Concurrent int + Command string + Timeout int + ExecuteTimes int + PrevTime int64 + Status int + IsNotify int + NotifyType int + NotifyUserIds string + CreateId int + UpdateId int + CreateTime int64 + UpdateTime int64 } func (t *Task) TableName() string { diff --git a/notify/email.go b/notify/email.go new file mode 100644 index 0000000..94adf46 --- /dev/null +++ b/notify/email.go @@ -0,0 +1,104 @@ +/************************************************************ +** @Description: notify +** @Author: george hao +** @Date: 2018-08-08 12:59 +** @Last Modified by: george hao +** @Last Modified time: 2018-08-08 12:59 +*************************************************************/ +package notify + +import ( + "github.com/astaxie/beego" + "net/smtp" + "strings" + "time" +) + +type PEmailConfig struct { + Host string + Port string + User string + Pwd string + From string +} + +type PEmail struct { + Config *PEmailConfig + Subject string + Body string + To string + Format string +} + +var ( + mailChan chan *PEmail + config *PEmailConfig +) + +func init() { + + poolSize, _ := beego.AppConfig.Int("email.pool") + host := beego.AppConfig.String("email.host") + port := beego.AppConfig.String("email.port") + user := beego.AppConfig.String("email.user") + pwd := beego.AppConfig.String("email.password") + from := beego.AppConfig.String("email.from") + + config = &PEmailConfig{ + Host: host, + From: from, + Port: port, + User: user, + Pwd: pwd, + } + + //创建通道 + mailChan = make(chan *PEmail, poolSize) + + go func() { + for { + select { + case m, ok := <-mailChan: + if !ok { + return + } + if err := m.SendToEmail(); err != nil { + beego.Error("SendMail:", err.Error()) + } + } + } + }() +} + +func SendToChan(to, subject, body, mailtype string) bool { + email := &PEmail{ + Config: config, + Body: body, + Subject: subject, + Format: mailtype, + To: to, + } + select { + case mailChan <- email: + return true + case <-time.After(time.Second * 3): + return false + } + +} + +func (pe *PEmail) SendToEmail() error { + auth := smtp.PlainAuth("", pe.Config.User, pe.Config.Pwd, pe.Config.Host) + var contentType string + if pe.Format == "html" { + contentType = "Content-Type: text/" + pe.Format + "; charset=UTF-8" + } else { + contentType = "Content-Type: text/plain" + "; charset=UTF-8" + } + + msg := []byte("To: " + pe.To + "\r\nFrom: " + pe.Config.User + + "\r\nSubject: " + pe.Subject + "\r\n" + contentType + "\r\n\r\n" + pe.Body) + sendTo := strings.Split(pe.To, ";") + err := smtp.SendMail(pe.Config.Host+":"+pe.Config.Port, auth, pe.Config.User, sendTo, msg) + return err +} diff --git a/views/task/add.html b/views/task/add.html index 2a2da8c..21d0040 100644 --- a/views/task/add.html +++ b/views/task/add.html @@ -1,3 +1,8 @@ +
@@ -49,8 +54,8 @@
- - + +
@@ -78,6 +83,38 @@
+ +
+ +
+ + +
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ +
+ {{range $k, $v := .adminInfo}} + + {{end}} + +
+
+
+ +
@@ -106,6 +143,30 @@ }) + var notify_user_ids = []; + form.on('checkbox(notify_user)', function(data){ + if(data.elem.checked==true){ + notify_user_ids.push(data.value) + }else{ + $.each(notify_user_ids,function(index,item){ + // index是索引值(即下标) item是每次遍历得到的值; + if(item==data.value){ + notify_user_ids.splice(index,1); + } + }); + } + $("#notify_user_ids").val(notify_user_ids.join(",")); + }); + form.on('radio(is_notify)', function(data){ + if(data.value==1){ + $(".notify").show() + }else{ + $(".notify").hide() + } + }); + + + form.on('submit(sub)', function(data){ var form_data = data.field; $.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) { diff --git a/views/task/copy.html b/views/task/copy.html index 3ea3edc..eb75da4 100644 --- a/views/task/copy.html +++ b/views/task/copy.html @@ -1,3 +1,8 @@ +
@@ -79,6 +84,36 @@
+ +
+ +
+ + +
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ +
+ {{range $k, $v := .adminInfo}} + + {{end}} + +
+
+
@@ -106,6 +141,32 @@ }); }) + if ($("input[name=is_notify]:checked").val()==1){ + $(".notify").show(); + } + form.on('radio(is_notify)', function(data){ + if(data.value==1){ + $(".notify").show() + }else{ + $(".notify").hide() + } + }); + + var notify_user_ids = []; + form.on('checkbox(notify_user)', function(data){ + if(data.elem.checked==true){ + notify_user_ids.push(data.value) + }else{ + $.each(notify_user_ids,function(index,item){ + // index是索引值(即下标) item是每次遍历得到的值; + if(item==data.value){ + notify_user_ids.splice(index,1); + } + }); + } + $("#notify_user_ids").val(notify_user_ids.join(",")); + }); + form.on('submit(sub)', function(data){ var form_data = data.field; $.post('{{urlfor "TaskController.AjaxSave"}}', form_data, function (out) { diff --git a/views/task/detail.html b/views/task/detail.html index 567b78e..327fa1d 100644 --- a/views/task/detail.html +++ b/views/task/detail.html @@ -90,6 +90,31 @@ + + 出错通知 + {{if eq .task.IsNotify 0}}否{{end}} {{if eq .task.IsNotify 1}}否{{end}} + + + + {{if eq .task.IsNotify 1}} + + 通知类型 + {{if eq .task.NotifyType 1}}短信{{end}} {{if eq .task.NotifyType 0}}邮件{{end}} + + + + + 通知用户 + + {{range $k, $v := .adminInfo}} + {{$v.RealName}}     + {{end}} + + + + + {{end}} + 创建时间 {{.CreateTime}} diff --git a/views/task/edit.html b/views/task/edit.html index ea8973f..7e1ff70 100644 --- a/views/task/edit.html +++ b/views/task/edit.html @@ -1,3 +1,8 @@ +
@@ -78,6 +83,36 @@
+ +
+ +
+ + +
+
+
+ +
+
+ +
+ + +
+
+
+ +
+ +
+ {{range $k, $v := .adminInfo}} + + {{end}} + +
+
+
@@ -107,6 +142,32 @@ }); }) + if ($("input[name=is_notify]:checked").val()==1){ + $(".notify").show(); + } + form.on('radio(is_notify)', function(data){ + if(data.value==1){ + $(".notify").show() + }else{ + $(".notify").hide() + } + }); + + var notify_user_ids = []; + form.on('checkbox(notify_user)', function(data){ + if(data.elem.checked==true){ + notify_user_ids.push(data.value) + }else{ + $.each(notify_user_ids,function(index,item){ + // index是索引值(即下标) item是每次遍历得到的值; + if(item==data.value){ + notify_user_ids.splice(index,1); + } + }); + } + $("#notify_user_ids").val(notify_user_ids.join(",")); + }); + form.on('submit(sub)', function(data){ var isAdmin = "{{.isAdmin}}"; var msg = "编辑任务需要重新审核,是否确认需要编辑?";