diff --git a/.gitignore b/.gitignore index 860f657..8fa530b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,5 @@ cbuild */.DS_Store main .vscode -go.sum *.exe tmp \ No newline at end of file diff --git a/app/controller/admin/cms_menu.go b/app/controller/admin/cms_menu.go index b609bcf..89eb213 100644 --- a/app/controller/admin/cms_menu.go +++ b/app/controller/admin/cms_menu.go @@ -69,7 +69,7 @@ func (c *CmsMenu) Add(r *ghttp.Request) { } list := gconv.Maps(menus) list = utils.PushSonToParent(list, 0, "parent_id") - typeChecker, err := dict_service.GetDictWithDataByType("cms_category_type", "") + typeChecker, err := dict_service.GetDictWithDataByType("cms_category_type", "", "") if err != nil { response.FailJson(true, r, err.Error()) } @@ -111,7 +111,7 @@ func (c *CmsMenu) Edit(r *ghttp.Request) { } list := gconv.Maps(menus) list = utils.PushSonToParent(list, 0, "parent_id") - typeChecker, err := dict_service.GetDictWithDataByType("cms_category_type", gconv.String(menuInfo.CateType)) + typeChecker, err := dict_service.GetDictWithDataByType("cms_category_type", gconv.String(menuInfo.CateType), "") if err != nil { response.FailJson(true, r, err.Error()) } diff --git a/app/controller/admin/monitor_job.go b/app/controller/admin/monitor_job.go index 4fdfc7b..0d7d85a 100644 --- a/app/controller/admin/monitor_job.go +++ b/app/controller/admin/monitor_job.go @@ -1,25 +1,70 @@ package admin import ( + "gfast/app/model/admin/sys_job" "gfast/app/service/admin/dict_service" + "gfast/app/service/admin/monitor_service" + "gfast/app/service/admin/user_service" "gfast/library/response" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/util/gconv" + "github.com/gogf/gf/util/gvalid" ) type MonitorJob struct{} //任务列表 -func (c *MonitorJob) List(r *ghttp.Request) {} - -//添加任务 -func (c *MonitorJob) Add(r *ghttp.Request) { - //获取相关选项 - jobStatus, err := dict_service.GetDictWithDataByType("sys_job_status", "") +func (c *MonitorJob) List(r *ghttp.Request) { + var req *sys_job.SelectPageReq + //获取参数 + if err := r.Parse(&req); err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + total, page, list, err := monitor_service.JobListByPage(req) if err != nil { response.FailJson(true, r, err.Error()) } - jobGroup, err := dict_service.GetDictWithDataByType("sys_job_group", "") + //获取相关选项 + jobStatus, err := dict_service.GetDictWithDataByType("sys_job_status", "", "全部") + if err != nil { + response.FailJson(true, r, err.Error()) + } + jobGroup, err := dict_service.GetDictWithDataByType("sys_job_group", "", "全部") + if err != nil { + response.FailJson(true, r, err.Error()) + } + result := g.Map{ + "currentPage": page, + "total": total, + "list": list, + "searchStatus": jobStatus, + "searchGroup": jobGroup, + } + response.SusJson(true, r, "任务列表", result) +} + +//添加任务 +func (c *MonitorJob) Add(r *ghttp.Request) { + if r.Method == "POST" { + var req *sys_job.ReqAdd + //获取参数 + if err := r.Parse(&req); err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + userId := user_service.GetLoginID(r) //获取登陆用户id + _, err := monitor_service.AddJob(req, userId) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "任务添加成功") + } + //获取相关选项 + jobStatus, err := dict_service.GetDictWithDataByType("sys_job_status", "", "") + if err != nil { + response.FailJson(true, r, err.Error()) + } + jobGroup, err := dict_service.GetDictWithDataByType("sys_job_group", "", "") if err != nil { response.FailJson(true, r, err.Error()) } @@ -29,3 +74,78 @@ func (c *MonitorJob) Add(r *ghttp.Request) { } response.SusJson(true, r, "添加任务", res) } + +//修改任务 +func (c *MonitorJob) Edit(r *ghttp.Request) { + if r.Method == "POST" { + var req *sys_job.ReqEdit + //获取参数 + if err := r.Parse(&req); err != nil { + response.FailJson(true, r, err.(*gvalid.Error).FirstString()) + } + userId := user_service.GetLoginID(r) //获取登陆用户id + _, err := monitor_service.EditJob(req, userId) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "修改任务成功") + } + id := r.GetInt64("id") + job, err := monitor_service.GetJobInfoById(id) + if err != nil { + response.FailJson(true, r, err.Error()) + } + //获取相关选项 + jobStatus, err := dict_service.GetDictWithDataByType("sys_job_status", gconv.String(job.Status), "") + if err != nil { + response.FailJson(true, r, err.Error()) + } + jobGroup, err := dict_service.GetDictWithDataByType("sys_job_group", job.JobGroup, "") + if err != nil { + response.FailJson(true, r, err.Error()) + } + res := g.Map{ + "jobStatus": jobStatus, + "jobGroup": jobGroup, + "jobInfo": job, + } + response.SusJson(true, r, "添加任务", res) +} + +//删除计划任务 +func (c *MonitorJob) Delete(r *ghttp.Request) { + ids := r.GetInts("ids") + err := monitor_service.DeleteJobByIds(ids) + if err != nil { + response.FailJson(true, r, err.Error()) + } + response.SusJson(true, r, "删除成功") +} + +//启动任务 +func (c *MonitorJob) Start(r *ghttp.Request) { + id := r.GetInt64("id") + job, err := monitor_service.GetJobInfoById(id) + if err != nil { + response.FailJson(true, r, err.Error()) + } + err = monitor_service.JobStart(job) + if err != nil { + response.FailJson(true, r, "定时任务管理启动"+err.Error()) + } + response.SusJson(true, r, "定时任务管理启动成功") +} + +//停止任务 +func (c *MonitorJob) Stop(r *ghttp.Request) { + id := r.GetInt64("id") + job, err := monitor_service.GetJobInfoById(id) + if err != nil { + response.FailJson(true, r, err.Error()) + } + err = monitor_service.JobStop(job) + if err != nil { + response.FailJson(true, r, "定时任务管理停止"+err.Error()) + } + response.SusJson(true, r, "定时任务管理停止成功") +} diff --git a/app/controller/admin/monitor_login_info.go b/app/controller/admin/monitor_login_info.go new file mode 100644 index 0000000..4101416 --- /dev/null +++ b/app/controller/admin/monitor_login_info.go @@ -0,0 +1,10 @@ +package admin + +import "github.com/gogf/gf/net/ghttp" + +type MonitorLoginLog struct{} + +//登录日志列表 +func (c *MonitorLoginLog) List(r *ghttp.Request) { + +} diff --git a/app/controller/admin/monitor_server.go b/app/controller/admin/monitor_server.go new file mode 100644 index 0000000..ece365b --- /dev/null +++ b/app/controller/admin/monitor_server.go @@ -0,0 +1,141 @@ +package admin + +import ( + "fmt" + "gfast/library/response" + "gfast/library/utils" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" + "github.com/gogf/gf/os/gtime" + "github.com/shirou/gopsutil/cpu" + "github.com/shirou/gopsutil/disk" + "github.com/shirou/gopsutil/host" + "github.com/shirou/gopsutil/load" + "github.com/shirou/gopsutil/mem" + "os" + "runtime" + "strconv" + "time" +) + +type MonitorServer struct{} + +var StartTime = gtime.Datetime() + +func (c *MonitorServer) Info(r *ghttp.Request) { + cpuNum := runtime.NumCPU() //核心数 + + var cpuUsed float64 = 0 //用户使用率 + var cpuAvg5 float64 = 0 //CPU负载5 + var cpuAvg15 float64 = 0 //当前空闲率 + + cpuInfo, err := cpu.Percent(time.Duration(time.Second), false) + if err == nil { + cpuUsed, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", cpuInfo[0]), 64) + } + + loadInfo, err := load.Avg() + if err == nil { + cpuAvg5, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", loadInfo.Load5), 64) + cpuAvg15, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", loadInfo.Load5), 64) + } + + var memTotal uint64 = 0 //总内存 + var memUsed uint64 = 0 //总内存 := 0 //已用内存 + var memFree uint64 = 0 //剩余内存 + var memUsage float64 = 0 //使用率 + + v, err := mem.VirtualMemory() + if err == nil { + memTotal = v.Total / 1024 / 1024 + memFree = v.Free / 1024 / 1024 + memUsed = v.Used / 1024 / 1024 + memUsage, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", v.UsedPercent), 64) + } + + var goTotal uint64 = 0 //go分配的总内存数 + var goUsed uint64 = 0 //go使用的内存数 + var goFree uint64 = 0 //go剩余的内存数 + var goUsage float64 = 0 //使用率 + + var gomem runtime.MemStats + runtime.ReadMemStats(&gomem) + goUsed = gomem.Sys / 1024 / 1024 + + sysComputerIp := "" //服务器IP + + ip, err := utils.GetLocalIP() + if err == nil { + sysComputerIp = ip + } + + sysComputerName := "" //服务器名称 + sysOsName := "" //操作系统 + sysOsArch := "" //系统架构 + + sysInfo, err := host.Info() + + if err == nil { + sysComputerName = sysInfo.Hostname + sysOsName = sysInfo.OS + sysOsArch = sysInfo.KernelArch + } + + goName := "GoLang" //语言环境 + goVersion := runtime.Version() //版本 + gtime.Date() + goStartTime := StartTime //启动时间 + + goRunTime := utils.GetHourDiffer(StartTime, gtime.Datetime()) //运行时长 + goHome := runtime.GOROOT() //安装路径 + goUserDir := "" //项目路径 + + curDir, err := os.Getwd() + + if err == nil { + goUserDir = curDir + } + + //服务器磁盘信息 + disklist := make([]disk.UsageStat, 0) + diskInfo, err := disk.Partitions(true) //所有分区 + if err == nil { + for _, p := range diskInfo { + diskDetail, err := disk.Usage(p.Mountpoint) + if err == nil { + diskDetail.UsedPercent, _ = strconv.ParseFloat(fmt.Sprintf("%.2f", diskDetail.UsedPercent), 64) + diskDetail.Total = diskDetail.Total / 1024 / 1024 + diskDetail.Used = diskDetail.Used / 1024 / 1024 + diskDetail.Free = diskDetail.Free / 1024 / 1024 + disklist = append(disklist, *diskDetail) + } + } + } + + res := g.Map{ + "cpuNum": cpuNum, + "cpuUsed": cpuUsed, + "cpuAvg5": cpuAvg5, + "cpuAvg15": cpuAvg15, + "memTotal": memTotal, + "goTotal": goTotal, + "memUsed": memUsed, + "goUsed": goUsed, + "memFree": memFree, + "goFree": goFree, + "memUsage": memUsage, + "goUsage": goUsage, + "sysComputerName": sysComputerName, + "sysOsName": sysOsName, + "sysComputerIp": sysComputerIp, + "sysOsArch": sysOsArch, + "goName": goName, + "goVersion": goVersion, + "goStartTime": goStartTime, + "goRunTime": goRunTime, + "goHome": goHome, + "goUserDir": goUserDir, + "disklist": disklist, + } + response.SusJson(true, r, "服务监控", res) +} diff --git a/app/model/admin/sys_job/sys_job.go b/app/model/admin/sys_job/sys_job.go index 5258f82..eaaf368 100644 --- a/app/model/admin/sys_job/sys_job.go +++ b/app/model/admin/sys_job/sys_job.go @@ -1,3 +1,31 @@ package sys_job // Fill with you ideas below. + +//添加操作请求参数 +type ReqAdd struct { + JobName string `p:"jobName" v:"required#任务名称不能为空"` + JobParams string `p:"jobParams"` // 任务参数 + JobGroup string `p:"jobGroup" ` + InvokeTarget string `p:"invokeTarget" v:"required#执行方法不能为空"` + CronExpression string `p:"cronExpression" v:"required#任务表达式不能为空"` + MisfirePolicy int `p:"misfirePolicy"` + Concurrent int `p:"concurrent" ` + Status int `p:"status" v:"required#状态(0正常 1暂停)不能为空"` + Remark string `p:"remark" ` +} + +//修改操作请求参数 +type ReqEdit struct { + JobId int64 `p:"jobId" v:"min:1#任务id不能为空"` + ReqAdd +} + +//分页请求参数 +type SelectPageReq struct { + JobName string `p:"jobName"` //任务名称 + JobGroup string `p:"jobGroup"` //任务组名 + Status string `p:"status"` //状态(0正常 1暂停) + PageNum int `p:"page"` //当前页码 + PageSize int `p:"pageSize"` //每页数 +} diff --git a/app/model/admin/sys_login_log/sys_login_log.go b/app/model/admin/sys_login_log/sys_login_log.go new file mode 100644 index 0000000..e31eb20 --- /dev/null +++ b/app/model/admin/sys_login_log/sys_login_log.go @@ -0,0 +1,3 @@ +package sys_login_log + +// Fill with you ideas below. diff --git a/app/model/admin/sys_login_log/sys_login_log_entity.go b/app/model/admin/sys_login_log/sys_login_log_entity.go new file mode 100644 index 0000000..9c14761 --- /dev/null +++ b/app/model/admin/sys_login_log/sys_login_log_entity.go @@ -0,0 +1,60 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package sys_login_log + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" +) + +// Entity is the golang structure for table sys_login_log. +type Entity struct { + InfoId int64 `orm:"info_id,primary" json:"info_id"` // 访问ID + LoginName string `orm:"login_name" json:"login_name"` // 登录账号 + Ipaddr string `orm:"ipaddr" json:"ipaddr"` // 登录IP地址 + LoginLocation string `orm:"login_location" json:"login_location"` // 登录地点 + Browser string `orm:"browser" json:"browser"` // 浏览器类型 + Os string `orm:"os" json:"os"` // 操作系统 + Status int `orm:"status" json:"status"` // 登录状态(0成功 1失败) + Msg string `orm:"msg" json:"msg"` // 提示消息 + LoginTime int64 `orm:"login_time" json:"login_time"` // 访问时间 +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (r *Entity) OmitEmpty() *arModel { + return Model.Data(r).OmitEmpty() +} + +// Inserts does "INSERT...INTO..." statement for inserting current object into table. +func (r *Entity) Insert() (result sql.Result, err error) { + return Model.Data(r).Insert() +} + +// Replace does "REPLACE...INTO..." statement for inserting current object into table. +// If there's already another same record in the table (it checks using primary key or unique index), +// it deletes it and insert this one. +func (r *Entity) Replace() (result sql.Result, err error) { + return Model.Data(r).Replace() +} + +// Save does "INSERT...INTO..." statement for inserting/updating current object into table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Save() (result sql.Result, err error) { + return Model.Data(r).Save() +} + +// Update does "UPDATE...WHERE..." statement for updating current object from table. +// It updates the record if there's already another same record in the table +// (it checks using primary key or unique index). +func (r *Entity) Update() (result sql.Result, err error) { + return Model.Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() +} + +// Delete does "DELETE FROM...WHERE..." statement for deleting current object from table. +func (r *Entity) Delete() (result sql.Result, err error) { + return Model.Where(gdb.GetWhereConditionOfStruct(r)).Delete() +} diff --git a/app/model/admin/sys_login_log/sys_login_log_model.go b/app/model/admin/sys_login_log/sys_login_log_model.go new file mode 100644 index 0000000..0fe4678 --- /dev/null +++ b/app/model/admin/sys_login_log/sys_login_log_model.go @@ -0,0 +1,367 @@ +// ========================================================================== +// This is auto-generated by gf cli tool. You may not really want to edit it. +// ========================================================================== + +package sys_login_log + +import ( + "database/sql" + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" + "time" +) + +// arModel is a active record design model for table sys_login_log operations. +type arModel struct { + M *gdb.Model +} + +var ( + // Table is the table name of sys_login_log. + Table = "sys_login_log" + // Model is the model object of sys_login_log. + Model = &arModel{g.DB("default").Table(Table).Safe()} +) + +// FindOne is a convenience method for Model.FindOne. +// See Model.FindOne. +func FindOne(where ...interface{}) (*Entity, error) { + return Model.FindOne(where...) +} + +// FindAll is a convenience method for Model.FindAll. +// See Model.FindAll. +func FindAll(where ...interface{}) ([]*Entity, error) { + return Model.FindAll(where...) +} + +// FindValue is a convenience method for Model.FindValue. +// See Model.FindValue. +func FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return Model.FindValue(fieldsAndWhere...) +} + +// FindCount is a convenience method for Model.FindCount. +// See Model.FindCount. +func FindCount(where ...interface{}) (int, error) { + return Model.FindCount(where...) +} + +// Insert is a convenience method for Model.Insert. +func Insert(data ...interface{}) (result sql.Result, err error) { + return Model.Insert(data...) +} + +// Replace is a convenience method for Model.Replace. +func Replace(data ...interface{}) (result sql.Result, err error) { + return Model.Replace(data...) +} + +// Save is a convenience method for Model.Save. +func Save(data ...interface{}) (result sql.Result, err error) { + return Model.Save(data...) +} + +// Update is a convenience method for Model.Update. +func Update(dataAndWhere ...interface{}) (result sql.Result, err error) { + return Model.Update(dataAndWhere...) +} + +// Delete is a convenience method for Model.Delete. +func Delete(where ...interface{}) (result sql.Result, err error) { + return Model.Delete(where...) +} + +// As sets an alias name for current table. +func (m *arModel) As(as string) *arModel { + return &arModel{m.M.As(as)} +} + +// TX sets the transaction for current operation. +func (m *arModel) TX(tx *gdb.TX) *arModel { + return &arModel{m.M.TX(tx)} +} + +// Master marks the following operation on master node. +func (m *arModel) Master() *arModel { + return &arModel{m.M.Master()} +} + +// Slave marks the following operation on slave node. +// Note that it makes sense only if there's any slave node configured. +func (m *arModel) Slave() *arModel { + return &arModel{m.M.Slave()} +} + +// LeftJoin does "LEFT JOIN ... ON ..." statement on the model. +func (m *arModel) LeftJoin(joinTable string, on string) *arModel { + return &arModel{m.M.LeftJoin(joinTable, on)} +} + +// RightJoin does "RIGHT JOIN ... ON ..." statement on the model. +func (m *arModel) RightJoin(joinTable string, on string) *arModel { + return &arModel{m.M.RightJoin(joinTable, on)} +} + +// InnerJoin does "INNER JOIN ... ON ..." statement on the model. +func (m *arModel) InnerJoin(joinTable string, on string) *arModel { + return &arModel{m.M.InnerJoin(joinTable, on)} +} + +// Fields sets the operation fields of the model, multiple fields joined using char ','. +func (m *arModel) Fields(fields string) *arModel { + return &arModel{m.M.Fields(fields)} +} + +// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','. +func (m *arModel) FieldsEx(fields string) *arModel { + return &arModel{m.M.FieldsEx(fields)} +} + +// Option sets the extra operation option for the model. +func (m *arModel) Option(option int) *arModel { + return &arModel{m.M.Option(option)} +} + +// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers +// the data and where attributes for empty values. +func (m *arModel) OmitEmpty() *arModel { + return &arModel{m.M.OmitEmpty()} +} + +// Filter marks filtering the fields which does not exist in the fields of the operated table. +func (m *arModel) Filter() *arModel { + return &arModel{m.M.Filter()} +} + +// Where sets the condition statement for the model. The parameter can be type of +// string/map/gmap/slice/struct/*struct, etc. Note that, if it's called more than one times, +// multiple conditions will be joined into where statement using "AND". +// Eg: +// Where("uid=10000") +// Where("uid", 10000) +// Where("money>? AND name like ?", 99999, "vip_%") +// Where("uid", 1).Where("name", "john") +// Where("status IN (?)", g.Slice{1,2,3}) +// Where("age IN(?,?)", 18, 50) +// Where(User{ Id : 1, UserName : "john"}) +func (m *arModel) Where(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Where(where, args...)} +} + +// And adds "AND" condition to the where statement. +func (m *arModel) And(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.And(where, args...)} +} + +// Or adds "OR" condition to the where statement. +func (m *arModel) Or(where interface{}, args ...interface{}) *arModel { + return &arModel{m.M.Or(where, args...)} +} + +// Group sets the "GROUP BY" statement for the model. +func (m *arModel) Group(groupBy string) *arModel { + return &arModel{m.M.Group(groupBy)} +} + +// Order sets the "ORDER BY" statement for the model. +func (m *arModel) Order(orderBy string) *arModel { + return &arModel{m.M.Order(orderBy)} +} + +// Limit sets the "LIMIT" statement for the model. +// The parameter can be either one or two number, if passed two number is passed, +// it then sets "LIMIT limit[0],limit[1]" statement for the model, or else it sets "LIMIT limit[0]" +// statement. +func (m *arModel) Limit(limit ...int) *arModel { + return &arModel{m.M.Limit(limit...)} +} + +// Offset sets the "OFFSET" statement for the model. +// It only makes sense for some databases like SQLServer, PostgreSQL, etc. +func (m *arModel) Offset(offset int) *arModel { + return &arModel{m.M.Offset(offset)} +} + +// Page sets the paging number for the model. +// The parameter is started from 1 for paging. +// Note that, it differs that the Limit function start from 0 for "LIMIT" statement. +func (m *arModel) Page(page, limit int) *arModel { + return &arModel{m.M.Page(page, limit)} +} + +// Batch sets the batch operation number for the model. +func (m *arModel) Batch(batch int) *arModel { + return &arModel{m.M.Batch(batch)} +} + +// Cache sets the cache feature for the model. It caches the result of the sql, which means +// if there's another same sql request, it just reads and returns the result from cache, it +// but not committed and executed into the database. +// +// If the parameter < 0, which means it clear the cache with given . +// If the parameter = 0, which means it never expires. +// If the parameter > 0, which means it expires after . +// +// The optional parameter is used to bind a name to the cache, which means you can later +// control the cache like changing the or clearing the cache with specified . +// +// Note that, the cache feature is disabled if the model is operating on a transaction. +func (m *arModel) Cache(expire time.Duration, name ...string) *arModel { + return &arModel{m.M.Cache(expire, name...)} +} + +// Data sets the operation data for the model. +// The parameter can be type of string/map/gmap/slice/struct/*struct, etc. +// Eg: +// Data("uid=10000") +// Data("uid", 10000) +// Data(g.Map{"uid": 10000, "name":"john"}) +// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"}) +func (m *arModel) Data(data ...interface{}) *arModel { + return &arModel{m.M.Data(data...)} +} + +// Insert does "INSERT INTO ..." statement for the model. +// The optional parameter is the same as the parameter of Model.Data function, +// see Model.Data. +func (m *arModel) Insert(data ...interface{}) (result sql.Result, err error) { + return m.M.Insert(data...) +} + +// Replace does "REPLACE INTO ..." statement for the model. +// The optional parameter is the same as the parameter of Model.Data function, +// see Model.Data. +func (m *arModel) Replace(data ...interface{}) (result sql.Result, err error) { + return m.M.Replace(data...) +} + +// Save does "INSERT INTO ... ON DUPLICATE KEY UPDATE..." statement for the model. +// It updates the record if there's primary or unique index in the saving data, +// or else it inserts a new record into the table. +// +// The optional parameter is the same as the parameter of Model.Data function, +// see Model.Data. +func (m *arModel) Save(data ...interface{}) (result sql.Result, err error) { + return m.M.Save(data...) +} + +// Update does "UPDATE ... " statement for the model. +// +// If the optional parameter is given, the dataAndWhere[0] is the updated +// data field, and dataAndWhere[1:] is treated as where condition fields. +// Also see Model.Data and Model.Where functions. +func (m *arModel) Update(dataAndWhere ...interface{}) (result sql.Result, err error) { + return m.M.Update(dataAndWhere...) +} + +// Delete does "DELETE FROM ... " statement for the model. +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) Delete(where ...interface{}) (result sql.Result, err error) { + return m.M.Delete(where...) +} + +// Count does "SELECT COUNT(x) FROM ..." statement for the model. +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) Count(where ...interface{}) (int, error) { + return m.M.Count(where...) +} + +// All does "SELECT FROM ..." statement for the model. +// It retrieves the records from table and returns the result as []*Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) All(where ...interface{}) ([]*Entity, error) { + all, err := m.M.All(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// One retrieves one record from table and returns the result as *Entity. +// It returns nil if there's no record retrieved with the given conditions from table. +// +// The optional parameter is the same as the parameter of Model.Where function, +// see Model.Where. +func (m *arModel) One(where ...interface{}) (*Entity, error) { + one, err := m.M.One(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// Value retrieves a specified record value from table and returns the result as interface type. +// It returns nil if there's no record found with the given conditions from table. +// +// If the optional parameter is given, the fieldsAndWhere[0] is the selected fields +// and fieldsAndWhere[1:] is treated as where condition fields. +// Also see Model.Fields and Model.Where functions. +func (m *arModel) Value(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return m.M.Value(fieldsAndWhere...) +} + +// FindOne retrieves and returns a single Record by Model.WherePri and Model.One. +// Also see Model.WherePri and Model.One. +func (m *arModel) FindOne(where ...interface{}) (*Entity, error) { + one, err := m.M.FindOne(where...) + if err != nil { + return nil, err + } + var entity *Entity + if err = one.Struct(&entity); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entity, nil +} + +// FindAll retrieves and returns Result by by Model.WherePri and Model.All. +// Also see Model.WherePri and Model.All. +func (m *arModel) FindAll(where ...interface{}) ([]*Entity, error) { + all, err := m.M.FindAll(where...) + if err != nil { + return nil, err + } + var entities []*Entity + if err = all.Structs(&entities); err != nil && err != sql.ErrNoRows { + return nil, err + } + return entities, nil +} + +// FindValue retrieves and returns single field value by Model.WherePri and Model.Value. +// Also see Model.WherePri and Model.Value. +func (m *arModel) FindValue(fieldsAndWhere ...interface{}) (gdb.Value, error) { + return m.M.FindValue(fieldsAndWhere...) +} + +// FindCount retrieves and returns the record number by Model.WherePri and Model.Count. +// Also see Model.WherePri and Model.Count. +func (m *arModel) FindCount(where ...interface{}) (int, error) { + return m.M.FindCount(where...) +} + +// Chunk iterates the table with given size and callback function. +func (m *arModel) Chunk(limit int, callback func(entities []*Entity, err error) bool) { + m.M.Chunk(limit, func(result gdb.Result, err error) bool { + var entities []*Entity + err = result.Structs(&entities) + if err == sql.ErrNoRows { + return false + } + return callback(entities, err) + }) +} diff --git a/app/service/admin/dict_service/dict_type.go b/app/service/admin/dict_service/dict_type.go index f9d5569..5c34f45 100644 --- a/app/service/admin/dict_service/dict_type.go +++ b/app/service/admin/dict_service/dict_type.go @@ -7,6 +7,7 @@ import ( "github.com/gogf/gf/errors/gerror" "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" ) @@ -139,7 +140,7 @@ func GetDictById(id int) (dict *sys_dict_type.Entity, err error) { } //通过字典键类型获取选项 -func GetDictWithDataByType(dictType, defaultValue string) (dict g.Map, err error) { +func GetDictWithDataByType(dictType, defaultValue, emptyLabel string) (dict g.Map, err error) { dictEntity, err := sys_dict_type.Model.FindOne("dict_type", dictType) if err != nil { g.Log().Error(err) @@ -160,10 +161,10 @@ func GetDictWithDataByType(dictType, defaultValue string) (dict g.Map, err error for k, v := range dictDataEntities { isDefault := 0 if defaultValue != "" { - if defaultValue == v.DictValue { + if gstr.Equal(defaultValue, v.DictValue) { isDefault = 1 } - } else { + } else if emptyLabel == "" { isDefault = v.IsDefault } values[k] = g.Map{ @@ -172,6 +173,9 @@ func GetDictWithDataByType(dictType, defaultValue string) (dict g.Map, err error "isDefault": isDefault, } } + if emptyLabel != "" { + values = append(g.List{g.Map{"isDefault": 0, "key": "", "value": emptyLabel}}, values...) + } dict = g.Map{ "dict_name": dictEntity.DictName, "remark": dictEntity.Remark, diff --git a/app/service/admin/monitor_service/job.go b/app/service/admin/monitor_service/job.go new file mode 100644 index 0000000..8c00df1 --- /dev/null +++ b/app/service/admin/monitor_service/job.go @@ -0,0 +1,194 @@ +package monitor_service + +import ( + "gfast/app/model/admin/sys_job" + "gfast/app/task" + "gfast/library/utils" + "github.com/gogf/gf/errors/gerror" + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/os/gcron" + "github.com/gogf/gf/os/gtime" + "github.com/gogf/gf/util/gconv" + "strings" +) + +func init() { + //自动执行已开启的任务 + jobs, err := sys_job.Model.Where("status", 0).All() + if err != nil { + g.Log().Error(err) + } + for _, job := range jobs { + JobStart(job) + } +} + +//添加计划任务 +func AddJob(req *sys_job.ReqAdd, userId int) (id int64, err error) { + entity := new(sys_job.Entity) + entity.JobName = req.JobName + entity.JobGroup = req.JobGroup + entity.InvokeTarget = req.InvokeTarget + entity.JobParams = req.JobParams + entity.CronExpression = req.CronExpression + entity.MisfirePolicy = req.MisfirePolicy + entity.Remark = req.Remark + entity.Status = req.Status + entity.CreateTime = gconv.Uint64(gtime.Timestamp()) + entity.CreateBy = gconv.Uint64(userId) + res, err := entity.Save() + if err != nil { + g.Log().Error(err) + err = gerror.New("添加任务失败") + } + id, err = res.LastInsertId() + if err != nil { + g.Log().Error(err) + err = gerror.New("添加任务失败") + } + return +} + +//修改计划任务 +func EditJob(req *sys_job.ReqEdit, userId int) (rows int64, err error) { + entity, err := GetJobInfoById(req.JobId) + if err != nil { + return + } + entity.JobName = req.JobName + entity.JobGroup = req.JobGroup + entity.InvokeTarget = req.InvokeTarget + entity.JobParams = req.JobParams + entity.CronExpression = req.CronExpression + entity.MisfirePolicy = req.MisfirePolicy + entity.Remark = req.Remark + entity.Status = req.Status + entity.UpdateTime = gconv.Uint64(gtime.Timestamp()) + entity.UpdateBy = gconv.Uint64(userId) + res, err := entity.Update() + if err != nil { + g.Log().Error(err) + err = gerror.New("修改任务失败") + } + rows, err = res.RowsAffected() + if err != nil { + g.Log().Error(err) + err = gerror.New("修改任务失败") + } + return +} + +//任务列表 +func JobListByPage(req *sys_job.SelectPageReq) (total, page int, list []*sys_job.Entity, err error) { + model := sys_job.Model + if req != nil { + if req.Status != "" { + model = model.Where("status", gconv.Int(req.Status)) + } + if req.JobGroup != "" { + model = model.Where("job_group", req.JobGroup) + } + if req.JobName != "" { + model = model.Where("job_name like ?", "%"+req.JobName+"%") + } + } + total, err = model.Count() + if err != nil { + g.Log().Error(err) + err = gerror.New("获取总行数失败") + return + } + if req.PageNum == 0 { + req.PageNum = 1 + } + page = req.PageNum + if req.PageSize == 0 { + req.PageSize = utils.AdminPageNum + } + list, err = model.Page(page, req.PageSize).Order("job_id asc").All() + if err != nil { + g.Log().Error(err) + err = gerror.New("获取数据失败") + return + } + return +} + +//通过id获取任务信息 +func GetJobInfoById(id int64) (job *sys_job.Entity, err error) { + if id == 0 { + err = gerror.New("参数错误") + return + } + job, err = sys_job.Model.FindOne("job_id", id) + if err != nil { + g.Log().Error(err) + } + if job == nil || err != nil { + err = gerror.New("获取任务信息失败") + return + } + return +} + +//批量删除计划任务 +func DeleteJobByIds(ids []int) (err error) { + if len(ids) == 0 { + err = gerror.New("参数错误") + } + _, err = sys_job.Model.Delete("job_id in (?)", ids) + if err != nil { + g.Log().Error(err) + err = gerror.New("删除失败") + } + return +} + +//启动任务 +func JobStart(job *sys_job.Entity) error { + //可以task目录下是否绑定对应的方法 + f := task.GetByName(job.InvokeTarget) + if f == nil { + return gerror.New("当前task目录下没有绑定这个方法") + } + //传参 + paramArr := strings.Split(job.JobParams, "|") + g.Log().Debug(paramArr) + task.EditParams(f.FuncName, paramArr) + rs := gcron.Search(job.InvokeTarget) + if rs == nil { + if job.MisfirePolicy == 1 { + task, err := gcron.Add(job.CronExpression, f.Run, job.InvokeTarget) + if err != nil || task == nil { + return err + } + } else { + task, err := gcron.AddOnce(job.CronExpression, f.Run, job.InvokeTarget) + if err != nil || task == nil { + return err + } + } + } + gcron.Start(job.InvokeTarget) + if job.MisfirePolicy == 1 { + job.Status = 0 + job.Update() + } + return nil +} + +//停止任务 +func JobStop(job *sys_job.Entity) error { + //可以task目录下是否绑定对应的方法 + f := task.GetByName(job.InvokeTarget) + if f == nil { + return gerror.New("当前task目录下没有绑定这个方法") + } + rs := gcron.Search(job.InvokeTarget) + if rs != nil { + gcron.Stop(job.InvokeTarget) + } + job.Status = 1 + job.Update() + return nil +} diff --git a/app/service/cache_service/cache.go b/app/service/cache_service/cache.go index dc001b6..47e2841 100644 --- a/app/service/cache_service/cache.go +++ b/app/service/cache_service/cache.go @@ -136,8 +136,8 @@ func (c *CacheTagService) Removes(keys []interface{}) { // Remove deletes the in the cache, and returns its value. func (c *CacheTagService) RemoveByTag(tag interface{}) { - c.setTagKey(tag) tagSetMux.Lock() + c.setTagKey(tag) //删除tagKey 对应的 key和值 keys := c.Get(c.tagKey) if keys != nil { diff --git a/app/task/config.go b/app/task/config.go new file mode 100644 index 0000000..b0eb58d --- /dev/null +++ b/app/task/config.go @@ -0,0 +1,47 @@ +package task + +import "github.com/gogf/gf/container/garray" + +type Entity struct { + FuncName string + Param []string + Run func() +} + +var taskList = garray.New() + +//增加Task方法 +func Add(task Entity) { + if task.FuncName == "" { + return + } + if task.Run == nil { + return + } + taskList.Append(task) +} + +//检查方法名是否存在 +func GetByName(funcName string) *Entity { + var result *Entity + for _, item := range taskList.Slice() { + task := item.(Entity) + if task.FuncName == funcName { + result = &task + break + } + } + return result +} + +//修改参数 +func EditParams(funcName string, params []string) { + for index, item := range taskList.Slice() { + task := item.(Entity) + if task.FuncName == funcName { + task.Param = params + taskList.Set(index, task) + break + } + } +} diff --git a/app/task/functions.go b/app/task/functions.go new file mode 100644 index 0000000..85606ae --- /dev/null +++ b/app/task/functions.go @@ -0,0 +1,32 @@ +package task + +func init() { + var task1 Entity + task1.FuncName = "test1" + task1.Param = nil + task1.Run = Test1 + Add(task1) + + var task2 Entity + task2.FuncName = "test2" + task2.Param = nil + task2.Run = Test2 + Add(task2) +} + +//无参测试 +func Test1() { + println("无参测试") +} + +//传参测试 +func Test2() { + //获取参数 + task := GetByName("test2") + if task == nil { + return + } + for _, v := range task.Param { + println(v) + } +} diff --git a/data/db.sql b/data/db.sql index 7ea0372..a323d82 100644 --- a/data/db.sql +++ b/data/db.sql @@ -302,7 +302,7 @@ CREATE TABLE `sys_job` ( `update_time` bigint(20) unsigned DEFAULT '0' COMMENT '更新时间', `remark` varchar(500) DEFAULT '' COMMENT '备注信息', PRIMARY KEY (`job_id`,`job_name`,`job_group`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='定时任务调度表'; +) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COMMENT='定时任务调度表'; /*!40101 SET character_set_client = @saved_cs_client */; -- @@ -311,9 +311,40 @@ CREATE TABLE `sys_job` ( LOCK TABLES `sys_job` WRITE; /*!40000 ALTER TABLE `sys_job` DISABLE KEYS */; +INSERT INTO `sys_job` VALUES (1,'测试任务1','','DEFAULT','test1','* * * * * *',1,0,1,1,1583805259,0,0,''),(2,'测试任务2','hello|gfast','DEFAULT','test2','* * * * * *',1,0,1,1,1583805312,1,1583809723,'备注'),(6,'测试任务666','hello|gfast','DEFAULT','test2','* * * * * *',1,0,1,1,1583811085,1,1583811184,'备注'); /*!40000 ALTER TABLE `sys_job` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `sys_login_log` +-- + +DROP TABLE IF EXISTS `sys_login_log`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!40101 SET character_set_client = utf8 */; +CREATE TABLE `sys_login_log` ( + `info_id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '访问ID', + `login_name` varchar(50) DEFAULT '' COMMENT '登录账号', + `ipaddr` varchar(50) DEFAULT '' COMMENT '登录IP地址', + `login_location` varchar(255) DEFAULT '' COMMENT '登录地点', + `browser` varchar(50) DEFAULT '' COMMENT '浏览器类型', + `os` varchar(50) DEFAULT '' COMMENT '操作系统', + `status` tinyint(4) DEFAULT '0' COMMENT '登录状态(0成功 1失败)', + `msg` varchar(255) DEFAULT '' COMMENT '提示消息', + `login_time` bigint(20) DEFAULT '0' COMMENT '访问时间', + PRIMARY KEY (`info_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='系统访问记录'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_login_log` +-- + +LOCK TABLES `sys_login_log` WRITE; +/*!40000 ALTER TABLE `sys_login_log` DISABLE KEYS */; +/*!40000 ALTER TABLE `sys_login_log` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `user` -- @@ -392,4 +423,4 @@ UNLOCK TABLES; /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; --- Dump completed on 2020-03-09 23:07:26 +-- Dump completed on 2020-03-10 18:03:43 diff --git a/go.mod b/go.mod index b9eaf74..0ee6651 100644 --- a/go.mod +++ b/go.mod @@ -1,11 +1,14 @@ module gfast require ( + github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/casbin/casbin/v2 v2.1.2 + github.com/go-ole/go-ole v1.2.4 // indirect github.com/goflyfox/gtoken v1.3.11 github.com/gogf/gf v1.11.5 github.com/mojocn/base64Captcha v1.3.0 - github.com/mssola/user_agent v0.5.1 + github.com/mssola/user_agent v0.5.1 + github.com/shirou/gopsutil v2.20.2+incompatible ) go 1.14 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..098a03a --- /dev/null +++ b/go.sum @@ -0,0 +1,65 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw= +github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/casbin/casbin/v2 v2.1.2 h1:bTwon/ECRx9dwBy2ewRVr5OiqjeXSGiTUY74sDPQi/g= +github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= +github.com/clbanning/mxj v1.8.4 h1:HuhwZtbyvyOw+3Z1AowPkU87JkJUSv751ELWaiTpj8I= +github.com/clbanning/mxj v1.8.4/go.mod h1:BVjHeAH+rl9rs6f+QIpeRl0tfu10SXn1pUSa5PVGJng= +github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= +github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/gf-third/mysql v1.4.2 h1:f1M5CNFUG3WkE07UOomtu4o0n/KJKeuUUf5Nc9ZFXs4= +github.com/gf-third/mysql v1.4.2/go.mod h1:+dd90V663ppI2fV5uQ6+rHk0u8KCyU6FkG8Um8Cx3ms= +github.com/gf-third/yaml v1.0.1 h1:pqD4ix+65DqGphU1MDnToPZfGYk0tuuwRzuTSl3g0d0= +github.com/gf-third/yaml v1.0.1/go.mod h1:t443vj0txEw3+E0MOtkr83kt+PrZg2I8SRuYfn85NM0= +github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= +github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= +github.com/goflyfox/gtoken v1.3.9 h1:9WSFlk0R9Fy8vmbjKqR33Yqfsce9TYfn2lMwZ9zVOPU= +github.com/goflyfox/gtoken v1.3.9/go.mod h1:AdsIiP+9xYazR69j4dN1nLYdBoC+Xh3UVUYjqOM8OJs= +github.com/goflyfox/gtoken v1.3.11 h1:dttowT4XrxtDrmpNYCtF23mtpNW2keNR/Me5ULPsQjw= +github.com/goflyfox/gtoken v1.3.11/go.mod h1:2oUK27DG/F+jVPCGtEWsKL5aM4zmCIg9/VREkGhIrw0= +github.com/gogf/gf v1.10.1 h1:mu1VWviGm8ucgFNODQnw8ourgvgNBBovbLFbot/70BY= +github.com/gogf/gf v1.10.1/go.mod h1:/37gncPmuM06D4YSqiDze9GsasDtF2QnWkUfKeiGW/Q= +github.com/gogf/gf v1.11.2/go.mod h1:/37gncPmuM06D4YSqiDze9GsasDtF2QnWkUfKeiGW/Q= +github.com/gogf/gf v1.11.4 h1:/G5cRbv6wOHmJroZUu2Q1N/qqYfbgvHgKI54HhRZAh0= +github.com/gogf/gf v1.11.4/go.mod h1:/37gncPmuM06D4YSqiDze9GsasDtF2QnWkUfKeiGW/Q= +github.com/gogf/gf v1.11.5 h1:e6HB9x1QZ6EFD3eEWXDCsFeVs7KxNtmWQRVNh2c0+bQ= +github.com/gogf/gf v1.11.5/go.mod h1:iuHZkqyEfxFtpwRYboAU7409O/sfdy79YTpY8si332I= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf h1:wIOAyJMMen0ELGiFzlmqxdcV1yGbkyHBAB6PolcNbLA= +github.com/grokify/html-strip-tags-go v0.0.0-20190921062105-daaa06bf1aaf/go.mod h1:2Su6romC5/1VXOQMaWL2yb618ARB8iVo6/DR99A6d78= +github.com/mattn/go-runewidth v0.0.7 h1:Ei8KR0497xHyKJPAv59M1dkC+rOZCMBJ+t3fZ+twI54= +github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mojocn/base64Captcha v1.3.0 h1:2mWu9fUoOx3ribrrsm4+8/UknSn8/g/xmPOkTwiY2Fo= +github.com/mojocn/base64Captcha v1.3.0/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= +github.com/mssola/user_agent v0.5.1 h1:sJUCUozh+j7c0dR2zMIUX5aJjoY/TNo/gXiNujoH5oY= +github.com/mssola/user_agent v0.5.1/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= +github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88= +github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/shirou/gopsutil v2.20.2+incompatible h1:ucK79BhBpgqQxPASyS2cu9HX8cfDVljBN1WWFvbNvgY= +github.com/shirou/gopsutil v2.20.2+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75 h1:TbGuee8sSq15Iguxu4deQ7+Bqq/d2rsQejGcEtADAMQ= +golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e h1:9vRrk9YW2BTzLP0VCB9ZDjU4cPqkg+IDWL7XgxA1yxQ= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= diff --git a/library/utils/tools.go b/library/utils/tools.go new file mode 100644 index 0000000..ee858ff --- /dev/null +++ b/library/utils/tools.go @@ -0,0 +1,42 @@ +package utils + +import ( + "net" + "time" +) + +//服务端ip +func GetLocalIP() (ip string, err error) { + addrs, err := net.InterfaceAddrs() + if err != nil { + return + } + for _, addr := range addrs { + ipAddr, ok := addr.(*net.IPNet) + if !ok { + continue + } + if ipAddr.IP.IsLoopback() { + continue + } + if !ipAddr.IP.IsGlobalUnicast() { + continue + } + return ipAddr.IP.String(), nil + } + return +} + +//获取相差时间 +func GetHourDiffer(startTime, endTime string) int64 { + var hour int64 + t1, err := time.ParseInLocation("2006-01-02 15:04:05", startTime, time.Local) + t2, err := time.ParseInLocation("2006-01-02 15:04:05", endTime, time.Local) + if err == nil && t1.Before(t2) { + diff := t2.Unix() - t1.Unix() // + hour = diff / 3600 + return hour + } else { + return hour + } +} diff --git a/router/router.go b/router/router.go index 79dba44..baed703 100644 --- a/router/router.go +++ b/router/router.go @@ -33,6 +33,7 @@ func init() { group.Group("/monitor", func(group *ghttp.RouterGroup) { group.ALL("/online", new(admin.MonitorOnline)) group.ALL("/job", new(admin.MonitorJob)) + group.ALL("/server", new(admin.MonitorServer)) }) }) diff --git a/test/demo2_test.go b/test/demo2_test.go index 55d8bb4..2f22994 100644 --- a/test/demo2_test.go +++ b/test/demo2_test.go @@ -2,7 +2,6 @@ package test import ( "fmt" - "github.com/gogf/gf/frame/g" "testing" ) @@ -11,15 +10,8 @@ func TestDemo2(t *testing.T) { } func test21(t *testing.T) { - c := make(chan bool) - for i := 0; i < 10000; i++ { - go func() { - i, e := g.Redis().Do("get", "GToken:adminIJ1xz+Wve+ZONVMFfXJQMw==50607842719694a7380dc72aacc4a0b4") - if e != nil { - fmt.Println(e) - } - fmt.Println(string(i.([]byte))) - }() - } - <-c + s := []int{1, 2, 3} + s1 := []int{0} + s1 = append(s1, s...) + fmt.Println(s1) }