jianghan7 2 vuotta sitten
vanhempi
commit
82fc9eb0d8

+ 3 - 4
src/lua/spiderwarn.go

@@ -8,7 +8,6 @@ import (
 	"qfw/util/redis"
 	"strings"
 	"time"
-	"udptask"
 	. "util"
 
 	"github.com/go-xweb/xweb"
@@ -202,9 +201,9 @@ func (l *Lua) SpiderSave() {
 			b := JYMgo.UpdateById("bidding", bid, map[string]interface{}{"$set": update}) //更新
 			if b {                                                                        //更新成功                                                                   //更新成功 重生bidding索引 项目合并
 				qu.Debug("更新成功")
-				udptask.BiddingIndexUdp(bid, "bidding") //coll:bidding 是因为调用此方法的数据都是增量数据
-				if IsModifyPro(modifyinfo) {            //修改了影响项目合并的字段 进行项目合并
-					udptask.ProjectSetUdp(bid, "bidding")
+				BiddingIndexUdp(bid, "bidding") //coll:bidding 是因为调用此方法的数据都是增量数据
+				if IsModifyPro(modifyinfo) {    //修改了影响项目合并的字段 进行项目合并
+					ProjectSetUdp(bid, "bidding")
 				}
 				delName1 := RedisDelKey1 + "*_" + bid
 				redis.DelByCodePattern(RedisJYName, delName1)

+ 23 - 3
src/main.go

@@ -2,13 +2,14 @@ package main
 
 import (
 	_ "filter"
+	"fmt"
 	"front"
+	"github.com/cron"
 	"log"
 	"lua"
 	qu "qfw/util"
 	"service"
 	"time"
-	. "udptask"
 	"util"
 
 	"github.com/go-xweb/xweb"
@@ -18,7 +19,7 @@ func init() {
 	qu.ReadConfig(&util.Sysconfig) //初始化配置
 	util.InitMgoPool()             //初始化连接
 	util.InitOther()
-	InitUdp()
+	util.InitUdp()
 	//xweb框架配置
 	xweb.Config.RecoverPanic = true
 	xweb.Config.Profiler = true
@@ -34,15 +35,34 @@ func init() {
 	xweb.AddAction(&service.RepairRule{})
 	xweb.AddAction(&service.OprdData{})
 	xweb.AddAction(&service.JyData{})
+	xweb.AddAction(&service.App{}) // 程序
 	xweb.AddAction(&lua.Lua{})
 	xweb.RootApp().AppConfig.SessionTimeout = 1 * time.Hour
-	xweb.RootApp().Logger.SetOutputLevel(1)
+	xweb.RootApp().Logger.SetOutputLevel(3)
 
 	//xweb.AddTmplVar("add", func(a, b int) int { return a + b })
 	//InitTask("5ecf56ed92b4ee16ffd7e21e")
 }
 
 func main() {
+	go TimeTask()
+	go util.MailWork()
 	log.Println("port:", util.Sysconfig["port"])
 	xweb.Run(":" + qu.ObjToString(util.Sysconfig["port"]))
 }
+
+func TimeTask() {
+
+	util.Task()
+
+	crn := cron.New()
+	ct := 0
+	_ = crn.AddFunc("0 */10 * * * ?", func() {
+		ct += 1
+		if ct%100 == 0 {
+			qu.Debug(fmt.Sprintf("task count: %d", ct))
+		}
+		util.Task()
+	})
+	crn.Start()
+}

+ 112 - 0
src/service/app.go

@@ -0,0 +1,112 @@
+package service
+
+import (
+	"github.com/go-xweb/xweb"
+	"go.mongodb.org/mongo-driver/bson"
+	qu "qfw/util"
+	"strings"
+	"time"
+	"util"
+)
+
+type App struct {
+	*xweb.Action
+	appList   xweb.Mapper `xweb:"/service/app/monitor/list"`
+	appCreate xweb.Mapper `xweb:"/service/app/monitor/create"`
+	appEdit   xweb.Mapper `xweb:"/service/app/monitor/edit"`
+	appDel    xweb.Mapper `xweb:"/service/app/monitor/del"`
+}
+
+func (app *App) AppList() {
+	if app.Method() == "POST" {
+		identity := app.GetString("identity")
+		start, _ := app.GetInteger("start")
+		limit, _ := app.GetInteger("length")
+		draw, _ := app.GetInteger("draw")
+		q := bson.M{"name": bson.M{"$regex": identity}}
+		q["del"] = false
+		count := util.Mgo.Count("app_list", q)
+		info, _ := util.Mgo.Find("app_list", q, nil, nil, false, start, limit)
+		if len(*info) > 0 {
+			app.ServeJson(map[string]interface{}{
+				"draw":            draw,
+				"rep":             true,
+				"data":            *info,
+				"recordsFiltered": count,
+				"recordsTotal":    count,
+			})
+		} else {
+			app.ServeJson(map[string]interface{}{
+				"rep": false,
+				"msg": "未查询到数据",
+			})
+		}
+	} else {
+		_ = app.Render("app/app_list.html")
+	}
+}
+
+func (app *App) AppCreate() {
+	if app.Method() == "POST" {
+		data := util.GetPostForm(app.Request)
+		pg := *qu.ObjToMap(data["data"])
+		if name := qu.ObjToString(pg["name"]); name != "" {
+			now := time.Now().Unix()
+			ip_port := strings.Split(qu.ObjToString(pg["ip_port"]), ":")
+			leader := qu.ObjToString(pg["leader"])
+			mail := qu.ObjToString(pg["mail"])
+			timing := qu.ObjToString(pg["timing"])
+
+			save := bson.M{"name": name, "ip": ip_port[0], "port": qu.IntAll(ip_port[1]), "leader": leader, "mail": mail, "timing": timing}
+			save["del"] = false
+			save["updatetime"] = now
+			save["status"] = 1
+			util.Mgo.Save("app_list", save)
+			app.ServeJson(map[string]interface{}{
+				"rep": true,
+			})
+		} else {
+			app.ServeJson(map[string]interface{}{
+				"rep": false,
+			})
+		}
+	}
+}
+
+func (app *App) AppEdit() {
+	if app.Method() == "POST" {
+		data := util.GetPostForm(app.Request)
+		pg := *qu.ObjToMap(data["data"])
+		id := qu.ObjToString(data["id"])
+		if name := qu.ObjToString(pg["name"]); name != "" {
+			now := time.Now().Unix()
+			ip_port := strings.Split(qu.ObjToString(pg["ip_port"]), ":")
+			leader := qu.ObjToString(pg["leader"])
+			mail := qu.ObjToString(pg["mail"])
+			timing := qu.ObjToString(pg["timing"])
+			status := qu.IntAll(pg["status"])
+
+			save := bson.M{"name": name, "ip": ip_port[0], "port": qu.IntAll(ip_port[1]), "leader": leader, "mail": mail, "timing": timing}
+			save["updatetime"] = now
+			save["status"] = status
+			util.Mgo.UpdateById("app_list", id, bson.M{"$set": save})
+			app.ServeJson(map[string]interface{}{
+				"rep": true,
+			})
+		} else {
+			app.ServeJson(map[string]interface{}{
+				"rep": false,
+			})
+		}
+	}
+}
+
+func (app *App) AppDel() {
+	if app.Method() == "POST" {
+		id := app.GetString("id")
+		b := util.Mgo.UpdateById("app_list", id, bson.M{"$set": bson.M{"del": true}})
+		app.ServeJson(map[string]interface{}{
+			"rep": b,
+		})
+	}
+}

+ 1 - 2
src/service/repair_pro_service.go

@@ -6,7 +6,6 @@ import (
 	"net"
 	qu "qfw/util"
 	"time"
-	"udptask"
 	. "util"
 )
 
@@ -86,7 +85,7 @@ func (jy *RepairRule) RepairProSave() {
 			Port: qu.IntAll(nextNode["port"]),
 		}
 		qu.Debug(string(by))
-		err := udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+		err := Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 		if err == nil {
 			jy.ServeJson(map[string]interface{}{
 				"rep": true,

+ 22 - 17
src/service/repair_service.go

@@ -24,7 +24,6 @@ import (
 	"strconv"
 	"strings"
 	"time"
-	"udptask"
 	. "util"
 )
 
@@ -90,7 +89,7 @@ func (jy *RepairRule) EsDel() {
 	_ = jy.Render("repair/jy_es_del.html")
 }
 
-//新增数据
+// 新增数据
 func (jy *RepairRule) RepairCreate() {
 	defer qu.Catch()
 	jy.T["data"] = map[string]string{
@@ -106,7 +105,7 @@ func (jy *RepairRule) RepairCreate() {
 	jy.Render("repair/jy_create.html", &jy.T)
 }
 
-//新增数据
+// 新增数据
 func (jy *RepairRule) RepairNewSave() {
 	defer qu.Catch()
 	//mongo新增
@@ -144,6 +143,8 @@ func (jy *RepairRule) RepairNewSave() {
 		pt := int64(0)
 		(*updata)["publishtime"] = pt
 	}
+
+	(*updata)["detail_isvalidity"] = 1 // 正文有效
 	// infoformat
 	if val, ok := (*updata)["infoformat"]; ok {
 		(*updata)["infoformat"] = qu.IntAll(val)
@@ -268,6 +269,7 @@ func (jy *RepairRule) RepairPub() {
 	save["comeintime"] = time.Now().Unix()
 	save["infoformat"] = 1
 	save["extracttype"] = 0
+	save["detail_isvalidity"] = 1 // 正文有效
 
 	b := JYMgo.SaveByOriID(JyCollNameOne, save)
 	if b {
@@ -313,7 +315,7 @@ func (jy *RepairRule) RepairPub() {
 			Port: qu.IntAll(indexNode["port"]),
 		}
 		qu.Debug("udp---1---------", string(by))
-		udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+		Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 		jy.ServeJson(map[string]interface{}{
 			"rep": true,
 		})
@@ -325,7 +327,7 @@ func (jy *RepairRule) RepairPub() {
 	}
 }
 
-//编辑
+// 编辑
 func (jy *RepairRule) RepairEdit() {
 	defer qu.Catch()
 	id := jy.GetString("id")
@@ -350,7 +352,7 @@ func (jy *RepairRule) RepairEdit() {
 	jy.Render("repair/jy_edit.html", &jy.T)
 }
 
-//删除
+// 删除
 func (jy *RepairRule) RepairDelete() {
 	defer qu.Catch()
 	id := jy.GetString("_id")
@@ -429,7 +431,7 @@ func (jy *RepairRule) RepairDelete() {
 			Port: qu.IntAll(nextNode["port"]),
 		}
 		qu.Debug(string(by))
-		udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+		Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 
 		jy.ServeJson(map[string]interface{}{
 			"rep": b,
@@ -437,7 +439,7 @@ func (jy *RepairRule) RepairDelete() {
 	}
 }
 
-//修改-mongo -es
+// 修改-mongo -es
 func (jy *RepairRule) RepairSave() {
 	defer qu.Catch()
 	if jy.Method() == "POST" {
@@ -561,7 +563,7 @@ func (jy *RepairRule) RepairSave() {
 				Port: qu.IntAll(indexNode["port"]),
 			}
 			qu.Debug("udp---1---------", string(by))
-			udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+			Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 			if IsModifyPro(modifyinfo) {
 				// udp	项目
 				nextNode := *qu.ObjToMap(Sysconfig["jy_pro_node"])
@@ -575,7 +577,7 @@ func (jy *RepairRule) RepairSave() {
 					Port: qu.IntAll(nextNode["port"]),
 				}
 				qu.Debug("udp---2---------", string(by1))
-				udptask.Udpclient.WriteUdp(by1, mu.OP_TYPE_DATA, addr1)
+				Udpclient.WriteUdp(by1, mu.OP_TYPE_DATA, addr1)
 			}
 
 			//删除redis 指定key
@@ -636,7 +638,7 @@ func (jy *RepairRule) RepairSave() {
 	}
 }
 
-//查询
+// 查询
 func (jy *RepairRule) SearchID() {
 	defer qu.Catch()
 	if jy.Method() == "POST" {
@@ -803,7 +805,8 @@ func (jy *RepairRule) RepairRecord() {
 	}
 }
 
-/**
+/*
+*
 批量修改
 */
 func (jy *RepairRule) RepairImport() {
@@ -866,7 +869,8 @@ func ParsJyData(filebyte []byte) ([]map[string]interface{}, error) {
 	return jyData, nil
 }
 
-/**
+/*
+*
 批量删除
 */
 func (jy *RepairRule) RepairImport1() {
@@ -892,7 +896,8 @@ func (jy *RepairRule) RepairImport1() {
 	}
 }
 
-/**
+/*
+*
 批量修改保存
 */
 func (jy *RepairRule) RepairModify() {
@@ -1001,7 +1006,7 @@ func ModifyData(tmp map[string]interface{}, user map[string]interface{}) (err ma
 		Port: qu.IntAll(indexNode["port"]),
 	}
 	qu.Debug("udp------------", string(by))
-	udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+	Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 	if IsModifyPro(modifyinfo) {
 		// udp	项目
 		nextNode := *qu.ObjToMap(Sysconfig["jy_pro_node"])
@@ -1014,7 +1019,7 @@ func ModifyData(tmp map[string]interface{}, user map[string]interface{}) (err ma
 			Port: qu.IntAll(nextNode["port"]),
 		}
 		qu.Debug("udp---2---------", string(by1))
-		udptask.Udpclient.WriteUdp(by1, mu.OP_TYPE_DATA, addr1)
+		Udpclient.WriteUdp(by1, mu.OP_TYPE_DATA, addr1)
 	}
 
 	//删除redis 指定key
@@ -1129,7 +1134,7 @@ func ModifyData1(tmp map[string]interface{}, user map[string]interface{}) (err m
 		Port: qu.IntAll(nextNode["port"]),
 	}
 	qu.Debug(string(by))
-	udptask.Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
+	Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 
 	//删除redis 指定key
 	delName1 := RedisDelKey1 + "*_" + "*_" + id

+ 107 - 0
src/util/timetask.go

@@ -0,0 +1,107 @@
+package util
+
+import (
+	"encoding/json"
+	"fmt"
+	"github.com/go-xweb/log"
+	"go.mongodb.org/mongo-driver/bson"
+	"io"
+	mu "mfw/util"
+	"net"
+	"net/http"
+	qu "qfw/util"
+	"strings"
+	"sync"
+	"time"
+)
+
+var (
+	UdpTaskMap = &sync.Map{}
+)
+
+func Task() {
+	q := bson.M{"status": bson.M{"$gt": 0}, "del": false}
+	info, _ := Mgo.Find("app_list", q, nil, nil, false, -1, -1)
+	for _, m := range *info {
+		name := qu.ObjToString(m["name"])
+		ip := qu.ObjToString(m["ip"])
+		port := qu.IntAll(m["port"])
+		mail := qu.ObjToString(m["mail"])
+		timing := qu.ObjToString(m["timing"])
+
+		now := time.Now().Unix()
+		t := TimeDis(timing)
+		if t > 0 && (now-qu.Int64All(m["checktime"]) >= t) {
+			udpNode(ip, name, mail, port, now)
+			Mgo.UpdateById("app_list", m["_id"], bson.M{"$set": bson.M{"checktime": now}})
+		}
+	}
+}
+
+func TimeDis(str string) int64 {
+	if strings.Contains(str, "m") {
+		m := qu.IntAll(strings.Replace(str, "m", "", -1))
+		return int64(m * 60)
+	} else if strings.Contains(str, "h") {
+		h := qu.IntAll(strings.Replace(str, "h", "", -1))
+		return int64(h * 60 * 60)
+	}
+	return 0
+}
+
+type UdpNode struct {
+	data      []byte
+	addr      *net.UDPAddr
+	timestamp int64
+	retry     int
+	to        string
+	name      string
+}
+
+func udpNode(addr, name, to string, port int, now int64) {
+	var next = &net.UDPAddr{
+		IP:   net.ParseIP(addr),
+		Port: port,
+	}
+	mapInfo := make(map[string]interface{})
+	mapInfo["stype"] = "monitor"
+	key := fmt.Sprintf("%d-%s-%s", now, name, "monitor")
+	mapInfo["key"] = key
+	datas, _ := json.Marshal(mapInfo)
+	node := &UdpNode{datas, next, now, 0, to, name}
+	UdpTaskMap.Store(key, node)
+	_ = Udpclient.WriteUdp(datas, mu.OP_TYPE_DATA, next)
+}
+
+func MailWork() {
+	qu.Debug("mailWork...")
+	jkmail, _ := Sysconfig["jkmail"].(map[string]interface{})
+	for {
+		UdpTaskMap.Range(func(k, v interface{}) bool {
+			now := time.Now().Unix()
+			node, _ := v.(*UdpNode)
+			if now-node.timestamp > 120 {
+				node.retry++
+				if node.retry > 1 {
+					UdpTaskMap.Delete(k)
+					t := fmt.Sprintf("数据处理程序异常监控通知--%s", node.name)
+					b := fmt.Sprintf("应用:%s,ip-port:%s,监控通信未成功,检测异常时间%s,请负责人检查程序并处理。",
+						node.name, node.addr.String(), qu.FormatDateByInt64(&node.timestamp, qu.Date_Time_Layout))
+					res, err := http.Get(fmt.Sprintf("%s?to=%s&title=%s&body=%s", qu.ObjToString(jkmail["api"]), node.to, t, b))
+					if err == nil {
+						defer res.Body.Close()
+						read, _ := io.ReadAll(res.Body)
+						qu.Debug("send mail ...", node.name, node.to, string(read))
+					}
+				} else {
+					qu.Debug("udp重发", k)
+					_ = Udpclient.WriteUdp(node.data, mu.OP_TYPE_DATA, node.addr)
+				}
+			} else if now-node.timestamp > 10 {
+				log.Info("udp任务超时中..", k)
+			}
+			return true
+		})
+		time.Sleep(60 * time.Second)
+	}
+}

+ 12 - 18
src/udptask/udptask.go → src/util/udptask.go

@@ -1,4 +1,4 @@
-package udptask
+package util
 
 import (
 	"encoding/json"
@@ -8,7 +8,6 @@ import (
 	"mongodb"
 	"net"
 	qu "qfw/util"
-	"util"
 )
 
 var (
@@ -18,7 +17,7 @@ var (
 )
 
 func InitUdp() {
-	updport := util.Sysconfig["udpport"].(string)
+	updport := Sysconfig["udpport"].(string)
 	Udpclient = mu.UdpClient{Local: updport, BufSize: 1024}
 	log.Println("Udp服务监听", updport)
 	Udpclient.Listen(processUdpMsg)
@@ -26,10 +25,9 @@ func InitUdp() {
 
 func processUdpMsg(act byte, data []byte, ra *net.UDPAddr) {
 	switch act {
-	case mu.OP_TYPE_DATA: //测试接收
+	case mu.OP_TYPE_DATA:
 		var rep map[string]interface{}
 		err := json.Unmarshal(data, &rep)
-		log.Println("测试接收:", rep)
 		if err != nil {
 			//go Udpclient.WriteUdp([]byte{}, mu.OP_NOOP, ra) //回应上一个节点
 		} else {
@@ -39,23 +37,19 @@ func processUdpMsg(act byte, data []byte, ra *net.UDPAddr) {
 			//})
 			//go Udpclient.WriteUdp(by, mu.OP_NOOP, ra) //回应上一个节点
 		}
-	case mu.OP_NOOP: //下个节点回应
-		qu.Debug("接收回应:", string(data))
-		var rep map[string]interface{}
-		err := json.Unmarshal(data, &rep)
-		if err != nil { //空数据
-			//
-
-		} else { //正确
-
+	case mu.OP_NOOP:
+		//qu.Debug("接收回应:", string(data))
+		ok := string(data)
+		if ok != "" {
+			UdpTaskMap.Delete(ok)
 		}
 	}
 
 }
 
-//bidding索引udp
+// bidding索引udp
 func BiddingIndexUdp(id, coll string) {
-	indexNode := util.Sysconfig["indexNode"].(map[string]interface{})
+	indexNode := Sysconfig["indexNode"].(map[string]interface{})
 	addr := &net.UDPAddr{
 		IP:   net.ParseIP(indexNode["addr"].(string)),
 		Port: qu.IntAll(indexNode["port"]),
@@ -72,9 +66,9 @@ func BiddingIndexUdp(id, coll string) {
 	Udpclient.WriteUdp(by, mu.OP_TYPE_DATA, addr)
 }
 
-//项目合并udp
+// 项目合并udp
 func ProjectSetUdp(id, coll string) {
-	nextNode := util.Sysconfig["jy_pro_node"].(map[string]interface{})
+	nextNode := Sysconfig["jy_pro_node"].(map[string]interface{})
 	addr := &net.UDPAddr{
 		IP:   net.ParseIP(nextNode["addr"].(string)),
 		Port: qu.IntAll(nextNode["port"]),

+ 385 - 0
src/web/templates/app/app_list.html

@@ -0,0 +1,385 @@
+{{include "com/inc.html"}}
+<!-- Main Header -->
+{{include "com/header.html"}}
+<!-- Left side column. 权限菜单 -->
+{{include "com/menu.html"}}
+
+<div class="content-wrapper">
+    <section class="content-header">
+        <h1>
+            <small><a onclick="createModel()" class="btn btn-primary opr">新增程序</a></small>
+        </h1>
+        <ol class="breadcrumb">
+            <li><a href="#"><i class="fa fa-dashboard"></i> 首页</a></li>
+            <li><a href="#"><i class="fa fa-dashboard"></i> 程序列表</a></li>
+        </ol>
+        <br/>
+    </section>
+    <!-- Main content -->
+    <section class="content">
+        <div class="row">
+            <div class="col-xs-12">
+                <div class="box">
+                    <div class="box-body">
+                        <h3>程序列表</h3>
+                        <table id="dataTable" class="table table-bordered table-hover">
+                            <thead>
+                            <tr>
+                                <th>编号</th>
+                                <th>应用名称</th>
+                                <th>地址&端口</th>
+                                <th>定时时间</th>
+                                <th>负责人</th>
+                                <th>邮箱</th>
+                                <th>状态</th>
+                                <th>心跳时间</th>
+                                <th>操作</th>
+                            </tr>
+                            </thead>
+                        </table>
+                    </div>
+                    <!-- /.box-body -->
+                </div>
+                <!-- /.box -->
+            </div>
+        </div>
+    </section>
+</div>
+
+<!-- 新增程序model -->
+<div class="modal fade" id="modal-create" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <div class="edit-form">
+                        <div class="edit-info">
+                            <span class="info"><i class="fa fa-fw fa-tags fa-lg"></i>新建程序</span>
+                        </div>
+                        <div class="edit-form">
+                            <hr>
+                            <form class="form-horizontal">
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>程序名称</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="app-name" placeholder="程序名称" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>服务器&端口</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="ip-port" placeholder="服务器&端口" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>定时时间</label>
+                                    <div class="col-sm-8">
+                                        <select class="form-control selectpicker" id="timing">
+                                            <option value="0" selected>10m</option>
+                                            <option value="1">30m</option>
+                                            <option value="2">1h</option>
+                                            <option value="3">3h</option>
+                                            <option value="4">24h</option>
+                                        </select>
+                                    </div>
+                                </div>
+<!--                                <div class="form-group margin-bottom">-->
+<!--                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>程序状态</label>-->
+<!--                                    <div class="col-sm-5 radio">-->
+<!--                                        <label class="margin-r-5">-->
+<!--                                            <input type="radio" name="role" value=1 checked>正常-->
+<!--                                        </label>-->
+<!--                                        <label class="margin-r-5">-->
+<!--                                            <input type="radio" name="role" value=2 checked>废弃-->
+<!--                                        </label>-->
+<!--                                    </div>-->
+<!--                                </div>-->
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>负责人</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="leader" placeholder="负责人" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>邮箱地址</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="email" placeholder="邮箱地址,可以多个英文逗号拼接" required>
+                                    </div>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="button" onclick="saveData()" class="btn btn-primary saveBtn" value="保存">
+                    <input type="button" onclick="cancelModel()" class="btn btn-default" style="margin-left: 24px" value="取消">
+                </div>
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+
+<div class="modal fade" id="modal-edit" tabindex="-1" role="dialog" aria-hidden="true">
+    <div class="modal-dialog">
+        <div class="modal-content">
+            <div class="modal-header">
+                <div class="modal-header">
+                    <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                    <div class="edit-form">
+                        <div class="edit-info">
+                            <span class="info"><i class="fa fa-fw fa-tags fa-lg"></i>修改程序</span>
+                        </div>
+                        <div class="edit-form">
+                            <hr>
+                            <form class="form-horizontal">
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>程序名称</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-name" placeholder="程序名称" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>服务器&端口</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-ip-port" placeholder="服务器&端口" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>定时时间</label>
+                                    <div class="col-sm-8">
+                                        <select class="form-control selectpicker" id="edit-timing">
+                                            <option value="0" selected>10m</option>
+                                            <option value="1">30m</option>
+                                            <option value="2">1h</option>
+                                            <option value="3">3h</option>
+                                            <option value="4">24h</option>
+                                        </select>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>程序状态</label>
+                                    <div class="col-sm-5 radio">
+                                        <label class="margin-r-5">
+                                            <input type="radio" name="status" value=1>正常
+                                        </label>
+                                        <label class="margin-r-5">
+                                            <input type="radio" name="status" value=0>暂停
+                                        </label>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>负责人</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-leader" placeholder="负责人" required>
+                                    </div>
+                                </div>
+                                <div class="form-group margin-bottom">
+                                    <label class="col-sm-3 control-label"><span style="color:red;">* </span>邮箱地址</label>
+                                    <div class="col-sm-8">
+                                        <input type="text" class="form-control" id="edit-email" placeholder="邮箱地址,可以多个英文逗号拼接" required>
+                                    </div>
+                                </div>
+                            </form>
+                        </div>
+                    </div>
+                </div>
+                <div class="modal-footer">
+                    <input type="button" onclick="saveEdit()" class="btn btn-primary saveBtn" value="保存">
+                    <input type="button" onclick="cancelModel()" class="btn btn-default" style="margin-left: 24px" value="取消">
+                </div>
+            </div>
+        </div>
+    </div><!-- /.modal -->
+</div>
+
+
+{{include "com/footer.html"}}
+<script>
+    menuActive("/service/app/monitor/list");
+
+    var _id = ""
+
+    $(document).ready(function () {
+        ttable = $('#dataTable').DataTable({
+            "paging": true,
+            "lengthChange": false,
+            "searching": true,
+            "ordering": false,
+            "info": true,
+            "autoWidth": true,
+            "serverSide": true,
+            "language": {
+                "url": "/dist/js/dataTables.chinese.lang"
+            },
+            "ajax": {
+                "url": "/service/app/monitor/list",
+                "type": "post",
+                "data": {}
+            },
+            "fnDrawCallback": function () {
+                $("ul.pagination").prepend("&nbsp;&nbsp;&nbsp;转到第 <input type='text' id='changePage'   style='width:20px;'> 页    <a type='text' href='javascript:void(0);' id='dataTable-btn' style='text-align:center'>GO</a>");
+                $('#dataTable-btn').click(function (e) {
+                    var redirectpage = 0
+                    if ($("#changePage").val() && $("#changePage").val() > 0) {
+                        var redirectpage = $("#changePage").val() - 1;
+                    }
+                    ttable.page(redirectpage).draw(false);
+                });
+                this.api().column(0).nodes().each(function(cell, i) {
+                    cell.innerHTML = i + 1;
+                });
+            },
+            "columns": [
+                {"data": null, width:"4%"},
+                {"data": "name", width: "10%"},
+                {"data": function (row) {
+                        var str = ""
+                        str = row["ip"] + ":" + row["port"]
+                        return str
+                    }, width: "10%",},
+                {"data": "timing", width: "10%"},
+                {"data": "leader", width: "10%"},
+                {"data": "mail", width: "20%"},
+                {"data": "status", width: "8%", render: function (val) {
+                        if (val > 0) {
+                            return "运行中"
+                        } else if (val < 0) {
+                            return "异常"
+                        } else if (val === 0) {
+                            return "已暂停"
+                        }
+                    }},
+                {"data": "checktime", width:"10%", render: function (val) {
+                        var dt = new Date()
+                        dt.setTime(parseInt(val) * 1000);
+                        return dt.format("yyyy-MM-dd hh:mm:ss")
+                    }},
+                {"data":"_id",render:function(val,a,row){
+                        return  "<a href='#' onclick='edit(\""+row['name']+"\",\""+row['ip']+"\",\""+row['port']+"\",\""+row['leader']+"\",\""+row['mail']+
+                            "\",\""+row['timing']+"\",\""+row['status']+"\",\""+row['_id']+"\")'>"+"<i class='fa fa-fw fa-edit text-yellow'></i></a> &nbsp;"+
+                            "<a href='#' onclick='del(\""+val+"\")'><i class='fa fa-fw fa-trash text-red'></i></a>"
+                    }}
+            ]
+        });
+    });
+
+    function saveData() {
+        const name = $("#app-name").val();
+        const ip_port = $("#ip-port").val()
+        const leader = $("#leader").val()
+        const mail = $("#email").val()
+        const timing = $("#timing option:selected").text()
+
+        const app = {}
+        if (name === "" || ip_port === "" || leader === "" || mail === "") {
+            alert("必填信息不能为空")
+            return
+        }
+        app['name'] = name
+        app['ip_port'] = ip_port
+        app['leader'] = leader
+        app['mail'] = mail
+        app['timing'] = timing
+        $.ajax({
+            url: "/service/app/monitor/create",
+            type: 'POST',
+            data: {"data": JSON.stringify(app)},
+            success: function (r) {
+                if (r.rep) {
+                    showTip("保存成功", 1000);
+                    ttable.ajax.reload();
+                } else {
+                    showTip("保存失败", 1000);
+                }
+                $('#modal-create').modal("hide")
+            }
+        })
+    }
+
+    function createModel() {
+        $('#modal-create').modal("show")
+    }
+
+    function cancelModel() {
+        $('#modal-create').modal("hide")
+        $('#modal-edit').modal("hide")
+    }
+
+    function edit(name, ip, port, leader, mail, timing, status, id) {
+        _id = id;
+        $("#modal-edit #edit-name").val(name);
+        $("#modal-edit #edit-ip-port").val(ip+":"+port);
+        $("#modal-edit #edit-leader").val(leader);
+        $("#modal-edit #edit-email").val(mail);
+        $("#modal-edit").modal("show");
+
+        const s = document.getElementById("edit-timing");
+        for(let i=0; i<s.options.length; i++){
+            if(s.options[i].innerHTML === timing){
+                s.options[i].selected = true;
+                break;
+            }
+        }
+        $("#edit-timing").selectpicker("refresh");
+        const r = document.getElementsByName("status");
+        for(let i=0;i < r.length;i++) {
+            if (r[i].value === status) {
+                r[i].checked = true;
+            }
+        }
+    }
+
+    function saveEdit() {
+        const name = $("#edit-name").val();
+        const ip_port = $("#edit-ip-port").val()
+        const leader = $("#edit-leader").val()
+        const mail = $("#edit-email").val()
+
+        const app = {}
+
+        if (name === "" || ip_port === "" || leader === "" || mail === "") {
+            alert("必填信息不能为空")
+            return
+        }
+        app['name'] = name
+        app['ip_port'] = ip_port
+        app['leader'] = leader
+        app['mail'] = mail
+        app['timing'] = timing
+        app["timing"] = $("#edit-timing option:selected").text()
+        app["status"]= $("input[name='status']:checked").val()
+        $.ajax({
+            url: "/service/app/monitor/edit",
+            type: 'POST',
+            data: {"data": JSON.stringify(app), "id": _id},
+            success: function (r) {
+                if (r.rep) {
+                    showTip("保存成功", 1000);
+                    ttable.ajax.reload();
+                } else {
+                    showTip("保存失败", 1000);
+                }
+                $('#modal-edit').modal("hide")
+            }
+        })
+    }
+
+    function del(id) {
+        $.ajax({
+            url: "/service/app/monitor/del",
+            type: 'POST',
+            data: {"id": id},
+            success: function (r) {
+                if (r.rep) {
+                    showTip("删除成功", 1000);
+                    ttable.ajax.reload();
+                } else {
+                    showTip("删除失败", 1000);
+                }
+                $('#modal-edit').modal("hide")
+            }
+        })
+    }
+
+</script>