2 Star 8 Fork 2

Josin / golang-cknit

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
main.go 8.49 KB
一键复制 编辑 原始数据 按行查看 历史
liqiongfan 提交于 2020-08-26 19:21 . 修复更新不生效的bug
package main
import (
"encoding/json"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"os/exec"
"strconv"
"strings"
"syscall"
"time"
)
type Resp struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data string `json:"data"`
}
type Id struct {
Id int `json:"id"`
}
type Job struct {
Name string `json:"name"`
Val string `json:"val"`
App string `json:"app"`
}
type Cron struct {
Id int `json:"id"`
Val Job `json:"val"`
}
type UpdateCron struct {
Id int `json:"id"`
Name string `json:"name"`
Val string `json:"val"`
App string `json:"app"`
}
var configFileName = "_config.json"
var appRunLogFile = "_command_error.log"
var appRunLogOkFile = "_command_success.log"
var maxId = 0
var crons []Cron
// *,17,3 7,6,8,9 7,9,7,3 * * *
func parseUnit(s string, vInt int) bool {
if len(s) == 0 {
return false
}
if s == "*" {
return true
}
tryInt, e := strconv.Atoi(s)
if e == nil && vInt == tryInt {
return true
}
var startIndex = 0
var str string
strLen := len(s)
for k := startIndex; k < strLen; k++ {
v := s[k]
if v == ',' {
str = s[startIndex:k]
if str == "*" {
return true
}
strInt, err := strconv.Atoi(str)
if err != nil {
return false
}
if vInt == strInt {
return true
}
startIndex = k + 1
} else if v == '-' {
strInt, err := strconv.Atoi(s[startIndex:k])
if err != nil {
return false
}
var sliceIndex = k + 1
for j := sliceIndex; j < strLen; j++ {
v = s[j]
k++
if v == ' ' || v == ',' || j == strLen-1 {
if j == strLen-1 {
j++
}
strInt2, e := strconv.Atoi(s[sliceIndex:j])
if e != nil {
return false
}
if vInt >= strInt && vInt <= strInt2 {
return true
}
break
}
}
startIndex = k + 1
} else if v == '/' {
var sliceIndex = k + 1
for j := sliceIndex; j < strLen; j++ {
v = s[j]
k++
if v == ' ' || v == ',' || j == strLen-1 {
if j == strLen-1 {
j++
}
strInt2, e := strconv.Atoi(s[sliceIndex:j])
if e != nil {
return false
}
if vInt%strInt2 == 0 {
return true
}
break
}
}
startIndex = k + 1
}
}
return false
}
// 解析字符串,判断是否匹配当前时间
func parseCronStr(str string) bool {
var nowTime = time.Now()
var month, day, hour, minute, second = int(nowTime.Month()), nowTime.Day(),
nowTime.Hour(), nowTime.Minute(),
nowTime.Second()
_, week := nowTime.ISOWeek()
if len(str) == 0 {
return false
}
cron := strings.Split(str, " ")
if len(cron) == 5 {
return parseUnit("1", second) &&
parseUnit(cron[0], minute) &&
parseUnit(cron[1], hour) &&
parseUnit(cron[2], day) &&
parseUnit(cron[3], month) &&
parseUnit(cron[4], week)
} else if len(cron) == 6 {
return parseUnit(cron[0], second) &&
parseUnit(cron[1], minute) &&
parseUnit(cron[2], hour) &&
parseUnit(cron[3], day) &&
parseUnit(cron[4], month) &&
parseUnit(cron[5], week)
}
return false
}
// 响应成功
func RespOK(w http.ResponseWriter, v string) {
d, _ := json.Marshal(Resp{
Code: 200,
Msg: "SUCCESS",
Data: v,
})
w.Header().Add("Content-Type", "application/json;charset=utf-8")
_, _ = w.Write(d)
}
// 响应失败
func RespErr(w http.ResponseWriter, v string) {
d, _ := json.Marshal(Resp{
Code: 500,
Msg: "ERROR",
Data: v,
})
w.Header().Add("Content-Type", "application/json;charset=utf-8")
_, _ = w.Write(d)
}
// Not used
func GetRequestStr(w http.ResponseWriter, v interface{}, r *http.Request) interface{} {
s, _ := ioutil.ReadAll(r.Body)
err := json.Unmarshal(s, v)
if err != nil {
RespErr(w, "请求包体不合法,请校验请求数据!")
return nil
}
return v
}
// 判断文件是否存在
func checkFileExist(file string) bool {
if _, err := os.Stat(file); os.IsExist(err) {
return false
}
return true
}
// 获取所有的定时任务
func GetAllCrons() []Cron {
if !checkFileExist(configFileName) {
log.Fatal("File: " + configFileName + " not found!")
}
var confArr []Cron
f, _ := os.OpenFile(configFileName, os.O_APPEND, 0666)
byteArr, _ := ioutil.ReadAll(f)
if len(byteArr) == 0 {
return confArr
}
err := json.Unmarshal(byteArr, &confArr)
if err != nil {
log.Fatal(configFileName + " content format wrong!")
}
return confArr
}
// 更新文件内容
func UpdateDb(b []byte, a bool) bool {
if !checkFileExist(configFileName) {
log.Fatal(configFileName + " not found!")
}
var flag int
flag = os.O_WRONLY | os.O_SYNC
if a {
flag |= os.O_APPEND
} else {
flag |= os.O_TRUNC
}
f, _ := os.OpenFile(configFileName, flag, 0666)
_, err := io.WriteString(f, string(b))
_ = syscall.Sync()
if err != nil {
return false
}
return true
}
// 默认请求
func defaultResponse(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte("Welcome to use cknit!"))
}
// 更新定时任务
func UpdateJob(w http.ResponseWriter, r *http.Request) {
s, _ := ioutil.ReadAll(r.Body)
var updateCron UpdateCron
err := json.Unmarshal(s, &updateCron)
if err != nil {
RespErr(w, "请求包体不合法,请校验请求数据!")
return
}
var res = make([]Cron, 0)
for _, d := range crons {
if d.Id == updateCron.Id {
res = append(res, Cron{
Id: d.Id,
Val: Job{
Name: updateCron.Name,
Val: updateCron.Val,
App: updateCron.App,
},
})
} else {
res = append(res, Cron{
Id: d.Id,
Val: d.Val,
})
}
}
crons = res
// 写入文件,进行sync同步操作
b, _ := json.Marshal(res)
if UpdateDb(b, false) {
RespOK(w, string(b))
} else {
RespErr(w, string("系统错误!"))
}
}
// 新增定时任务
func AddJob(w http.ResponseWriter, r *http.Request) {
s, _ := ioutil.ReadAll(r.Body)
var updateCron UpdateCron
err := json.Unmarshal(s, &updateCron)
if err != nil {
RespErr(w, "请求包体不合法,请校验请求数据!")
return
}
var cron = Cron{
Id: maxId,
Val: Job{
Name: updateCron.Name,
Val: updateCron.Val,
App: updateCron.App,
},
}
crons = append(crons, cron)
res, _ := json.Marshal(crons)
if UpdateDb(res, false) {
maxId++
RespOK(w, string(s))
} else {
RespErr(w, string("系统错误!"))
}
}
// 删除定时任务
func DeleteJob(w http.ResponseWriter, r *http.Request) {
s, _ := ioutil.ReadAll(r.Body)
var id Id
err := json.Unmarshal(s, &id)
if err != nil {
RespErr(w, "请求包体不合法,请校验请求数据!")
return
}
var res = make([]Cron, 0)
for _, v := range crons {
if v.Id == id.Id {
continue
} else {
res = append(res, Cron{
Id: v.Id,
Val: v.Val,
})
}
}
crons = res
// 写入文件,进行sync同步操作
b, _ := json.Marshal(res)
if UpdateDb(b, false) {
RespOK(w, string(b))
} else {
RespErr(w, string("系统错误!"))
}
}
// 执行增删改查操作
func Do(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case "GET": // 获取定时任务
b, _ := json.Marshal(crons)
RespOK(w, string(b))
case "PUT": // 修改定时任务
UpdateJob(w, r)
case "POST": // 新增定时任务
AddJob(w, r)
case "DELETE": // 删除定时任务
DeleteJob(w, r)
}
}
// 增加日志
func AppendLog(job *Job, file string, info string) {
f, _ := os.OpenFile(file, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_SYNC, 0666)
if len(info) == 0 {
info = " [Warning: No console output]\n"
}
var data = time.Now().Format("2006-01-02 15:04:05") + " " + job.Name + " " + job.App + " " + info
_, _ = io.WriteString(f, data)
}
// 执行定时任务
func doJob(jobs *[]Cron) {
if len(*jobs) == 0 {
return
}
for _, job := range *jobs {
if parseCronStr(job.Val.Val) {
commandArr := strings.Split(job.Val.App, " ")
cmd := exec.Command(commandArr[0], commandArr[1])
stdout, err := cmd.StdoutPipe()
if err != nil {
AppendLog(&job.Val, appRunLogFile, err.Error())
continue
}
if err := cmd.Start(); err != nil {
AppendLog(&job.Val, appRunLogFile, err.Error())
continue
}
if opBytes, err := ioutil.ReadAll(stdout); err != nil {
AppendLog(&job.Val, appRunLogFile, err.Error())
continue
} else {
AppendLog(&job.Val, appRunLogOkFile, string(opBytes))
}
stdout.Close()
}
}
}
// 执行定时任务
func DoCron() {
t := time.Tick(time.Second)
for {
select {
case <-t:
go doJob(&crons)
}
}
}
// 入口
func main() {
go DoCron()
crons = GetAllCrons()
http.HandleFunc("/", defaultResponse)
http.HandleFunc("/do", Do)
for _, v := range crons {
if maxId == 0 {
maxId = v.Id
}
if v.Id > maxId {
maxId = v.Id
}
}
maxId++
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatal("Can't create socket and listen!")
}
}
Go
1
https://gitee.com/josinli/golang-cknit.git
git@gitee.com:josinli/golang-cknit.git
josinli
golang-cknit
golang-cknit
master

搜索帮助