基于(robfig_cron开发一个小工具)

包: https://github.com/robfig/cron

1
2
3
go get github.com/robfig/cron/v3@v3.0.0

import "github.com/robfig/cron/v3"

util

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package util

import (
"github.com/pkg/errors"
cron "github.com/robfig/cron/v3"
"sync"
)

// Crontab crontab manager
type Crontab struct {
inner *cron.Cron
ids map[string]cron.EntryID
mutex sync.Mutex
}

// NewCrontab new crontab
func NewCrontab() *Crontab {
return &Crontab{
inner: cron.New(),
ids: make(map[string]cron.EntryID),
}
}

// IDs ...
func (c *Crontab) IDs() []string {
c.mutex.Lock()
defer c.mutex.Unlock()
validIDs := make([]string, 0, len(c.ids))
invalidIDs := make([]string, 0)
for sid, eid := range c.ids {
if e := c.inner.Entry(eid); e.ID != eid {
invalidIDs = append(invalidIDs, sid)
continue
}
validIDs = append(validIDs, sid)
}
for _, id := range invalidIDs {
delete(c.ids, id)
}
return validIDs
}

// Start start the crontab engine
func (c *Crontab) Start() {
c.inner.Start()
}

// Stop stop the crontab engine
func (c *Crontab) Stop() {
c.inner.Stop()
}

// DelByID remove one crontab task
func (c *Crontab) DelByID(id string) {
c.mutex.Lock()
defer c.mutex.Unlock()

eid, ok := c.ids[id]
if !ok {
return
}
c.inner.Remove(eid)
delete(c.ids, id)
}

// AddByID add one crontab task
// id is unique
// spec is the crontab expression
func (c *Crontab) AddByID(id string, spec string, cmd cron.Job) error {
c.mutex.Lock()
defer c.mutex.Unlock()

if _, ok := c.ids[id]; ok {
return errors.Errorf("crontab id exists")
}
eid, err := c.inner.AddJob(spec, cmd)
if err != nil {
return err
}
c.ids[id] = eid
return nil
}

// AddByFunc add function as crontab task
func (c *Crontab) AddByFunc(id string, spec string, f func()) error {
c.mutex.Lock()
defer c.mutex.Unlock()

if _, ok := c.ids[id]; ok {
return errors.Errorf("crontab id exists")
}
eid, err := c.inner.AddFunc(spec, f)
if err != nil {
return err
}
c.ids[id] = eid
return nil
}

// IsExists check the crontab task whether existed with job id
func (c *Crontab) IsExists(jid string) bool {
_, exist := c.ids[jid]
return exist
}

使用

1
2
3
4
5
6
7
8
func main() {
crontab := util.NewCrontab()
crontab.AddByFunc(accAlert.ID, "0 */12 * * *", func() {
fmt.Print("测试!!!")
})
crontab.Start()
select {}
}

结合泛型和reflect写一个groupBy的工具类

1.18出来了,把之前一堆groupBy的代码拿出来用泛型改一下,效率可能略有下降,但是小频率使用,效果还行

**注:**这里的T必须是struct,而且key必须是string

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// GroupByMap 切片按key(字段)分组转map
func GroupByMap[T interface{}](values []*T, key string) map[string][]*T {
mapObj := make(map[string][]*T)
// 先去重
for _, groupKey := range removeRepeatedElement(values, key) {
// 再过滤
arr := filter(values, key, groupKey)
mapObj[groupKey] = arr
}
return mapObj
}

// 过滤,将每个key对应的val放置切片
func filter[T interface{}](arr []*T, key, keyAsVal string) (newArr []*T) {
newArr = make([]*T, 0)
for _, item := range arr {
itemVal := reflect.ValueOf(item).Elem().FieldByName(key).String()
if itemVal == keyAsVal {
newArr = append(newArr, item)
}
}
return newArr
}

// 将所有的key取出来并去重
func removeRepeatedElement[T interface{}](arr []*T, key string) (newArr []string) {
newArr = make([]string, 0)
for i := 0; i < len(arr); i++ {
repeat := false
// 反射取key对应的val
ikeyAsVal := reflect.ValueOf(arr[i]).Elem().FieldByName(key).String()
for j := i + 1; j < len(arr); j++ {
// 反射取key对应的val
jkeyAsVal := reflect.ValueOf(arr[j]).Elem().FieldByName(key).String()
if ikeyAsVal == jkeyAsVal {
repeat = true
break
}
}
if !repeat {
newArr = append(newArr, ikeyAsVal)
}
}
return newArr
}

统一结果响应,统一异常处理

定义统一响应体(包含code, msg, data和WithData / WithMsg方法)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package config

import (
"encoding/json"
"net/http"
)

var (
// OK
OK = response(http.StatusOK, "ok")
)

type Response struct {
Code int `json:"code"` // 错误码
Msg string `json:"msg"` // 错误描述
Data interface{} `json:"data"` // 返回数据
}

// 自定义响应信息
func (res *Response) WithMsg(message string) Response {
return Response{
Code: res.Code,
Msg: message,
Data: res.Data,
}
}

// 追加响应数据
func (res *Response) WithData(data interface{}) Response {
return Response{
Code: res.Code,
Msg: res.Msg,
Data: data,
}
}

// ToString 返回 JSON 格式的错误详情
func (res *Response) ToString() string {
err := &struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}{
Code: res.Code,
Msg: res.Msg,
Data: res.Data,
}
raw, _ := json.Marshal(err)
return string(raw)
}

// 构造函数
func response(code int, msg string) *Response {
return &Response{
Code: code,
Msg: msg,
Data: nil,
}
}

使用

1
2
3
4
func load(c *gin.Context) error {
c.JSON(http.StatusOK, config.OK.WithData(...))
return nil
}

定义响应错误的结构体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package config

import (
"github.com/gin-gonic/gin"
)

// APIException 错误的结构体,继承Response
type APIException struct {
*Response
}

// 实现error接口
func (e *APIException) Error() string {
return e.Msg
}

func newAPIException(code int, msg string) *APIException {
return &APIException{
&Response{
Code: code,
Msg: msg,
},
}
}
// 这里单独定义一个和gin HandlerFunc相同的type,供后续使用
type HandlerFunc func(c *gin.Context) error

异常处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package config

import (
"github.com/gin-gonic/gin"
"net/http"
)

// Wrapper 统一异常处理
func Wrapper(handler HandlerFunc) func(c *gin.Context) {
return func(c *gin.Context) {
var (
err error
)
err = handler(c)
if err != nil {
var apiException *APIException
// APIException异常
if h, ok := err.(*APIException); ok {
apiException = h
} else if e, ok := err.(error); ok {
if gin.Mode() == "debug" {
// 错误
apiException = ServerErrorWithMsg(e.Error())
} else {
// 未知错误
apiException = UnknownError(e.Error())
}
} else {
apiException = ServerError()
}
c.JSON(http.StatusOK, apiException)
return
}
}
}

// ServerError 500 错误处理
func ServerError() *APIException {
return newAPIException(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
}

// ServerErrorWithMsg 500 错误处理
func ServerErrorWithMsg(msg string) *APIException {
return newAPIException(http.StatusInternalServerError, msg)
}

// NotFound 404 错误
func NotFound() *APIException {
return newAPIException(http.StatusNotFound, http.StatusText(http.StatusNotFound))
}

// UnknownError 未知错误
func UnknownError(message string) *APIException {
return newAPIException(http.StatusForbidden, message)
}

// ParameterError 参数错误
func ParameterError(message string) *APIException {
return newAPIException(http.StatusBadRequest, message)
}

// HandleNotFound method/request not found处理
func HandleNotFound(c *gin.Context) {
handleErr := NotFound()
c.JSON(handleErr.Code, handleErr)
}

使用

1
2
3
4
5
6
func Route(e *gin.RouterGroup) {
e.POST("/", config.Wrapper(load))
}

func load(c *gin.Context) error {
}