Compare commits

..

No commits in common. "master" and "develop" have entirely different histories.

82 changed files with 342 additions and 6815 deletions

3
.gitignore vendored

@ -6,7 +6,6 @@
*.dll
*.so
*.dylib
*.xlsx
# Test binary, built with `go test -c`
*.test
@ -18,5 +17,5 @@
# vendor/
.idea
.vscode
mail_test.go
*_test.go

@ -1,30 +0,0 @@
// Package balance...
//
// Description : balance...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-22 4:45 下午
package balance
import (
"net/http"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
// IBalance 负载算法接口约束
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:45 下午 2021/11/22
type IBalance interface {
// GetServerNode 获取一个服务器节点
GetServerNode(req *http.Request) (*define.ServerNode, error)
// AddServerNode 新增一个服务器节点
AddServerNode(node *define.ServerNode) error
// Remove 移除一个节点
Remove(hostIP string, port int, force bool)
// GetServerNodeList 获取服务节点列表
GetServerNodeList() []*define.ServerNode
}

@ -7,12 +7,7 @@
// Date : 2021-10-19 2:26 下午
package balance
import (
"errors"
"sync"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
import "sync"
// base ...
//
@ -20,8 +15,7 @@ import (
//
// Date : 2:26 下午 2021/10/19
type base struct {
lock *sync.RWMutex
severList []*define.ServerNode
lock *sync.RWMutex
}
// Lock ...
@ -59,53 +53,3 @@ func (b *base) RLock() {
func (b *base) RUnlock() {
b.lock.RUnlock()
}
// AddServerNode 新添加一个服务器节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:07 下午 2021/10/20
func (b *base) AddServerNode(node *define.ServerNode) error {
if len(node.HostIP) == 0 || node.Port <= 0 {
return errors.New("host ip or port is invalid")
}
b.Lock()
defer b.Unlock()
for _, item := range b.severList {
if item.HostIP == node.HostIP && item.Port == node.Port {
return errors.New("host config is already exist")
}
}
b.severList = append(b.severList, node)
return nil
}
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:19 下午 2021/10/20
func (b *base) Remove(hostIP string, port int, force bool) {
b.Lock()
defer b.Unlock()
tmpServerNode := make([]*define.ServerNode, 0)
for _, item := range b.severList {
if item.HostIP == hostIP && item.Port == port {
if force {
continue
}
item.Status = define.ServerNodeStatusRemove
}
tmpServerNode = append(tmpServerNode, item)
}
b.severList = tmpServerNode
}
// GetServerNodeList 获取服务器节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:33 下午 2021/10/20
func (b *base) GetServerNodeList() []*define.ServerNode {
return b.severList
}

@ -13,9 +13,8 @@ package define
//
// Date : 12:36 下午 2021/10/19
type ServerNode struct {
HostIP string `json:"host_ip"` // 机器IP
Port int `json:"port"` // 机器端口
Status int `json:"status"` // 机器状态
Weight float64 `json:"weight"` // 机器权重
CurrentWeight float64 `json:"current_weight"` // 当前权重
HostIP string `json:"host_ip"` // 机器IP
Port int `json:"port"` // 机器端口
Status int `json:"status"` // 机器状态
Weight int `json:"weight"` // 机器权重
}

@ -1,62 +0,0 @@
// Package balance...
//
// Description : balance...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-22 4:39 下午
package balance
import (
"net/http"
"sync"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
"github.com/pkg/errors"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
// NewIPHash ip hash 负载均衡
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:53 下午 2021/11/22
func NewIPHash(severList []*define.ServerNode) (IBalance, error) {
if nil == severList || len(severList) == 0 {
return nil, errors.New("sever list is empty")
}
return &IPHash{
base: base{
lock: &sync.RWMutex{},
severList: severList,
},
serverList: severList,
}, nil
}
// IPHash ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:57 下午 2021/11/22
type IPHash struct {
base
serverList []*define.ServerNode
}
// GetServerNode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:53 下午 2021/11/22
func (ih *IPHash) GetServerNode(req *http.Request) (*define.ServerNode, error) {
clintIPHashID := util.GetHashID(util.GetRemoteIp(req))
ih.RLock()
defer ih.RUnlock()
if len(ih.serverList) == 0 {
return nil, errors.New("sever list is empty")
}
return ih.serverList[int(clintIPHashID%uint64(len(ih.serverList)))], nil
}

@ -8,7 +8,6 @@
package balance
import (
"net/http"
"sync"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
@ -20,15 +19,15 @@ import (
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:40 下午 2021/10/19
func NewPoll(serverList []*define.ServerNode) (IBalance, error) {
func NewPoll(serverList []*define.ServerNode) (*Poll, error) {
if nil == serverList || len(serverList) == 0 {
return nil, errors.New("server list is empty")
}
return &Poll{
base: base{
lock: &sync.RWMutex{},
severList: serverList,
lock: &sync.RWMutex{},
},
serverList: serverList,
currentServerIndex: 0,
}, nil
}
@ -40,6 +39,7 @@ func NewPoll(serverList []*define.ServerNode) (IBalance, error) {
// Date : 12:41 下午 2021/10/19
type Poll struct {
base
serverList []*define.ServerNode
currentServerIndex int
}
@ -48,18 +48,21 @@ type Poll struct {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:43 下午 2021/10/19
func (p *Poll) GetServerNode(req *http.Request) (*define.ServerNode, error) {
p.RLock()
defer p.RUnlock()
func (p *Poll) GetServerNode() (*define.ServerNode, error) {
if len(p.serverList) == 0 {
return nil, errors.New("server list is empty")
}
p.Lock()
defer p.Unlock()
var (
serverNode *define.ServerNode
)
// 循环次数
loopTimes := 0
for loopTimes < len(p.severList) {
for loopTimes < len(p.serverList) {
loopTimes++
p.currentServerIndex = (p.currentServerIndex + 1) % len(p.severList)
if serverNode = p.severList[p.currentServerIndex]; serverNode.Status != define.ServerNodeStatusNormal {
p.currentServerIndex = (p.currentServerIndex + 1) % len(p.serverList)
if serverNode = p.serverList[p.currentServerIndex]; serverNode.Status != define.ServerNodeStatusNormal {
continue
}
break
@ -71,3 +74,57 @@ func (p *Poll) GetServerNode(req *http.Request) (*define.ServerNode, error) {
return serverNode, nil
}
// AddServerNode 新添加一个服务器节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:07 下午 2021/10/20
func (p *Poll) AddServerNode(hostIP string, port int) error {
if len(hostIP) == 0 || port <= 0 {
return errors.New("host ip or port is invalid")
}
p.Lock()
defer p.Unlock()
for _, item := range p.serverList {
if item.HostIP == hostIP && item.Port == port {
return errors.New("host config is already exist")
}
}
p.serverList = append(p.serverList, &define.ServerNode{
HostIP: hostIP,
Port: port,
Status: define.ServerNodeStatusNormal,
})
return nil
}
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:19 下午 2021/10/20
func (p *Poll) Remove(hostIP string, port int, force bool) {
p.Lock()
defer p.Unlock()
tmpServerNode := make([]*define.ServerNode, 0)
for _, item := range p.serverList {
if item.HostIP == hostIP && item.Port == port {
if force {
continue
}
item.Status = define.ServerNodeStatusRemove
}
tmpServerNode = append(tmpServerNode, item)
}
p.serverList = tmpServerNode
}
// GetServerNodeList 获取服务器节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:33 下午 2021/10/20
func (p *Poll) GetServerNodeList() []*define.ServerNode {
return p.serverList
}

@ -1,38 +0,0 @@
// Package balance...
//
// Description : balance...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-19 2:39 下午
package balance
import (
"fmt"
"testing"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
// TestPoll_GetServerNode 轮询模式负载均衡单测
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:39 下午 2021/10/19
func TestPoll_GetServerNode(t *testing.T) {
t.Run("轮询模式负载均衡单元测试", func(t *testing.T) {
p, _ := NewPoll([]*define.ServerNode{
{HostIP: "127.0.0.1", Port: 80, Status: 0, Weight: 0},
{HostIP: "127.0.0.1", Port: 81, Status: 0, Weight: 0},
{HostIP: "127.0.0.1", Port: 82, Status: 0, Weight: 0},
})
for i := 0; i < 100; i++ {
got, err := p.GetServerNode()
if nil != err {
fmt.Println(err)
return
}
fmt.Printf("%s:%d\n", got.HostIP, got.Port)
}
})
}

@ -1,59 +0,0 @@
// Package balance...
//
// Description : balance...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-22 6:27 下午
package balance
import (
"errors"
"net/http"
"sync"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
// NewRandom ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:29 下午 2021/11/22
func NewRandom(severList []*define.ServerNode) (IBalance, error) {
if nil == severList || len(severList) == 0 {
return nil, errors.New("sever list is empty")
}
return &Random{
base{
lock: &sync.RWMutex{},
severList: make([]*define.ServerNode, 0),
},
}, nil
}
// Random 随机负载均衡
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:27 下午 2021/11/22
type Random struct {
base
}
// GetServerNode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:31 下午 2021/11/22
func (r *Random) GetServerNode(req *http.Request) (*define.ServerNode, error) {
r.RLock()
defer r.RUnlock()
if len(r.severList) == 0 {
return nil, errors.New("sever list is empty")
}
randomID := util.GetHashID(util.GenRandomString("", 128)) % uint64(len(r.severList))
return r.severList[int(randomID)], nil
}

@ -1,114 +0,0 @@
// Package balance...
//
// Description : balance...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-22 6:58 下午
package balance
import (
"net/http"
"sync"
"github.com/pkg/errors"
"git.zhangdeman.cn/zhangdeman/gopkg/balance/define"
)
// NewWeightServerRoundRobin ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:58 下午 2021/11/22
func NewWeightServerRoundRobin(severList []*define.ServerNode) (IBalance, error) {
if nil == severList || len(severList) == 0 {
return nil, errors.New("sever list is empty")
}
return &WeightServerRoundRobin{
base: base{
lock: &sync.RWMutex{},
severList: severList,
},
effectiveWeight: 0,
}, nil
}
// WeightServerRoundRobin 加权轮询
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:58 下午 2021/11/22
type WeightServerRoundRobin struct {
base
effectiveWeight float64
}
// GetServerNode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:00 下午 2021/11/22
func (w *WeightServerRoundRobin) GetServerNode(req *http.Request) (*define.ServerNode, error) {
w.RLock()
defer w.RUnlock()
if len(w.severList) == 0 {
return nil, errors.New("sever list is empty")
}
var expectBackendServer *define.ServerNode
for _, backendServer := range w.severList {
// 给每个后端服务增加自身权重
backendServer.CurrentWeight += backendServer.Weight
if expectBackendServer == nil {
expectBackendServer = backendServer
}
if backendServer.CurrentWeight > expectBackendServer.CurrentWeight {
expectBackendServer = backendServer
}
}
// 把选择的后端服务权重减掉总权重
expectBackendServer.CurrentWeight -= w.effectiveWeight
return expectBackendServer, nil
}
// AddServerNode 新添加一个服务器节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:07 下午 2021/10/20
func (w *WeightServerRoundRobin) AddServerNode(node *define.ServerNode) error {
if len(node.HostIP) == 0 || node.Port <= 0 {
return errors.New("host ip or port is invalid")
}
w.Lock()
defer w.Unlock()
for _, item := range w.severList {
if item.HostIP == node.HostIP && item.Port == node.Port {
return errors.New("host config is already exist")
}
}
w.effectiveWeight += node.Weight
w.severList = append(w.severList, node)
return nil
}
// Remove 移除一个节点, force = true , 强制删除, force = false 逻辑删除, 设置状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:19 下午 2021/10/20
func (w *WeightServerRoundRobin) Remove(hostIP string, port int, force bool) {
w.Lock()
defer w.Unlock()
tmpServerNode := make([]*define.ServerNode, 0)
for _, item := range w.severList {
if item.HostIP == hostIP && item.Port == port {
if force {
continue
}
item.Status = define.ServerNodeStatusRemove
}
tmpServerNode = append(tmpServerNode, item)
}
w.severList = tmpServerNode
}

@ -1,42 +0,0 @@
// Package cmd...
//
// Description : cmd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-12 2:18 下午
package cmd
// Config 配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:19 下午 2021/11/12
type Config struct {
WorkDir string `json:"work_dir"` // 工作目录
Command string `json:"command"` // 指令
Script string `json:"script"` // 脚本
ParameterList []Parameter `json:"parameter_list"` // 参数列表
}
// Parameter ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:21 下午 2021/11/12
type Parameter struct {
Key string `json:"key"`
Value string `json:"value"`
}
// Result ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:25 下午 2021/11/12
type Result struct {
WorkDir string `json:"work_dir"`
Err error `json:"err"`
Output []byte `json:"output"`
ExecuteCommand string `json:"execute_command"`
}

@ -1,58 +0,0 @@
// Package cmd...
//
// Description : cmd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-12 2:21 下午
package cmd
import (
"os/exec"
"strings"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
)
// Execute 执行指令
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:22 下午 2021/11/12
func Execute(cmdConfig Config) *Result {
if len(cmdConfig.WorkDir) == 0 {
cmdConfig.WorkDir, _ = util.GetProjectPath()
}
paramList := buildCmdParameter(cmdConfig.Script, cmdConfig.ParameterList)
cmdInstance := exec.Command(cmdConfig.Command, paramList...)
// 设置指令的工作目录
cmdInstance.Dir = cmdConfig.WorkDir
result := &Result{
Err: nil,
Output: nil,
WorkDir: cmdConfig.WorkDir,
ExecuteCommand: cmdConfig.Command + " " + strings.Join(paramList, " "),
}
result.Output, result.Err = cmdInstance.CombinedOutput()
return result
}
// buildCmdParameter 构建参数列表
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:53 下午 2021/11/12
func buildCmdParameter(script string, parameterList []Parameter) []string {
paramList := make([]string, 0)
if len(script) > 0 {
paramList = append(paramList, script)
}
for _, item := range parameterList {
if len(item.Key) == 0 {
paramList = append(paramList, item.Value)
continue
}
paramList = append(paramList, item.Key+"="+item.Value)
}
return paramList
}

@ -1,35 +0,0 @@
// Package cmd...
//
// Description : cmd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-12 2:28 下午
package cmd
import (
"fmt"
"testing"
)
// TestExecute ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:28 下午 2021/11/12
func TestExecute(t *testing.T) {
r := Execute(Config{
WorkDir: "/tmp",
Command: "ls",
Script: "",
ParameterList: []Parameter{{"", "-l"}, {"", "-a"}},
})
fmt.Println(string(r.Output), r.Err, r.ExecuteCommand, r.WorkDir)
r = Execute(Config{
WorkDir: "",
Command: "sh",
Script: "test.sh",
ParameterList: []Parameter{},
})
fmt.Println(string(r.Output), r.Err, r.ExecuteCommand, r.WorkDir)
}

@ -1,103 +0,0 @@
// Package consistent_hash...
//
// Description : consistent_hash...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-21 4:57 下午
package consistent_hash
import (
"sort"
"strconv"
"sync"
)
// New 获取实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:03 下午 2021/11/21
func New(replicas int, fn HashFunc) *Consistent {
if nil == fn {
fn = DefaultHashFunc
}
return &Consistent{
hashFunc: fn,
replicas: replicas,
keys: make([]int, 0),
hashMap: make(map[int]string),
lock: &sync.RWMutex{},
}
}
// Consistent ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:00 下午 2021/11/21
type Consistent struct {
hashFunc HashFunc // 哈希函数
replicas int // 虚拟的数量
keys []int // 哈希环
hashMap map[int]string // 虚拟节点与真实节点的映射表
lock *sync.RWMutex // 锁
}
// Add 添加节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:10 下午 2021/11/21
func (c *Consistent) Add(keyList ...string) {
c.lock.Lock()
defer c.lock.Unlock()
for _, key := range keyList {
for i := 0; i < c.replicas; i++ {
hash := int(c.hashFunc([]byte(strconv.Itoa(i) + key)))
c.keys = append(c.keys, hash)
c.hashMap[hash] = key
}
}
// 哈希值排序
sort.Ints(c.keys)
}
// Get 获取节点
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:16 下午 2021/11/21
func (c *Consistent) Get(key string) string {
c.lock.RLock()
defer c.lock.Unlock()
if len(c.keys) == 0 {
return ""
}
hash := int(c.hashFunc([]byte(key)))
// 查找节点索引
idx := sort.Search(len(c.keys), func(i int) bool {
return c.keys[i] >= hash
})
return c.hashMap[c.keys[idx%len(c.keys)]]
}
// Delete 删除一个节点,本质逻辑 : 重建哈希环
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:35 下午 2021/11/21
func (c *Consistent) Delete(key string) {
c.lock.Lock()
defer c.lock.Unlock()
tmpKeyList := make([]string, 0)
for _, k := range c.hashMap {
if k == key {
continue
}
tmpKeyList = append(tmpKeyList, k)
}
c.hashMap = make(map[int]string)
c.keys = make([]int, 0)
c.Add(tmpKeyList...)
}

@ -1,22 +0,0 @@
// Package consistent_hash...
//
// Description : consistent_hash...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-21 4:49 下午
package consistent_hash
import "hash/crc32"
// HashFunc 哈希函数定义
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:50 下午 2021/11/21
type HashFunc func(data []byte) uint32
var (
// DefaultHashFunc 默认的哈希函数
DefaultHashFunc = crc32.ChecksumIEEE
)

@ -1,586 +0,0 @@
// Package demo ...
//
// Description : demo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-12-02 12:43 下午
package demo
import (
"errors"
"reflect"
"sync"
"sync/atomic"
"time"
)
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package context defines the Context type, which carries deadlines,
// cancellation signals, and other request-scoped values across API boundaries
// and between processes.
//
// Incoming requests to a server should create a Context, and outgoing
// calls to servers should accept a Context. The chain of function
// calls between them must propagate the Context, optionally replacing
// it with a derived Context created using WithCancel, WithDeadline,
// WithTimeout, or WithValue. When a Context is canceled, all
// Contexts derived from it are also canceled.
//
// The WithCancel, WithDeadline, and WithTimeout functions take a
// Context (the parent) and return a derived Context (the child) and a
// CancelFunc. Calling the CancelFunc cancels the child and its
// children, removes the parent's reference to the child, and stops
// any associated timers. Failing to call the CancelFunc leaks the
// child and its children until the parent is canceled or the timer
// fires. The go vet tool checks that CancelFuncs are used on all
// control-flow paths.
//
// Programs that use Contexts should follow these rules to keep interfaces
// consistent across packages and enable static analysis tools to check context
// propagation:
//
// Do not store Contexts inside a struct type; instead, pass a Context
// explicitly to each function that needs it. The Context should be the first
// parameter, typically named ctx:
//
// func DoSomething(ctx context.Context, arg Arg) error {
// // ... use ctx ...
// }
//
// Do not pass a nil Context, even if a function permits it. Pass context.TODO
// if you are unsure about which Context to use.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The same Context may be passed to functions running in different goroutines;
// Contexts are safe for simultaneous use by multiple goroutines.
//
// See https://blog.golang.org/context for example code for a server that uses
// Contexts.
// A Context carries a deadline, a cancellation signal, and other values across
// API boundaries.
//
// Context's methods may be called by multiple goroutines simultaneously.
type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
// The close of the Done channel may happen asynchronously,
// after the cancel function returns.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See https://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancellation.
Done() <-chan struct{}
// If Done is not yet closed, Err returns nil.
// If Done is closed, Err returns a non-nil error explaining why:
// Canceled if the context was canceled
// or DeadlineExceeded if the context's deadline passed.
// After Err returns a non-nil error, successive calls to Err return the same error.
Err() error
// Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stored using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}
// Canceled is the error returned by Context.Err when the context is canceled.
var Canceled = errors.New("context canceled")
// DeadlineExceeded is the error returned by Context.Err when the context's
// deadline passes.
var DeadlineExceeded error = deadlineExceededError{}
type deadlineExceededError struct{}
func (deadlineExceededError) Error() string { return "context deadline exceeded" }
func (deadlineExceededError) Timeout() bool { return true }
func (deadlineExceededError) Temporary() bool { return true }
// An emptyCtx is never canceled, has no values, and has no deadline. It is not
// struct{}, since vars of this type must have distinct addresses.
type emptyCtx int
func (*emptyCtx) Deadline() (deadline time.Time, ok bool) {
return
}
func (*emptyCtx) Done() <-chan struct{} {
return nil
}
func (*emptyCtx) Err() error {
return nil
}
func (*emptyCtx) Value(key interface{}) interface{} {
return nil
}
func (e *emptyCtx) String() string {
switch e {
case background:
return "context.Background"
case todo:
return "context.TODO"
}
return "unknown empty Context"
}
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)
// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}
// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).
func TODO() Context {
return todo
}
// A CancelFunc tells an operation to abandon its work.
// A CancelFunc does not wait for the work to stop.
// A CancelFunc may be called by multiple goroutines simultaneously.
// After the first call, subsequent calls to a CancelFunc do nothing.
type CancelFunc func()
// WithCancel returns a copy of parent with a new Done channel. The returned
// context's Done channel is closed when the returned cancel function is called
// or when the parent context's Done channel is closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
c := newCancelCtx(parent)
propagateCancel(parent, &c)
return &c, func() { c.cancel(true, Canceled) }
}
// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
return cancelCtx{Context: parent}
}
// goroutines counts the number of goroutines ever created; for testing.
var goroutines int32
// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
done := parent.Done()
if done == nil {
return // parent is never canceled
}
select {
case <-done:
// parent is already canceled
child.cancel(false, parent.Err())
return
default:
}
if p, ok := parentCancelCtx(parent); ok {
p.mu.Lock()
if p.err != nil {
// parent has already been canceled
child.cancel(false, p.err)
} else {
if p.children == nil {
p.children = make(map[canceler]struct{})
}
p.children[child] = struct{}{}
}
p.mu.Unlock()
} else {
atomic.AddInt32(&goroutines, +1)
go func() {
select {
case <-parent.Done():
child.cancel(false, parent.Err())
case <-child.Done():
}
}()
}
}
// &cancelCtxKey is the key that a cancelCtx returns itself for.
var cancelCtxKey int
// parentCancelCtx returns the underlying *cancelCtx for parent.
// It does this by looking up parent.Value(&cancelCtxKey) to find
// the innermost enclosing *cancelCtx and then checking whether
// parent.Done() matches that *cancelCtx. (If not, the *cancelCtx
// has been wrapped in a custom implementation providing a
// different done channel, in which case we should not bypass it.)
func parentCancelCtx(parent Context) (*cancelCtx, bool) {
done := parent.Done()
if done == closedchan || done == nil {
return nil, false
}
p, ok := parent.Value(&cancelCtxKey).(*cancelCtx)
if !ok {
return nil, false
}
pdone, _ := p.done.Load().(chan struct{})
if pdone != done {
return nil, false
}
return p, true
}
// removeChild removes a context from its parent.
func removeChild(parent Context, child canceler) {
p, ok := parentCancelCtx(parent)
if !ok {
return
}
p.mu.Lock()
if p.children != nil {
delete(p.children, child)
}
p.mu.Unlock()
}
// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
cancel(removeFromParent bool, err error)
Done() <-chan struct{}
}
// closedchan is a reusable closed channel.
var closedchan = make(chan struct{})
func init() {
close(closedchan)
}
// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
Context
mu sync.Mutex // protects following fields
done atomic.Value // of chan struct{}, created lazily, closed by first cancel call
children map[canceler]struct{} // set to nil by the first cancel call
err error // set to non-nil by the first cancel call
}
func (c *cancelCtx) Value(key interface{}) interface{} {
if key == &cancelCtxKey {
return c
}
return c.Context.Value(key)
}
func (c *cancelCtx) Done() <-chan struct{} {
d := c.done.Load()
if d != nil {
return d.(chan struct{})
}
c.mu.Lock()
defer c.mu.Unlock()
d = c.done.Load()
if d == nil {
d = make(chan struct{})
c.done.Store(d)
}
return d.(chan struct{})
}
func (c *cancelCtx) Err() error {
c.mu.Lock()
err := c.err
c.mu.Unlock()
return err
}
type stringer interface {
String() string
}
func contextName(c Context) string {
if s, ok := c.(stringer); ok {
return s.String()
}
return reflect.TypeOf(c).String()
}
func (c *cancelCtx) String() string {
return contextName(c.Context) + ".WithCancel"
}
// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
if err == nil {
panic("context: internal error: missing cancel error")
}
c.mu.Lock()
if c.err != nil {
c.mu.Unlock()
return // already canceled
}
c.err = err
d, _ := c.done.Load().(chan struct{})
if d == nil {
c.done.Store(closedchan)
} else {
close(d)
}
for child := range c.children {
// NOTE: acquiring the child's lock while holding parent's lock.
child.cancel(false, err)
}
c.children = nil
c.mu.Unlock()
if removeFromParent {
removeChild(c.Context, c)
}
}
// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) {
if parent == nil {
panic("cannot create context from nil parent")
}
if cur, ok := parent.Deadline(); ok && cur.Before(d) {
// The current deadline is already sooner than the new one.
return WithCancel(parent)
}
c := &timerCtx{
cancelCtx: newCancelCtx(parent),
deadline: d,
}
propagateCancel(parent, c)
dur := time.Until(d)
if dur <= 0 {
c.cancel(true, DeadlineExceeded) // deadline has already passed
return c, func() { c.cancel(false, Canceled) }
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err == nil {
c.timer = time.AfterFunc(dur, func() {
c.cancel(true, DeadlineExceeded)
})
}
return c, func() { c.cancel(true, Canceled) }
}
// A timerCtx carries a timer and a deadline. It embeds a cancelCtx to
// implement Done and Err. It implements cancel by stopping its timer then
// delegating to cancelCtx.cancel.
type timerCtx struct {
cancelCtx
timer *time.Timer // Under cancelCtx.mu.
deadline time.Time
}
func (c *timerCtx) Deadline() (deadline time.Time, ok bool) {
return c.deadline, true
}
func (c *timerCtx) String() string {
return contextName(c.cancelCtx.Context) + ".WithDeadline(" +
c.deadline.String() + " [" +
time.Until(c.deadline).String() + "])"
}
func (c *timerCtx) cancel(removeFromParent bool, err error) {
c.cancelCtx.cancel(false, err)
if removeFromParent {
// Remove this timerCtx from its parent cancelCtx's children.
removeChild(c.cancelCtx.Context, c)
}
c.mu.Lock()
if c.timer != nil {
c.timer.Stop()
c.timer = nil
}
c.mu.Unlock()
}
// WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete:
//
// func slowOperationWithTimeout(ctx context.Context) (Result, error) {
// ctx, cancel := context.WithTimeout(ctx, 100*time.Millisecond)
// defer cancel() // releases resources if slowOperation completes before timeout elapses
// return slowOperation(ctx)
// }
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
return WithDeadline(parent, time.Now().Add(timeout))
}
// WithValue returns a copy of parent in which the value associated with key is
// val.
//
// Use context Values only for request-scoped data that transits processes and
// APIs, not for passing optional parameters to functions.
//
// The provided key must be comparable and should not be of type
// string or any other built-in type to avoid collisions between
// packages using context. Users of WithValue should define their own
// types for keys. To avoid allocating when assigning to an
// interface{}, context keys often have concrete type
// struct{}. Alternatively, exported context key variables' static
// type should be a pointer or interface.
func WithValue(parent Context, key, val interface{}) Context {
if parent == nil {
panic("cannot create context from nil parent")
}
if key == nil {
panic("nil key")
}
if !reflect.TypeOf(key).Comparable() {
panic("key is not comparable")
}
return &valueCtx{parent, key, val}
}
// A valueCtx carries a key-value pair. It implements Value for that key and
// delegates all other calls to the embedded Context.
type valueCtx struct {
Context
key, val interface{}
}
// stringify tries a bit to stringify v, without using fmt, since we don't
// want context depending on the unicode tables. This is only used by
// *valueCtx.String().
func stringify(v interface{}) string {
switch s := v.(type) {
case stringer:
return s.String()
case string:
return s
}
return "<not Stringer>"
}
func (c *valueCtx) String() string {
return contextName(c.Context) + ".WithValue(type " +
reflect.TypeOf(c.key).String() +
", val " + stringify(c.val) + ")"
}
func (c *valueCtx) Value(key interface{}) interface{} {
if c.key == key {
return c.val
}
return c.Context.Value(key)
}
// ======================== 以上为 context.go 源码
// MyContext 自定义 context
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:45 下午 2021/12/2
type MyContext struct {
Context
}

@ -1,42 +0,0 @@
// Package demo ...
//
// Description : demo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-12-02 12:46 下午
package demo
import (
"fmt"
"reflect"
"testing"
"time"
)
// TestCancelCtx_UDC 测试自定义context
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:46 下午 2021/12/2
func TestCancelCtx_UDC(t *testing.T) {
childCancel := true
parentCtx, parentFunc := WithCancel(Background())
mctx := MyContext{parentCtx}
childCtx, childFun := WithCancel(mctx)
if childCancel {
childFun()
} else {
parentFunc()
}
fmt.Println("parent context => ", parentCtx, reflect.TypeOf(parentCtx).String())
fmt.Println("my context => ", mctx, reflect.TypeOf(mctx).String())
fmt.Println("child context => ", childCtx, reflect.TypeOf(childCtx).String())
// 防止主协程退出太快,子协程来不及打印
time.Sleep(5 * time.Second)
}

@ -1,6 +1,6 @@
// Package easylock ...
// Package easylock...
//
// Description : easylock ...
// Description : easylock...
//
// Author : go_developer@163.com<白茶清欢>
//

@ -11,7 +11,7 @@ type option struct {
flag string // 锁的标识
}
// OptionFunc 设置option选项
// Option 设置option选项
//
// Author : go_developer@163.com<白茶清欢>
//

@ -1,6 +1,6 @@
// Package easylock ...
// Package easylock...
//
// Description : easylock ...
// Description : easylock...
//
// Author : go_developer@163.com<白茶清欢>
//

@ -1,4 +1,4 @@
// Package easylock ...
// Package easylock...
//
// Description : 包装各种姿势的锁
//

@ -1,4 +1,4 @@
// Package easylock ...
// Package easylock...
//
// Description : 分段的锁
//

@ -1,82 +0,0 @@
// Package excel...
//
// Description : excel...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-19 12:25 下午
package excel
import (
"fmt"
"github.com/xuri/excelize/v2"
)
// NewExcel 获取excel实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:28 下午 2021/11/19
func NewExcel() *Create {
return &Create{
fileHandler: excelize.NewFile(),
}
}
// Create 创建excel
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:27 下午 2021/11/19
type Create struct {
// fileHandler excel文件处理句柄
fileHandler *excelize.File
}
// GenerateSheet 生成sheet
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:43 下午 2021/11/19
func (c *Create) GenerateSheet(sheetList []SheetData) error {
for _, sheet := range sheetList {
sheetIndex := c.fileHandler.NewSheet(sheet.Name)
if sheet.IsDefault {
// 设置活跃
c.fileHandler.SetActiveSheet(sheetIndex)
}
for lineIdx, colList := range sheet.Data {
for colIdx, col := range colList {
position := c.getColPosition(colIdx, lineIdx)
if err := c.fileHandler.SetCellValue(sheet.Name, position, col); nil != err {
return err
}
}
}
}
return nil
}
// Save 保存文件
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:04 下午 2021/11/19
func (c *Create) Save(fullFilePath string) error {
return c.fileHandler.SaveAs(fullFilePath)
}
// getColPosition 获取列名
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:14 下午 2021/11/19
func (c *Create) getColPosition(colIndex int, dataLineIndex int) string {
realIndex := colIndex % 26
first := colIndex / 26
if first == 0 {
return fmt.Sprintf("%s%d", WordMap[realIndex], dataLineIndex+1)
}
return fmt.Sprintf("%s%s%d", WordMap[first], WordMap[realIndex], dataLineIndex+1)
}

@ -1,30 +0,0 @@
// Package excel...
//
// Description : excel...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-19 2:05 下午
package excel
import (
"fmt"
"testing"
)
// TestCreate_GenerateSheet ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:05 下午 2021/11/19
func TestCreate_GenerateSheet(t *testing.T) {
e := NewExcel()
sheetList := []SheetData{
{true, "sheet11111", [][]interface{}{[]interface{}{123, 345, 678}}},
{false, "sheet2222", [][]interface{}{[]interface{}{123, 345, 678}}},
{false, "sheet3333", [][]interface{}{[]interface{}{123, 345, 678}}},
{false, "sheet44444", [][]interface{}{[]interface{}{123, 345, 678}}},
}
fmt.Println(e.GenerateSheet(sheetList))
fmt.Println(e.Save("./test.xlsx"))
}

@ -1,61 +0,0 @@
// Package excel...
//
// Description : excel...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-19 11:57 上午
package excel
// ReadResult ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:57 上午 2021/11/19
type ReadResult struct {
Error error // 异常信息
Result map[string][]map[string]interface{} // 查询结果 sheet => dataList
}
// SheetData ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:35 下午 2021/11/19
type SheetData struct {
IsDefault bool // 是否默认工作表
Name string // sheet 名称
Data [][]interface{} // 数据列表
}
var (
// WordMap 单元格序号映射
WordMap = map[int]string{
0: "A",
1: "B",
2: "C",
3: "D",
4: "E",
5: "F",
6: "G",
7: "H",
8: "I",
9: "J",
10: "K",
11: "L",
12: "M",
13: "N",
14: "O",
15: "P",
16: "Q",
17: "R",
18: "S",
19: "T",
20: "U",
21: "V",
22: "W",
23: "X",
24: "Y",
25: "Z",
}
)

@ -1,174 +0,0 @@
// Package excel...
//
// Description : excel...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-19 3:59 下午
package excel
import (
"github.com/xuri/excelize/v2"
)
// NewRead 获取读取实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:00 下午 2021/11/19
func NewRead(fullFilePath string, filePassword string) (*Read, error) {
var err error
read := &Read{}
if read.fileHandler, err = excelize.OpenFile(fullFilePath, excelize.Options{Password: filePassword}); nil != err {
return nil, err
}
return read, nil
}
// Read ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:03 下午 2021/11/19
type Read struct {
fileHandler *excelize.File
}
// GetAllSheetList ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:06 下午 2021/11/19
func (r *Read) GetAllSheetList() []string {
return r.fileHandler.GetSheetList()
}
// GetAllData 读取全部数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:07 下午 2021/11/19
func (r *Read) GetAllData() (map[string][][]string, error) {
var (
sheetList []string
result map[string][][]string
err error
)
sheetList = r.GetAllSheetList()
result = make(map[string][][]string)
for _, sheetName := range sheetList {
if result[sheetName], err = r.fileHandler.GetRows(sheetName); nil != err {
return nil, err
}
}
return result, nil
}
// GetAllDataToMap 读取全部数据,并返回map结构
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:20 下午 2021/11/19
func (r *Read) GetAllDataToMap(fieldList []string) (map[string][]map[string]string, error) {
var (
allData map[string][][]string
err error
formatResult map[string][]map[string]string
)
formatResult = make(map[string][]map[string]string)
if allData, err = r.GetAllData(); nil != err {
return nil, err
}
for sheetName, sheetData := range allData {
formatResult[sheetName] = make([]map[string]string, 0)
for _, lineData := range sheetData {
tmpResult := make(map[string]string)
for idx, colData := range lineData {
if idx >= len(fieldList) {
// 指定的字段列表较短, 自动剔除后面的字段
break
}
tmpResult[fieldList[idx]] = colData
}
// 字段列表较长, 单元格不足, 自动补齐空字符串
for i := len(lineData); i < len(fieldList); i++ {
tmpResult[fieldList[i]] = ""
}
formatResult[sheetName] = append(formatResult[sheetName], tmpResult)
}
}
// 格式化数据
return formatResult, nil
}
// ExtractAssignCol 抽取指定的列并返回list数据, 列的计数从 0 开始
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:53 下午 2021/11/19
func (r *Read) ExtractAssignCol(colIndexList []int) (map[string][][]string, error) {
var (
allData map[string][][]string
err error
formatResult map[string][][]string
)
if allData, err = r.GetAllData(); nil != err {
return nil, err
}
formatResult = make(map[string][][]string)
for sheetName, sheetData := range allData {
formatResult[sheetName] = make([][]string, 0)
for _, lineData := range sheetData {
tmpData := make([]string, 0)
for _, colIdx := range colIndexList {
if len(lineData) <= colIdx {
// 索引越界,默认空值
tmpData = append(tmpData, "")
continue
}
tmpData = append(tmpData, lineData[colIdx])
}
formatResult[sheetName] = append(formatResult[sheetName], tmpData)
}
}
return formatResult, nil
}
// ExtractAssignColToMap 抽取指定的列并返回map数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:53 下午 2021/11/19
func (r *Read) ExtractAssignColToMap(ruleTable map[int]string) (map[string][]map[string]string, error) {
var (
allData map[string][][]string
err error
formatResult map[string][]map[string]string
)
formatResult = make(map[string][]map[string]string)
if allData, err = r.GetAllData(); nil != err {
return nil, err
}
for sheetName, sheetData := range allData {
formatResult[sheetName] = make([]map[string]string, 0)
for _, lineData := range sheetData {
tmpResult := make(map[string]string)
for idx, colData := range lineData {
colName, exist := ruleTable[idx]
if !exist {
// 当前字段不需要提取
continue
}
tmpResult[colName] = colData
}
for _, colName := range ruleTable {
if _, exist := tmpResult[colName]; !exist {
tmpResult[colName] = ""
}
}
formatResult[sheetName] = append(formatResult[sheetName], tmpResult)
}
}
// 格式化数据
return formatResult, nil
}

@ -1,54 +0,0 @@
// Package excel...
//
// Description : excel...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-19 4:12 下午
package excel
import (
"fmt"
"testing"
)
// TestRead_GetAllData ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:12 下午 2021/11/19
func TestRead_GetAllData(t *testing.T) {
r, _ := NewRead("./test.xlsx", "")
fmt.Println(r.GetAllData())
}
// TestRead_GetAllDataToMap ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:12 下午 2021/11/19
func TestRead_GetAllDataToMap(t *testing.T) {
r, _ := NewRead("./test.xlsx", "")
fmt.Println(r.GetAllDataToMap([]string{"num1", "num2", "num3", "num4", "num5"}))
fmt.Println(r.GetAllDataToMap([]string{"num1", "num2"}))
}
// TestRead_ExtractAssignCol ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:12 下午 2021/11/19
func TestRead_ExtractAssignCol(t *testing.T) {
r, _ := NewRead("./test.xlsx", "")
fmt.Println(r.ExtractAssignCol([]int{0, 2, 8}))
}
// TestRead_ExtractAssignColToMap ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:12 下午 2021/11/19
func TestRead_ExtractAssignColToMap(t *testing.T) {
r, _ := NewRead("./test.xlsx", "")
fmt.Println(r.ExtractAssignColToMap(map[int]string{0: "num1", 2: "num3", 10: "num11"}))
}

@ -1,4 +1,4 @@
// Package api ...
// Package gin ...
//
// Description : 便捷的相关API处理
//
@ -23,7 +23,7 @@ type IApi interface {
GetURI() string
// GetMiddleWareList 使用的中间件列表
GetMiddleWareList() []gin.HandlerFunc
// GetHandler 处理的handler
// 处理的handler
GetHandler() gin.HandlerFunc
}

@ -19,36 +19,12 @@ import (
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:53 下午 2021/3/9
func InitRequest(startTimeField string, traceIDField string) gin.HandlerFunc {
func InitRequest() func(ctx *gin.Context) {
return func(ctx *gin.Context) {
// 设置请求开始时间
if len(startTimeField) == 0 {
startTimeField = "start_time"
}
if len(traceIDField) == 0 {
traceIDField = "trace_id"
}
ctx.Set(startTimeField, time.Now().UnixMilli())
ctx.Set("start_time", time.Now().Unix())
// 设置请求trace_id
ctx.Set(traceIDField, commonUtil.GetHostIP()+"-"+ctx.ClientIP()+"-"+time.Now().Format("20060102150405")+"-"+commonUtil.Md5(commonUtil.GenRandomString("", 16)))
ctx.Next()
}
}
// SupportCross 支持跨域
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:40 PM 2022/1/13
func SupportCross(header map[string]string) gin.HandlerFunc {
return func(ctx *gin.Context) {
ctx.Writer.Header().Set("Access-Control-Allow-Origin", "*")
ctx.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token, Admin-User-Token, admin-user-token")
ctx.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE") //服务器支持的所有跨域请求的方
ctx.Header("Access-Control-Expose-Headers", "admin-user-token, Admin-User-Token, Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type")
for key, val := range header {
ctx.Header(key, val)
}
ctx.Set("trace_id", time.Now().Format("20060102150405")+"+"+commonUtil.GetHostIP()+"-"+commonUtil.Md5(commonUtil.GenRandomString("", 16)))
ctx.Next()
}
}

@ -1,18 +0,0 @@
// Package static ...
//
// Description : static
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021/12/24 2:18 PM
package static
// MapRule 定义映射规则
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:18 PM 2021/12/24
type MapRule struct {
URIPrefix string `json:"uri_prefix"` // 路由前缀
StaticDirPath string `json:"static_dir_path"` // 静态资源路由
}

@ -1,25 +0,0 @@
// Package static ...
//
// Description : 启动静态资源服务器
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021/12/24 2:14 PM
package static
import (
"net/http"
"github.com/gin-gonic/gin"
)
// Register 静态资源服务器
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:17 PM 2021/12/24
func Register(router *gin.Engine, ruleList []*MapRule) {
for _, rule := range ruleList {
router.StaticFS(rule.URIPrefix, http.Dir(rule.StaticDirPath))
}
}

@ -27,7 +27,7 @@ func Response(ctx *gin.Context, code interface{}, message string, data interface
"message": message,
"data": data,
"trace_id": ctx.GetString("trace_id"),
"cost": time.Now().UnixMilli() - ctx.GetInt64("start_time"),
"cost": time.Since(time.Unix(ctx.GetInt64("start_time"), 0)).Seconds(),
}
ctx.JSON(http.StatusOK, responseData)
}

@ -1,17 +0,0 @@
// Package git_hook...
//
// Description : git_hook...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-11 6:41 下午
package git_hook
import "github.com/gin-gonic/gin"
// IGitHookEventHandler 接口约束, pkg 内部会提供基础实现
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:42 下午 2021/11/11
type IGitHookEventHandler func(ctx *gin.Context, hookData *HookData) *ResponseData

@ -1,165 +0,0 @@
// Package git_hook...
//
// Description : git_hook...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-11 6:23 下午
package git_hook
// HookData hook通知数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:23 下午 2021/11/11
type HookData struct {
Repository Repository `json:"repository"` // 仓库信息
Sender User `json:"sender"`
Ref string `json:"ref"` // 分支,如 : refs/heads/master
Before string `json:"before"` // 之前版本号 : e162757f3f4a37786b4118a5346baae5dd24ecde
After string `json:"after"` // 当前版本号 : c70a362d850a820704bd374363ea4e7ea810fd1a
HeadCommit Commit `json:"head_commit"` // 最近一次提交
CompareUrl string `json:"compare_url"` // 两个版本号代码diff的URL, 点击后可查看diff
Commits []Commit `json:"commits"` // 提交记录列表
Pusher User `json:"pusher"` // 推送人信息
}
// Repository 仓库信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:24 下午 2021/11/11
type Repository struct {
Mirror bool `json:"mirror"`
OriginalUrl string `json:"original_url"` // original_url
StarsCount int64 `json:"stars_count"` // start 数量
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` // 忽略空格的冲突
FullName string `json:"full_name"` // 仓库完整名称
Fork bool `json:"fork"` // 是否 fork
CreatedAt string `json:"created_at"` // 创建时间
Internal bool `json:"internal"` // 是否内部仓库
AllowRebaseExplicit bool `json:"allow_rebase_explicit"` // 是否允许rebase
MirrorInterval string `json:"mirror_interval"` // mirror_interval
CloneUrl string `json:"clone_url"` // gi clone 对用的 https 地址
WatchersCount int64 `json:"watchers_count"` // 监听器数量
OpenIssuesCount int64 `json:"open_issues_count"` // 未解决的issues数量
ReleaseCounter int64 `json:"release_counter"` // 发布版本数量
InternalTracker RepositoryInternalTracker `json:"internal_tracker"` // 内部跟踪配置
Name string `json:"name"` // 仓库名称
Empty bool `json:"empty"` // 仓库是否为空
SshUrl string `json:"ssh_url"` // git clone 对应的 ssh 地址
ForksCount int64 `json:"forks_count"` // fork 次数
Permissions RepositoryPermission `json:"permissions"` // 权限配置
Private bool `json:"private"` // 是否为私有仓库
Size int64 `json:"size"` // 仓库大小 KB
HasWiki bool `json:"has_wiki"` // 是否有 wiki
HasProjects bool `json:"has_projects"` // 是否有项目
AllowMergeCommits bool `json:"allow_merge_commits"` // 是否允许合并提交
AvatarUrl string `json:"avatar_url"` // 头像地址
ID int64 `json:"id"` // 仓库ID
Description string `json:"description"` // 创建仓库时候的描述
Website string `json:"website"` // 网站
UpdatedAt string `json:"updated_at"` // 更新时间
HasPullRequests bool `json:"has_pull_requests"` // 是否有 pull request
Parent map[string]interface{} `json:"parent"` // parent
OpenPrCounter int64 `json:"open_pr_counter"` // 打开的pr数量
HasIssues bool `json:"has_issues"` // 是否有issues
AllowRebase bool `json:"allow_rebase"` // 是否允许rebase
AllowSquashMerge bool `json:"allow_squash_merge"` // 是否允许 合并 merge (将分支内容合并成一次提交, 合入主干)
Owner User `json:"owner"` // 仓库所有者信息
Template bool `json:"template"` // 是否是模版
HtmlUrl string `json:"html_url"` // 访问仓库的html地址
DefaultBranch string `json:"default_branch"` // 默认分支
Archived bool `json:"archived"` // 是否已归档
DefaultMergeStyle string `json:"default_merge_style"` // 默认的合并方式
}
// RepositoryInternalTracker 内部跟踪器
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:28 下午 2021/11/11
type RepositoryInternalTracker struct {
EnableTimeTracker bool `json:"enable_time_tracker"` // 启用time_tracker
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"` // 仅有代码贡献者允许
EnableIssueDependencies bool `json:"enable_issue_dependencies"` // 启用 issue_dependencies
}
// RepositoryPermission 仓库权限
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:29 下午 2021/11/11
type RepositoryPermission struct {
Pull bool `json:"pull"` // 是否允许 pull 代码
Admin bool `json:"admin"` // 是否是管理员
Push bool `json:"push"` // 是否允许push代码
}
// User 用户信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:30 下午 2021/11/11
type User struct {
IsAdmin bool `json:"is_admin"` // 是否是管理员
Website string `json:"website"` // 网页
Email string `json:"email"` // 邮箱
AvatarUrl string `json:"avatar_url"` // 头像
ProhibitLogin bool `json:"prohibit_login"` // 禁止登录
Description string `json:"description"` // 描述
FollowingCount int64 `json:"following_count"` // follower数量
StarredReposCount int64 `json:"starred_repos_count"` // star仓库数量
Username string `json:"username"` // 用户名
Login string `json:"login"` // 登录
Created string `json:"created"` // 创建时间
Visibility string `json:"visibility"` // 是否可见
FullName string `json:"full_name"` // 全名
LastLogin string `json:"last_login"` // 最近登录时间
Restricted bool `json:"restricted"` // 是否受限
Active bool `json:"active"` // 是否活跃
Location string `json:"location"` // 位置
FollowersCount int64 `json:"followers_count"` // follower数量
ID int64 `json:"id"` // id
Language string `json:"language"` // 语言
}
// Commit 提交记录信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:25 下午 2021/11/11
type Commit struct {
Message string `json:"message"` // 提交时带的message
Url string `json:"url"` // 浏览此版本代码的地址
Author CommitAuthor `json:"author"` // 作者信息
Verification map[string]interface{} `json:"verification"` // 验证
Timestamp string `json:"timestamp"` // 时间
ID string `json:"id"` // 提交的版本号 98ccd35d19d17c8e624a492cc5811fe6f381ca09
Committer CommitAuthor `json:"committer"` // 提交人信息
Added []string `json:"added"` // 增加的文件列表
Removed []string `json:"removed"` // 移除文件列表
Modified []string `json:"modified"` // 修改的文件列表
}
// CommitAuthor 提交人的信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:31 下午 2021/11/11
type CommitAuthor struct {
Name string `json:"name"` // 昵称
Email string `json:"email"` // 邮箱
Username string `json:"username"` // 用户名
}
// ResponseData 处理结果的返回值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:37 上午 2021/11/12
type ResponseData struct {
Code interface{} `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}

@ -1,45 +0,0 @@
// Package git_hook...
//
// Description : git_hook...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-11 6:39 下午
package git_hook
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/pkg/errors"
)
// RegisterGitHookRouter 注册 git hook 回调的路由
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:40 下午 2021/11/11
func RegisterGitHookRouter(router *gin.Engine, handler IGitHookEventHandler) error {
if nil == handler {
return errors.New("handler is nil")
}
router.POST("/git/hook/notice", func(ctx *gin.Context) {
var (
hookData HookData
err error
)
if err = ctx.ShouldBindJSON(&hookData); nil != err {
ctx.JSON(http.StatusBadRequest, gin.H{"code": -1, "message": err.Error()})
}
responseData := handler(ctx, &hookData)
if nil == responseData {
responseData = &ResponseData{
Code: 100,
Message: "处理完成(未设置返回值,系统默认返回此信息)",
Data: map[string]interface{}{},
}
}
ctx.JSON(http.StatusOK, responseData)
})
return nil
}

113
go.mod

@ -2,113 +2,72 @@ module git.zhangdeman.cn/zhangdeman/gopkg
go 1.17
replace github.com/coreos/bbolt v1.3.4 => go.etcd.io/bbolt v1.3.4
replace google.golang.org/grpc => google.golang.org/grpc v1.26.0
require (
github.com/Shopify/sarama v1.21.0
github.com/apolloconfig/agollo/v4 v4.0.9
github.com/coreos/etcd v3.3.27+incompatible
github.com/Shopify/sarama v1.29.1
github.com/ddliu/go-httpclient v0.6.9
github.com/gin-gonic/gin v1.7.7
github.com/go-redis/redis/v8 v8.11.4
github.com/go-redis/redis_rate/v9 v9.1.2
github.com/gin-gonic/gin v1.7.4
github.com/go-redis/redis/v8 v8.11.3
github.com/go-redis/redis_rate/v9 v9.1.1
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
github.com/pkg/errors v0.9.1
github.com/shirou/gopsutil v3.21.10+incompatible
github.com/shirou/gopsutil v3.21.9+incompatible
github.com/spaolacci/murmur3 v1.1.0
github.com/stretchr/testify v1.7.0
github.com/tidwall/gjson v1.11.0
github.com/xuri/excelize/v2 v2.4.1
github.com/tidwall/gjson v1.9.0
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
go.etcd.io/etcd v3.3.27+incompatible
go.uber.org/zap v1.20.0
go.uber.org/zap v1.19.1
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/yaml.v2 v2.4.0
gorm.io/driver/mysql v1.2.0
gorm.io/gorm v1.22.4
gorm.io/driver/mysql v1.1.2
gorm.io/gorm v1.21.15
)
require (
github.com/DataDog/zstd v1.3.5 // indirect
github.com/StackExchange/wmi v1.2.1 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/coreos/bbolt v1.3.4 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect
github.com/cespare/xxhash/v2 v2.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/eapache/go-resiliency v1.2.0 // indirect
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 // indirect
github.com/eapache/queue v1.1.0 // indirect
github.com/frankban/quicktest v1.14.0 // indirect
github.com/fsnotify/fsnotify v1.5.1 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-ole/go-ole v1.2.5 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.10.1 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/go-sql-driver/mysql v1.6.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.0-20210608040537-544b4180ac70 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/golang/snappy v0.0.3 // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.4 // indirect
github.com/jinzhu/now v1.1.2 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/klauspost/compress v1.12.2 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/lestrrat-go/strftime v1.0.5 // indirect
github.com/magiconair/properties v1.8.5 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/mitchellh/mapstructure v1.4.2 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/pelletier/go-toml v1.9.4 // indirect
github.com/pierrec/lz4 v2.6.1+incompatible // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/pierrec/lz4 v2.6.0+incompatible // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.11.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/richardlehane/mscfb v1.0.3 // indirect
github.com/richardlehane/msoleps v1.0.1 // indirect
github.com/soheilhy/cmux v0.1.5 // indirect
github.com/spf13/afero v1.6.0 // indirect
github.com/spf13/cast v1.4.1 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.8.1 // indirect
github.com/subosito/gotenv v1.2.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/tidwall/match v1.0.3 // indirect
github.com/tidwall/pretty v1.1.0 // indirect
github.com/tklauser/go-sysconf v0.3.9 // indirect
github.com/tklauser/numcpus v0.3.0 // indirect
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 // indirect
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.7.0 // indirect
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 // indirect
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b // indirect
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 // indirect
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 // indirect
google.golang.org/grpc v1.40.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e // indirect
golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 // indirect
google.golang.org/protobuf v1.26.0 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/ini.v1 v1.64.0 // indirect
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)

760
go.sum

@ -1,851 +1,271 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/zstd v1.3.5 h1:DtpNbljikUepEPD16hD4LvIcmhnhdLTiW/5pHgbmp14=
github.com/DataDog/zstd v1.3.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Shopify/sarama v1.21.0 h1:0GKs+e8mn1RRUzfg9oUXv3v7ZieQLmOZF/bfnmmGhM8=
github.com/Shopify/sarama v1.21.0/go.mod h1:yuqtN/pe8cXRWG5zPaO7hCfNJp5MwmkoJEoLjkm5tCQ=
github.com/Shopify/sarama v1.29.1 h1:wBAacXbYVLmWieEA/0X/JagDdCZ8NVFOfS6l6+2u5S0=
github.com/Shopify/sarama v1.29.1/go.mod h1:mdtqvCSg8JOxk8PmpTNGyo6wzd4BMm4QXSfDnTXmgkE=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA=
github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/apolloconfig/agollo/v4 v4.0.9 h1:4YzFSOVTZjGU2ag0XxL0y311TAkEsqh9a5pBS8FOgaI=
github.com/apolloconfig/agollo/v4 v4.0.9/go.mod h1:n/7qxpKOTbegygLmO5OKmFWCdy3T+S/zioBGlo457Dk=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/coreos/etcd v3.3.27+incompatible h1:QIudLb9KeBsE5zyYxd1mjzRSkzLg9Wf9QlRwFgd6oTA=
github.com/coreos/etcd v3.3.27+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM=
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/ddliu/go-httpclient v0.6.9 h1:/3hsBVpcgCJwqm1dkVlnAJ9NWuYInbRc+i9FyUXX/LE=
github.com/ddliu/go-httpclient v0.6.9/go.mod h1:zM9P0OxV4OGGz1pt/ibuj0ooX2SWH9a6MvXZLbT0JMc=
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.11.3 h1:8sXhOn0uLys67V8EsXLc6eszDs8VXWxL3iRvebPhedY=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.1 h1:mZcQUHVQUQWoPXXtuf9yuEXKudkV2sx1E06UadKWpgI=
github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM=
github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY=
github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho=
github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.10.1 h1:uA0+amWMiglNZKZ9FJRKUAe9U3RX91eVn1JYXMWt7ig=
github.com/go-playground/validator/v10 v10.10.1/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
github.com/go-redis/redis/v8 v8.11.4 h1:kHoYkfZP6+pe04aFTnhDH6GDROa5yJdHJVNxV3F46Tg=
github.com/go-redis/redis/v8 v8.11.4/go.mod h1:2Z2wHZXdQpCDXEGzqMockDpNyYvi2l4Pxt6RJr792+w=
github.com/go-redis/redis_rate/v9 v9.1.2 h1:H0l5VzoAtOE6ydd38j8MCq3ABlGLnvvbA1xDSVVCHgQ=
github.com/go-redis/redis_rate/v9 v9.1.2/go.mod h1:oam2de2apSgRG8aJzwJddXbNu91Iyz1m8IKJE2vpvlQ=
github.com/go-redis/redis/v8 v8.3.4/go.mod h1:jszGxBCez8QA1HWSmQxJO9Y82kNibbUmeYhKWrBejTU=
github.com/go-redis/redis/v8 v8.11.3 h1:GCjoYp8c+yQTJfc0n69iwSiHjvuAdruxl7elnZCxgt8=
github.com/go-redis/redis/v8 v8.11.3/go.mod h1:xNJ9xDG09FsIPwh3bWdk+0oDWHbtF9rPN0F/oD9XeKc=
github.com/go-redis/redis_rate/v9 v9.1.1 h1:7SIrbnhQ7zsTNEgIvprFhJf7/+l3wSpZc2iRVwUmaq8=
github.com/go-redis/redis_rate/v9 v9.1.1/go.mod h1:jjU9YxOSZ3cz0yj1QJVAJiy5ueKmL9o4AySJHcKyTSE=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.0-20210608040537-544b4180ac70 h1:QvkCapbHb5N1sQMkoQkCLOcBmss196N/m2F64fCA7mY=
github.com/golang/snappy v0.0.0-20210608040537-544b4180ac70/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.2 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI=
github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.3/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/klauspost/compress v1.12.2 h1:2KCfW3I9M7nSc5wOqXAlW2v2U6v+w6cbjvbfp+OykW8=
github.com/klauspost/compress v1.12.2/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
github.com/lestrrat-go/strftime v1.0.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE=
github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.2 h1:6h7AQ0yhTcIsmFmnAwQls75jp2Gzs4iB8W7pjMO+rqo=
github.com/mitchellh/mapstructure v1.4.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pierrec/lz4 v2.6.1+incompatible h1:9UY3+iC23yxF0UfGaYrGplQ+79Rg+h/q9FV9ix19jjM=
github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU=
github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0=
github.com/pierrec/lz4 v2.6.0+incompatible h1:Ix9yFKn1nSPBLFl/yZknTp8TU5G4Ps0JDmguYK6iH1A=
github.com/pierrec/lz4 v2.6.0+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI=
github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shirou/gopsutil v3.21.10+incompatible h1:AL2kpVykjkqeN+MFe1WcwSBVUjGjvdU8/ubvCuXAjrU=
github.com/shirou/gopsutil v3.21.10+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0=
github.com/shirou/gopsutil v3.21.9+incompatible h1:LTLpUnfX81MkHeCtSrwNKZwuW5Id6kCa7/P43NdcNn4=
github.com/shirou/gopsutil v3.21.9+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY=
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.4.1 h1:s0hze+J0196ZfEMTs80N7UlFt0BDuQ7Q+JDnHiMWKdA=
github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44=
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=
github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
github.com/tidwall/gjson v1.11.0 h1:C16pk7tQNiH6VlCrtIXL1w8GaOsi1X3W8KDkE1BuYd4=
github.com/tidwall/gjson v1.11.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/gjson v1.9.0 h1:+Od7AE26jAaMgVC31cQV/Ope5iKXulNMflrlB7k+F9E=
github.com/tidwall/gjson v1.9.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=
github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tklauser/go-sysconf v0.3.9 h1:JeUVdAOWhhxVcU6Eqr/ATFHgXk/mmiItdKeJPev3vTo=
github.com/tklauser/go-sysconf v0.3.9/go.mod h1:11DU/5sG7UexIrp/O6g35hrWzu0JxlwQ3LSFUzyeuhs=
github.com/tklauser/numcpus v0.3.0 h1:ILuRUQBtssgnxw0XXIjKUC56fgnOrFoQQ/4+DeU2biQ=
github.com/tklauser/numcpus v0.3.0/go.mod h1:yFGUr7TUHQRAhyqBcEg0Ge34zDBAsIvJJcyE6boqnA8=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA=
github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo=
github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=
github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o=
github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
github.com/xuri/excelize/v2 v2.4.1 h1:veeeFLAJwsNEBPBlDepzPIYS1eLyBVcXNZUW79exZ1E=
github.com/xuri/excelize/v2 v2.4.1/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A=
github.com/xdg/scram v1.0.3/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.3/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ=
github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.4 h1:hi1bXHMVrlQh6WwxAy+qZCV/SYIlqo+Ushwdpa4tAKg=
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/etcd v3.3.27+incompatible h1:5hMrpf6REqTHV2LW2OclNpRtxI0k9ZplMemJsMSWju0=
go.etcd.io/etcd v3.3.27+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723 h1:sHOAIxRGBp443oHZIPB+HsUGaksVCXVQENPxwTfQdH4=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0 h1:zaiO/rmgFjbmCXdSYJWQcdvOCsthmdaHfr3Gm2Kx4Ec=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.20.0 h1:N4oPlghZwYG55MlU6LXk/Zp00FVNE9X9wrYO8CEs4lc=
go.uber.org/zap v1.20.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
go.uber.org/zap v1.19.1 h1:ue41HOKd1vGURxrmeKIgELGb3jPW9DMUDGtsinblHwI=
go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b h1:vI32FkLJNAWtGD4BwkThwEy6XS7ZLLMHkSkYfF8M0W0=
golang.org/x/net v0.0.0-20220403103023-749bd193bc2b/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71 h1:ikCpsnYR+Ew0vu99XlDp55lGgDJdIMx3f4a18jfse/s=
golang.org/x/sys v0.0.0-20210816074244-15123e1e1f71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb h1:PVGECzEo9Y3uOidtkHGdd347NjLtITfJFO9BxFpmRoo=
golang.org/x/sys v0.0.0-20220403205710-6acee93ad0eb/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 h1:GZokNIeuVkl3aZHJchRrr13WCsols02MLUcz1U9is6M=
golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71 h1:z+ErRPu0+KS02Td3fOAgdX+lnPDh/VyaABEJPD4JRQs=
google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
google.golang.org/grpc v1.26.0 h1:2dTRdpdFEEhJYQD8EMLB61nnrzSCTbG38PhqdhvOltg=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.64.0 h1:Mj2zXEXcNb5joEiSA0zc3HZpTst/iyjNiR4CN8tDzOg=
gopkg.in/ini.v1 v1.64.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
@ -853,20 +273,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.2.0 h1:l8+9VwjjyzEkw0PNPBOr2JHhLOGVk7XEnl5hk42bcvs=
gorm.io/driver/mysql v1.2.0/go.mod h1:4RQmTg4okPghdt+kbe6e1bTXIQp7Ny1NnBn/3Z6ghjk=
gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.22.4 h1:8aPcyEJhY0MAt8aY6Dc524Pn+pO29K+ydu+e/cXSpQM=
gorm.io/gorm v1.22.4/go.mod h1:1aeVC+pe9ZmvKZban/gW4QPra7PRoTEssyc922qCAkk=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M=
gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM=
gorm.io/gorm v1.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk=
gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=

@ -20,37 +20,26 @@ import (
"git.zhangdeman.cn/zhangdeman/gopkg/util"
)
// MapDataRule 数据映射结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:09 PM 2022/1/17
type MapDataRule struct {
MapKey string
DefaultValue string
IsComplexType bool
}
// NewFilter 获取解析的实例
// NewParseJSONTree 获取解析的实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:43 下午 2021/3/14
func NewFilter(data interface{}, rule map[string]MapDataRule) *Filter {
func NewFilter(data interface{}, rule map[string]string) *Filter {
return &Filter{
data: data,
rule: rule,
}
}
// Filter 解析json树
// ParseJSONTree 解析json树
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:41 下午 2021/3/14
type Filter struct {
data interface{}
rule map[string]MapDataRule
rule map[string]string
}
// Result 数据过滤结果
@ -65,12 +54,12 @@ func (f *Filter) Result() (*DynamicJSON, error) {
result := NewDynamicJSON()
byteData, _ := json.Marshal(f.data)
source := string(byteData)
for extraDataPath, newDataRule := range f.rule {
for extraDataPath, newDataPath := range f.rule {
// 为数组的处理
pathArr := strings.Split(extraDataPath, ".[].")
val := gjson.Get(source, pathArr[0])
if len(pathArr) == 1 {
f.SetValue(result, newDataRule.MapKey, val.Value(), false, 0)
f.SetValue(result, newDataPath, val.Value(), false, 0)
continue
}
// 支持list再抽取一层,处于性能考虑,这么限制,不做递归无限深度处理
@ -80,11 +69,9 @@ func (f *Filter) Result() (*DynamicJSON, error) {
data := item.Map()
for _, key := range ketList {
if v, exist := data[key]; exist {
result.SetValue(strings.ReplaceAll(newDataRule.MapKey, "[]", fmt.Sprintf("[%d]", idx)), data[key].Value(), v.IsObject() || v.IsArray())
} else {
// 结果集中不存在对应key,设置默认值
result.SetValue(strings.ReplaceAll(newDataRule.MapKey, "[]", fmt.Sprintf("[%d]", idx)), newDataRule.DefaultValue, newDataRule.IsComplexType)
result.SetValue(strings.ReplaceAll(newDataPath, "[]", fmt.Sprintf("[%d]", idx)), data[key].Raw, v.IsObject() || v.IsArray())
}
// 结果集中不存在对应key,丢弃
}
}
}

@ -1,175 +0,0 @@
// Package json_tool...
//
// Description : json_tool...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-03-10 11:44 下午
package json_tool
import (
"encoding/json"
"fmt"
"testing"
)
// TestJSON ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:58 下午 2021/3/14
func TestJSON(t *testing.T) {
tree := NewDynamicJSON()
fmt.Println(tree.extraSliceIndex("[200]"))
tree.SetValue("extra.height.value", 180, false)
tree.SetValue("extra.height.unit.use", "cm", false)
tree.SetValue("extra.height.unit.open", "1", false)
tree.SetValue("name", "zhangdeman", false)
tree.SetValue("good.name", "good", false)
tree.SetValue("work", "111", false)
tree.SetValue("good.price", "180", false)
tree.SetValue("good.unit", "$", false)
tree.SetValue("slice.[0].name", "zhang", false)
tree.SetValue("slice.[0].age", 30, false)
tree.SetValue("slice.[1].name", "de", false)
tree.SetValue("slice.[2].name", "man", false)
tree.SetValue("slice.[3]", "zhangdeman", false)
fmt.Println(tree.String())
tree = NewDynamicJSON()
tree.SetValue("[0]", "zhang", false)
tree.SetValue("[1]", "de", false)
tree.SetValue("[2]", "man", false)
fmt.Println(tree.String())
}
// TestType 判断数据类型断言
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:59 下午 2021/3/14
func TestType(t *testing.T) {
}
// TestSelect 测试动态选择字段
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:47 下午 2021/4/13
func TestSelect(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
},
"slice": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "b": 2, "c": 4},
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1},
{"name": "bob", "age": 28, "number": 2},
{"name": "bob", "age": 28, "number": 2, "list": []int{1, 2, 3}},
},
}
rule := map[string]MapDataRule{
"name": {
MapKey: "user_name",
DefaultValue: "用户姓名默认值",
IsComplexType: false,
},
"extra.age": {MapKey: "user_age", DefaultValue: "用户年龄默认值", IsComplexType: false},
"extra.height": {MapKey: "user_height", DefaultValue: "扩展高度默认值", IsComplexType: false},
"table.[].name": {MapKey: "slice.[].name_modify", DefaultValue: "列表姓名默认值", IsComplexType: false},
"table.[].list": {MapKey: "slice.[].data_list", DefaultValue: "[\"567\",\"678\",\"789\"]", IsComplexType: true},
}
filter := NewFilter(source, rule)
d, e := filter.Result()
if nil != e {
fmt.Println(e)
return
}
fmt.Println(d.String())
}
// TestParse 测试获取JSON数据结构
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:59 PM 2022/1/9
func TestParse(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
"obj": map[string]interface{}{
"la": "aaaa",
},
},
"slice": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "b": 2, "c": 4},
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1, "obj": map[string]interface{}{"enen": "en"}},
{"name": "bob", "age": 28, "number": 2},
},
"two_slice": []map[string]interface{}{
{
"students": []map[string]interface{}{
{
"name": "enen",
"age": 18,
"score": []float64{1, 2, 3, 45},
},
},
"other": []interface{}{"others"},
"read_only": 1,
},
},
}
byteData, _ := json.Marshal(source)
fmt.Println(GetJSONDataStruct(string(byteData)))
}
// TestParseWithType 测试获取JSON数据结构
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:59 PM 2022/1/9
func TestParseWithType(t *testing.T) {
source := map[string]interface{}{
"name": "zhangdeman",
"extra": map[string]interface{}{
"age": 18,
"height": 180,
"slice": []int{1, 2, 3},
"obj": map[string]interface{}{
"la": "aaaa",
},
},
"slice": []int{1, 2, 3},
"map": map[string]interface{}{"a": 1, "d": 5.5, "e": "qqq"},
"empty_obj": map[string]interface{}{},
"empty_list": make([]interface{}, 0),
"table": []map[string]interface{}{
{"name": "alex", "age": 18, "number": 1, "obj": map[string]interface{}{"enen": "en"}},
{"name": "bob", "age": 28, "number": 2},
},
"two_slice": []map[string]interface{}{
{
"students": []map[string]interface{}{
{
"name": "enen",
"age": 18,
"score": []float64{1, 2, 3, 45},
},
},
"other": []interface{}{"others"},
"read_only": 1,
},
},
}
byteData, _ := json.Marshal(source)
fmt.Println(GetJSONDataStructWithType(string(byteData)))
}

@ -1,217 +0,0 @@
// Package json_tool ...
//
// Description : json_tool ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022-01-09 10:48 PM
package json_tool
import (
"strings"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
)
// Field ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022/1/10 10:47 PM
type Field struct {
Path string `json:"path"` // 路径
Type string `json:"type"` // 类型
Example string `json:"example"` // 示例值
}
const (
// FieldTypeInt ...
FieldTypeInt = "int64"
// FieldTypeIntSlice ...
FieldTypeIntSlice = "[]int64"
// FieldTypeFloat ...
FieldTypeFloat = "float64"
// FieldTypeFloatSlice ...
FieldTypeFloatSlice = "[]float64"
// FieldTypeBool ...
FieldTypeBool = "bool"
// FieldTypeBoolSlice ...
FieldTypeBoolSlice = "[]bool"
// FieldTypeString ...
FieldTypeString = "string"
// FieldTypeStringSLice ...
FieldTypeStringSLice = "string"
// FieldTypeAny ...
FieldTypeAny = "interface{}"
// FieldTypeAnySlice ...
FieldTypeAnySlice = "[]interface{}"
// FieldTypeSlice ...
FieldTypeSlice = "[]interface{}"
// FieldTypeMap ...
FieldTypeMap = "map"
// FieldTypeMapSlice ...
FieldTypeMapSlice = "[]map"
)
// GetJSONDataStruct 获取JSON数据的结构
//
// Author : go_developer@163.com<张德满>
//
// Date : 10:53 PM 2022/1/9
func GetJSONDataStruct(data string) ([]string, error) {
if !gjson.Valid(data) {
return make([]string, 0), errors.New("JSON format is invalid")
}
pathList := make([]string, 0)
r := gjson.Parse(data)
r.ForEach(func(key, value gjson.Result) bool {
if value.Value() == nil {
pathList = append(pathList, key.String())
return true
}
if value.IsObject() {
if value.String() == "{}" {
pathList = append(pathList, key.String())
} else {
list, _ := GetJSONDataStruct(value.String())
for _, k := range list {
pathList = append(pathList, key.String()+"."+k)
}
}
}
if value.IsArray() {
dataList := value.Array()
if len(dataList) > 0 {
if !dataList[0].IsObject() && !dataList[0].IsArray() {
pathList = append(pathList, key.String())
} else {
list, _ := GetJSONDataStruct(dataList[0].String())
for _, k := range list {
pathList = append(pathList, key.String()+".[]."+k)
}
}
} else {
pathList = append(pathList, key.String())
}
}
if !value.IsObject() && !value.IsArray() {
pathList = append(pathList, key.String())
}
return true
})
return pathList, nil
}
// GetJSONDataStructWithType 获取数据结构,并获取类型
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022/1/10 10:47 PM
func GetJSONDataStructWithType(data string) ([]Field, error) {
if !gjson.Valid(data) {
return make([]Field, 0), errors.New("JSON format is invalid")
}
pathList := make([]Field, 0)
r := gjson.Parse(data)
r.ForEach(func(key, value gjson.Result) bool {
if value.Value() == nil {
pathList = append(pathList, Field{
Path: key.String(),
Type: FieldTypeAny,
Example: "nil",
})
return true
}
if value.IsObject() {
if value.String() == "{}" {
pathList = append(pathList, Field{
Path: key.String(),
Type: FieldTypeMap,
Example: "{}",
})
} else {
list, _ := GetJSONDataStructWithType(value.String())
for _, field := range list {
pathList = append(pathList, Field{
Path: key.String() + "." + field.Path,
Type: field.Type,
Example: field.Example,
})
}
}
}
if value.IsArray() {
dataList := value.Array()
if len(dataList) > 0 {
if !dataList[0].IsObject() && !dataList[0].IsArray() {
pathList = append(pathList, Field{
Path: key.String(),
Type: "[]" + GetDataType(dataList[0]),
Example: value.String(),
})
} else {
list, _ := GetJSONDataStructWithType(dataList[0].String())
for _, field := range list {
pathList = append(pathList, Field{
Path: key.String() + ".[]." + field.Path,
Type: field.Type,
Example: field.Example,
})
}
}
} else {
pathList = append(pathList, Field{
Path: key.String(),
Type: FieldTypeSlice,
Example: "[]",
})
}
}
if !value.IsObject() && !value.IsArray() {
pathList = append(pathList, Field{
Path: key.String(),
Type: GetDataType(value),
Example: value.String(),
})
}
return true
})
return pathList, nil
}
// GetDataType 获取数据类型
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022/1/10 11:00 PM
func GetDataType(value gjson.Result) string {
switch value.Type.String() {
default:
return FieldTypeAny
case "Null":
return FieldTypeAny
case "False":
return FieldTypeBool
case "True":
return FieldTypeBool
case "Number":
if strings.Contains(value.String(), ".") {
return FieldTypeFloat
}
return FieldTypeInt
case "String":
return FieldTypeString
case "JSON":
if strings.HasPrefix(strings.TrimSpace(value.String()), "[") {
return FieldTypeSlice
}
return FieldTypeMap
}
}

@ -4,17 +4,14 @@
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021/03/01 9:52 下午
// Date : 2021-03-01 9:52 下午
package wrapper
import (
"context"
"fmt"
"strings"
"time"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"go.uber.org/zap/zapcore"
@ -50,36 +47,15 @@ func NewGormV2(loggerLevel zapcore.Level, consoleOutput bool, encoder zapcore.En
}, nil
}
// NewGormLoggerWithInstance 获取gorm日志实现
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:36 PM 2021/12/24
func NewGormLoggerWithInstance(ctx *gin.Context, dbClient *gorm.DB, instance *zap.Logger, node string, extraCtxFieldList []string) logger.Interface {
return &Gorm{
dbClient: dbClient,
instance: instance,
traceIDField: "",
extraCtxFieldList: extraCtxFieldList,
flag: "",
node: node,
ctx: ctx,
}
}
// Gorm v2 版本库日志实现
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:55 下午 2021/3/1
type Gorm struct {
dbClient *gorm.DB
instance *zap.Logger // 日志实例
traceIDField string // 串联请求上下文的的ID
extraCtxFieldList []string // 从请求上线问提取的字段
flag string // 数据库标识
node string // 数据库节点 master / slave
ctx *gin.Context // gin上下文
instance *zap.Logger // 日志实例
traceIDField string // 串联请求上下文的的ID
flag string // 数据库标识
}
// LogMode ...
@ -97,7 +73,12 @@ func (g *Gorm) LogMode(level logger.LogLevel) logger.Interface {
//
// Date : 10:18 下午 2021/3/1
func (g *Gorm) Info(ctx context.Context, s string, i ...interface{}) {
g.write(nil, "info")
g.instance.Info(
"Info日志",
zap.String(g.traceIDField, g.getTraceID(ctx)),
zap.String("db_flag", g.flag),
zap.String("message", fmt.Sprintf(s, i...)),
)
}
// Warn ...
@ -106,7 +87,12 @@ func (g *Gorm) Info(ctx context.Context, s string, i ...interface{}) {
//
// Date : 10:16 下午 2021/3/1
func (g *Gorm) Warn(ctx context.Context, s string, i ...interface{}) {
g.write(nil, "warn")
g.instance.Warn(
"SQL执行产生Warning",
zap.String(g.traceIDField, g.getTraceID(ctx)),
zap.String("db_flag", g.flag),
zap.String("message", fmt.Sprintf(s, i...)),
)
}
// Error 日志
@ -115,7 +101,12 @@ func (g *Gorm) Warn(ctx context.Context, s string, i ...interface{}) {
//
// Date : 10:18 下午 2021/3/1
func (g *Gorm) Error(ctx context.Context, s string, i ...interface{}) {
g.write(nil, "error")
g.instance.Warn(
"SQL执行产生Error",
zap.String(g.traceIDField, g.getTraceID(ctx)),
zap.String("db_flag", g.flag),
zap.String("message", fmt.Sprintf(s, i...)),
)
}
// Trace Trace 记录
@ -132,7 +123,8 @@ func (g *Gorm) Trace(ctx context.Context, begin time.Time, fc func() (string, in
sql, affectRows = fc()
}
dataList := []zap.Field{
g.instance.Info(
"SQL执行记录",
zap.String(g.traceIDField, g.getTraceID(ctx)),
zap.String("db_flag", g.flag),
zap.Int64("begin_time", start),
@ -141,45 +133,7 @@ func (g *Gorm) Trace(ctx context.Context, begin time.Time, fc func() (string, in
zap.String("sql", sql),
zap.Int64("affect_rows", affectRows),
zap.Error(err),
}
g.write(dataList, "info")
}
// write ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:11 PM 2021/12/24
func (g *Gorm) write(dataList []zap.Field, level string) {
if nil == g.instance {
// 未设置日志实例
return
}
if nil == dataList {
dataList = make([]zap.Field, 0)
}
if nil != g.ctx {
for _, extraField := range g.extraCtxFieldList {
dataList = append(dataList, zap.Any(extraField, g.ctx.Value(extraField)))
}
}
// 补齐 flag、node
sql := g.dbClient.Dialector.Explain(g.dbClient.Statement.SQL.String(), g.dbClient.Statement.Vars...)
affectRows := g.dbClient.RowsAffected
dataList = append(dataList, zap.String("db_node", g.node), zap.String("db_flag", g.flag), zap.String("execute_sql", sql), zap.Int64("affect_rows", affectRows))
message := "SQL执行记录"
switch strings.ToLower(level) {
case "info":
g.instance.Info(message, dataList...)
case "warn":
g.instance.Warn(message, dataList...)
case "error":
g.instance.Error(message, dataList...)
default:
g.instance.Info(message, dataList...)
}
)
}
// getTraceID 获取traceID
@ -191,7 +145,7 @@ func (g *Gorm) getTraceID(ctx context.Context) string {
return fmt.Sprintf("%v", ctx.Value(g.traceIDField))
}
// GetGormSQL 获取trace fn
// GetGormSQL 获取tracefn
//
// Author : go_developer@163.com<白茶清欢>
//

@ -1,28 +0,0 @@
// Package apollo...
//
// Description : 扩展第三方库的一些函数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 10:16 下午
package apollo
import (
"git.zhangdeman.cn/zhangdeman/gopkg/util"
"github.com/pkg/errors"
)
// GetValueWithReceiver 读取数据,并解析到指定的数据结构中
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:18 下午 2021/11/24
func GetValueWithReceiver(key string, receiver interface{}) error {
var (
result string
)
if result = Client.GetStringValue(key, ""); len(result) == 0 {
return errors.New("key is not found : " + key)
}
return util.JSONUnmarshalWithNumber([]byte(result), receiver)
}

@ -1,36 +0,0 @@
// Package apollo...
//
// Description : apollo...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 10:05 下午
package apollo
import (
"errors"
"github.com/apolloconfig/agollo/v4"
"github.com/apolloconfig/agollo/v4/env/config"
)
var (
// Client 客户端链接
Client *agollo.Client
)
// Init 初始化apollo客户端
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:12 下午 2021/11/24
func Init(cfg *config.AppConfig) error {
var err error
if Client, err = agollo.StartWithConfig(func() (*config.AppConfig, error) {
return cfg, nil
}); err != nil {
return errors.New("apollo client init fail : " + err.Error())
}
return nil
}

@ -1,281 +0,0 @@
// Package apollo...
//
// Description : apollo...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 10:26 下午
package apollo
import (
"encoding/json"
"errors"
"fmt"
"regexp"
"strings"
"sync"
"git.zhangdeman.cn/zhangdeman/gopkg/convert"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
)
const (
// OperateTypeIs 固定值
OperateTypeIs = "is"
// OperateTypeIn 在指定枚举值中
OperateTypeIn = "in"
// OperateTypeNotIn 不在指定枚举值中
OperateTypeNotIn = "not_in"
// OperateGt 大于
OperateGt = "gt"
// OperateGte 大于等于
OperateGte = "gte"
// OperateLt 小于
OperateLt = "lt"
// OperateLte 小于等于
OperateLte = "lte"
// OperateTypeContains 包含
OperateTypeContains = "contains"
// OperateTypeRegular 正则匹配
OperateTypeRegular = "regular"
)
var (
// hasCompilerRegula 已编译正则,不进行重复编辑
hasCompilerRegula map[string]*regexp.Regexp
// regulaLock 编译正则用的锁
regulaLock *sync.RWMutex
)
func init() {
regulaLock = &sync.RWMutex{}
hasCompilerRegula = make(map[string]*regexp.Regexp)
}
// Rule 数据过滤规则
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:32 下午 2021/11/24
type Rule struct {
Name string `json:"name"` // 规则名称
ConditionList []Condition `json:"condition_list"` // 条件列表
Data map[string]interface{} `json:"data"` // 满足条件时返回的数据
}
// Condition 条件处理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:27 下午 2021/11/24
type Condition struct {
InputKey string `json:"input_key"` // 从输入数据中读取的key
Operate string `json:"operate"` // 操作
TargetValue string `json:"target_value"` // 目标值
}
// Search 搜索key并按照指定条件过滤
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:01 下午 2021/11/24
func Search(key string, inputData map[string]interface{}, receiver interface{}) error {
if nil == receiver {
return errors.New("receiver is nil")
}
if nil == inputData {
inputData = make(map[string]interface{})
}
var (
readResult string
err error
formatResult []Rule
)
if readResult = Client.GetStringValue(key, ""); len(readResult) == 0 {
return errors.New("key is not found : " + key)
}
if err = json.Unmarshal([]byte(readResult), &formatResult); nil != err {
return errors.New("parse read config fail : " + err.Error())
}
for _, rule := range formatResult {
if checkRule(rule, inputData) {
byteData, mErr := json.Marshal(rule.Data)
if nil != mErr {
return mErr
}
// 命中规则, 返回数据
return util.JSONUnmarshalWithNumber(byteData, receiver)
}
}
return nil
}
// checkRule 检测数据是否命中规则
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:59 下午 2021/11/24
func checkRule(rule Rule, inputData map[string]interface{}) bool {
for _, cond := range rule.ConditionList {
val, exist := inputData[cond.InputKey]
if !exist {
// 输入的key 不存在, 不符合规则
return false
}
switch cond.Operate {
case OperateTypeIs:
if !is(val, cond.TargetValue) {
return false
}
case OperateTypeIn:
if !in(val, cond.TargetValue) {
return false
}
case OperateTypeNotIn:
if !notIn(val, cond.TargetValue) {
return false
}
case OperateTypeContains:
if !contains(val, cond.TargetValue) {
return false
}
case OperateGt:
if !compareNumber(val, cond.TargetValue, OperateGt) {
return false
}
case OperateGte:
if !compareNumber(val, cond.TargetValue, OperateGte) {
return false
}
case OperateLt:
if !compareNumber(val, cond.TargetValue, OperateLt) {
return false
}
case OperateLte:
if !compareNumber(val, cond.TargetValue, OperateLte) {
return false
}
default:
return false
}
}
return true
}
// is 判断是否相等
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:28 上午 2021/11/25
func is(inputVal interface{}, targetValue string) bool {
return fmt.Sprintf("%v", inputVal) == targetValue
}
// in ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:47 上午 2021/11/25
func in(inputValue interface{}, targetValue string) bool {
if nil == inputValue {
return false
}
inputStr := fmt.Sprintf("%v", inputValue)
targetList := strings.Split(targetValue, ",")
for _, item := range targetList {
if inputStr == item {
return true
}
}
return false
}
// notIn ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:47 上午 2021/11/25
func notIn(inputValue interface{}, targetValue string) bool {
if nil == inputValue {
return false
}
inputStr := fmt.Sprintf("%v", inputValue)
targetList := strings.Split(targetValue, ",")
for _, item := range targetList {
if inputStr == item {
return false
}
}
return true
}
// compareNumber ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:56 上午 2021/11/25
func compareNumber(inputValue interface{}, targetValue string, operateType string) bool {
if inputValue == nil || len(targetValue) == 0 {
return false
}
var (
err error
inputFloat float64
targetFloat float64
)
if err = convert.ConvertAssign(&inputFloat, inputValue); nil != err {
return false
}
if err = convert.ConvertAssign(&targetFloat, targetValue); nil != err {
return false
}
switch operateType {
case OperateGt:
return inputFloat > targetFloat
case OperateGte:
return inputFloat >= targetFloat
case OperateLt:
return inputFloat < targetFloat
case OperateLte:
return inputFloat <= targetFloat
}
return false
}
// contains ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:03 下午 2021/11/25
func contains(inputValue interface{}, targetValue string) bool {
return strings.Contains(fmt.Sprintf("%v", inputValue), targetValue)
}
// regula 正则匹配
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:15 下午 2021/11/25
func regula(inputValue interface{}, targetValue string) bool {
var (
reg *regexp.Regexp
err error
)
regulaLock.Lock()
if _, exist := hasCompilerRegula[targetValue]; !exist {
if reg, err = regexp.Compile(targetValue); nil != err {
regulaLock.Unlock()
return false
}
hasCompilerRegula[targetValue] = reg
} else {
reg = hasCompilerRegula[targetValue]
}
regulaLock.Unlock()
// 判断是否匹配到数据
result := reg.FindAllStringSubmatch(fmt.Sprintf("%v", inputValue), -1)
return len(result) > 0
}

@ -1,68 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 11:25 上午
package etcd
import (
"math"
"time"
"go.etcd.io/etcd/clientv3"
)
// 初始化client各种默认配置
const (
// DefaultDialTimeout 默认超时时间
DefaultDialTimeout = 5 * time.Second
// DefaultDialKeepAliveTime 默认ping时间
DefaultDialKeepAliveTime = 3 * time.Second
// DefaultDialKeepAliveTimeout ping之后等待响应的超时时间, 超时未响应, 连接将会断开
DefaultDialKeepAliveTimeout = 5 * time.Second
// DefaultMaxCallSendMsgSize 客户端请求体最大字节数, 默认 2M , 和etcd默认值保持一致
DefaultMaxCallSendMsgSize = 2 * 1024 * 1024
// DefaultMaxCallRecvMsgSize 客户端接受的响应题最大大小, 默认int32最大值, 和etcd默认值保持一致
DefaultMaxCallRecvMsgSize = math.MaxInt32
)
// 操作过程中各种默认配置
const (
// DefaultPutTimeout put 默认超时时间
DefaultPutTimeout = time.Second
// DefaultGetTimeout get 默认超时时间
DefaultGetTimeout = time.Second
// DefaultDeleteTimeout 删除的超时时间
DefaultDeleteTimeout = time.Second
)
// WatcherHandler 监听key变化的处理函数
type WatcherHandler func(event *clientv3.Event)
// CancelWatcherHandler 取消监听后的处理函数
type CancelWatcherHandler func(key string, data interface{})
// TimeoutWatcherHandler 超时之后的回调函数
type TimeoutWatcherHandler func(key string, timeout time.Duration)
// LeaseKeepALiveHandler 续期成功的处理
type LeaseKeepALiveHandler func(data *LeaseKeepAliveData)
// LeaseKeepAliveData 自动续期的数据结构
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:28 下午 2021/11/23
type LeaseKeepAliveData struct {
Key string `json:"key"` // 续期key
StartTime int64 `json:"start_time"` // 开始续期时间
LastLeaseTime int64 `json:"last_lease_time"` // 上一次续期事件时间
LeaseCnt int64 `json:"lease_cnt"` // 续期次数
HasFinish bool `json:"has_finish"` // 是否完成
LeaseFinishType string `json:"lease_finish_type"` // 续期完成类型
LeaseFinishTime int64 `json:"lease_finish_time"` // 续期完成时间
LeaseDetail *clientv3.LeaseKeepAliveResponse `json:"lease_detail"` // 续约数据
Data map[string]interface{} `json:"data"` // 携带的数据
}

@ -1,54 +0,0 @@
// Package etcd...
//
// Description : 删除相关操作
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 12:42 下午
package etcd
import (
"context"
"errors"
"time"
"go.etcd.io/etcd/clientv3"
)
// DeleteWithOption ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:02 下午 2021/11/24
func DeleteWithOption(ctx context.Context, key string, timeout time.Duration, optionList ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
if nil == ctx {
ctx = context.Background()
}
if timeout <= 0 {
timeout = DefaultDeleteTimeout
}
var (
cancelFunc context.CancelFunc
deleteResponse *clientv3.DeleteResponse
err error
)
ctx, cancelFunc = context.WithTimeout(ctx, timeout)
defer cancelFunc()
if deleteResponse, err = Client.Delete(ctx, key, optionList...); nil != err {
return nil, errors.New("delete key fail : " + err.Error())
}
return deleteResponse, nil
}
// DeleteWithKeyPrefix 基于 key 前缀, 删除key
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:12 下午 2021/11/24
func DeleteWithKeyPrefix(ctx context.Context, keyPrefix string, timeout time.Duration) (*clientv3.DeleteResponse, error) {
return DeleteWithOption(ctx, keyPrefix, timeout, clientv3.WithPrefix())
}

@ -1,22 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 2:11 下午
package etcd
import (
"fmt"
"testing"
)
// TestDeleteWithKeyPrefix ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:11 下午 2021/11/24
func TestDeleteWithKeyPrefix(t *testing.T) {
fmt.Println(DeleteWithKeyPrefix(nil, "/test", 0))
}

@ -1,59 +0,0 @@
// Package etcd ...
//
// Description : etcd ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 10:53 上午
package etcd
import (
"github.com/pkg/errors"
"go.etcd.io/etcd/clientv3"
)
var (
// Client 客户端
Client *clientv3.Client
)
// InitEtcdClient 初始化etcd连接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:53 上午 2021/11/23
func InitEtcdClient(cfg clientv3.Config) error {
if nil == cfg.Endpoints || len(cfg.Endpoints) == 0 {
return errors.New("endpoints is empty")
}
// 连接超时
if cfg.DialTimeout == 0 {
cfg.DialTimeout = DefaultDialTimeout
}
// ping 时间间隔
if cfg.DialKeepAliveTime == 0 {
cfg.DialKeepAliveTime = DefaultDialKeepAliveTime
}
// ping 响应超时时间
if cfg.DialKeepAliveTimeout == 0 {
cfg.DialKeepAliveTimeout = DefaultDialKeepAliveTimeout
}
// 客户端发送的最大请求体大小
if cfg.MaxCallSendMsgSize <= 0 {
cfg.MaxCallSendMsgSize = DefaultMaxCallSendMsgSize
}
// 客户端接受的最大响应体大小
if cfg.MaxCallRecvMsgSize <= 0 {
cfg.MaxCallRecvMsgSize = DefaultMaxCallRecvMsgSize
}
var err error
if Client, err = clientv3.New(cfg); nil != err {
return errors.New("etcd连接失败 : " + err.Error())
}
return nil
}

@ -1,179 +0,0 @@
// Package etcd ...
//
// Description : 租约相关操作
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 6:03 下午
package etcd
import (
"context"
"time"
"github.com/pkg/errors"
"go.etcd.io/etcd/clientv3"
)
// LeaseOnce 申请一个一次性租约
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:06 下午 2021/11/23
func LeaseOnce(ctx context.Context, key string, val string, ttl int64) error {
if ttl <= 0 {
return errors.New("lease time must be more than 0")
}
if nil == ctx {
ctx = context.Background()
}
var (
resp *clientv3.LeaseGrantResponse
err error
cancelFunc context.CancelFunc
)
ctx, cancelFunc = context.WithCancel(ctx)
defer cancelFunc()
// 创建一个5秒的租约
if resp, err = Client.Grant(ctx, ttl); err != nil {
return errors.New("lease grant error : " + err.Error())
}
// ttl 秒钟之后, 这个key就会被移除
if _, err = Client.Put(ctx, key, val, clientv3.WithLease(resp.ID)); err != nil {
return errors.New("lease key put fail : " + err.Error())
}
_, err = Client.KeepAliveOnce(ctx, resp.ID)
return err
}
// LeaseKeepAliveForever 无限续租一个key
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:40 下午 2021/11/23
func LeaseKeepAliveForever(ctx context.Context, key string, val string, ttl int64, keepAliveHandler LeaseKeepALiveHandler) error {
if ttl <= 0 {
return errors.New("lease time must be more than 0")
}
if nil == ctx {
ctx = context.TODO()
}
var (
resp *clientv3.LeaseGrantResponse
respChan <-chan *clientv3.LeaseKeepAliveResponse
err error
)
// 创建一个5秒的租约
if resp, err = Client.Grant(ctx, ttl); err != nil {
return errors.New("lease grant error : " + err.Error())
}
// ttl 秒钟之后, 这个key就会被移除
if _, err = Client.Put(context.TODO(), key, val, clientv3.WithLease(resp.ID)); err != nil {
return errors.New("lease key put fail : " + err.Error())
}
// the key will be kept forever
if respChan, err = Client.KeepAlive(ctx, resp.ID); nil != err {
return errors.New("lease keep alive fail : " + err.Error())
}
leaseData := &LeaseKeepAliveData{
Key: key,
StartTime: time.Now().Unix(),
LastLeaseTime: 0,
LeaseCnt: 0,
HasFinish: false,
LeaseFinishType: "",
LeaseFinishTime: 0,
LeaseDetail: nil,
Data: make(map[string]interface{}),
}
// 监听 chan
for ka := range respChan {
leaseData.LeaseCnt++
leaseData.LeaseDetail = ka
leaseData.LastLeaseTime = time.Now().Unix()
if nil != keepAliveHandler {
keepAliveHandler(leaseData)
}
}
return nil
}
// LeaseKeepAliveWithDuration 设置最大支持续期的时间, 中途可随时取消
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:05 下午 2021/11/23
func LeaseKeepAliveWithDuration(ctx context.Context, key string, val string, ttl int64, keepAliveHandler LeaseKeepALiveHandler, cancelLeaseChan chan *LeaseKeepAliveData, maxCnt int64) (*LeaseKeepAliveData, error) {
if ttl <= 0 {
return nil, errors.New("lease time must be more than 0")
}
if nil == cancelLeaseChan {
cancelLeaseChan = make(chan *LeaseKeepAliveData, 1)
}
var cancelFunc context.CancelFunc
if nil == ctx {
ctx, cancelFunc = context.WithCancel(context.Background())
} else {
ctx, cancelFunc = context.WithCancel(ctx)
}
defer cancelFunc()
var (
resp *clientv3.LeaseGrantResponse
respChan <-chan *clientv3.LeaseKeepAliveResponse
err error
)
leaseData := &LeaseKeepAliveData{
Key: key,
StartTime: 0,
LastLeaseTime: 0,
LeaseCnt: 0,
LeaseFinishType: "",
LeaseFinishTime: 0,
Data: make(map[string]interface{}),
HasFinish: false,
}
// 创建一个 ttl 秒的租约
if resp, err = Client.Grant(ctx, ttl); err != nil {
return nil, errors.New("lease grant error : " + err.Error())
}
leaseData.StartTime = time.Now().Unix()
// ttl 秒钟之后, 这个key就会被移除
if _, err = Client.Put(context.TODO(), key, val, clientv3.WithLease(resp.ID)); err != nil {
return nil, errors.New("lease key put fail : " + err.Error())
}
// the key will be kept forever
if respChan, err = Client.KeepAlive(ctx, resp.ID); nil != err {
return nil, errors.New("lease keep alive fail : " + err.Error())
}
for {
select {
case <-cancelLeaseChan:
leaseData.HasFinish = true
leaseData.LeaseFinishTime = time.Now().Unix()
leaseData.LeaseFinishType = "SIGNAL_CANCEL"
case leaseResp := <-respChan:
leaseData.LeaseCnt++
leaseData.LastLeaseTime = time.Now().Unix()
leaseData.LeaseDetail = leaseResp
if nil != keepAliveHandler {
keepAliveHandler(leaseData)
}
if leaseData.LeaseCnt >= maxCnt {
leaseData.HasFinish = true
leaseData.LeaseFinishType = "OVER_MAX_CNT"
leaseData.LeaseFinishTime = time.Now().Unix()
}
}
if leaseData.HasFinish {
break
}
}
return leaseData, nil
}

@ -1,68 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 11:40 上午
package etcd
import (
"fmt"
"testing"
"time"
)
// TestLeaseOnce ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:37 下午 2021/11/23
func TestLeaseOnce(t *testing.T) {
key := "lock"
fmt.Println(LeaseOnce(nil, key, "lock", 10))
for i := 0; i < 15; i++ {
fmt.Println(Get(nil, key, 1))
time.Sleep(time.Second)
}
}
// TestLeaseKeepAliveForever ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:54 下午 2021/11/23
func TestLeaseKeepAliveForever(t *testing.T) {
key := "lock"
keepAliveHandler := func(data *LeaseKeepAliveData) {
fmt.Println(key, data.LeaseDetail.ID, data.LeaseDetail.TTL, data.LeaseCnt)
}
go func() {
fmt.Println(LeaseKeepAliveForever(nil, key, "lock", 10, keepAliveHandler))
}()
for i := 0; i < 15; i++ {
r, e := Get(nil, key, 1)
fmt.Println("读取", r, e)
time.Sleep(time.Second)
}
}
// TestLeaseKeepAliveWithDuration ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:56 下午 2021/11/23
func TestLeaseKeepAliveWithDuration(t *testing.T) {
key := "lock"
keepAliveHandler := func(data *LeaseKeepAliveData) {
fmt.Println(key, data.LeaseDetail.ID, data.LeaseDetail.TTL, data.LeaseCnt)
}
go func() {
fmt.Println(LeaseKeepAliveWithDuration(nil, key, "lock", 1, keepAliveHandler, nil, 5))
}()
for i := 0; i < 15; i++ {
r, e := Get(nil, key, 1)
fmt.Println("读取", r, e)
time.Sleep(time.Second)
}
}

@ -1,101 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 12:09 下午
package etcd
import (
"context"
"time"
"github.com/coreos/etcd/mvcc/mvccpb"
"go.etcd.io/etcd/clientv3"
)
// Put put数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:09 下午 2021/11/23
func Put(ctx context.Context, key string, val string, operateTimeout time.Duration) error {
if operateTimeout <= 0 {
operateTimeout = DefaultPutTimeout
}
var (
cancel context.CancelFunc
err error
)
if nil == ctx {
ctx = context.Background()
}
ctx, cancel = context.WithTimeout(ctx, operateTimeout)
_, err = Client.Put(ctx, key, val)
cancel()
return err
}
// GetWithOption 使用各种option选项读取数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:17 上午 2021/11/24
func GetWithOption(ctx context.Context, key string, operateTimeout time.Duration, optionList ...clientv3.OpOption) (
[]*mvccpb.KeyValue,
error,
) {
if operateTimeout <= 0 {
operateTimeout = DefaultGetTimeout
}
var (
cancel context.CancelFunc
err error
result *clientv3.GetResponse
)
if nil == ctx {
ctx = context.Background()
}
ctx, cancel = context.WithTimeout(context.Background(), operateTimeout)
defer cancel()
if result, err = Client.Get(ctx, key, optionList...); err != nil {
return nil, err
}
return result.Kvs, nil
}
// Get 读取数据,按照key精确匹配
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:09 下午 2021/11/23
func Get(ctx context.Context, key string, operateTimeout time.Duration) (
[]*mvccpb.KeyValue,
error,
) {
return GetWithOption(ctx, key, operateTimeout)
}
// GetWithPrefix 使用key前缀查找
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:58 上午 2021/11/24
func GetWithPrefix(ctx context.Context, keyPrefix string, operateTimeout time.Duration) (
map[string]*mvccpb.KeyValue,
error,
) {
var (
result []*mvccpb.KeyValue
err error
)
if result, err = GetWithOption(ctx, keyPrefix, operateTimeout, clientv3.WithPrefix()); nil != err {
return make(map[string]*mvccpb.KeyValue), err
}
formatResult := make(map[string]*mvccpb.KeyValue)
for _, item := range result {
formatResult[string(item.Key)] = item
}
return formatResult, nil
}

@ -1,57 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 12:27 下午
package etcd
import (
"fmt"
"testing"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
"go.etcd.io/etcd/clientv3"
)
func init() {
err := InitEtcdClient(clientv3.Config{
Endpoints: []string{"localhost:2379"},
})
if nil != err {
panic(err.Error())
}
}
// TestPut ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:27 下午 2021/11/23
func TestPut(t *testing.T) {
fmt.Println(Put(nil, "name", "zhangdeman", 0))
}
// TestGet ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:30 下午 2021/11/23
func TestGet(t *testing.T) {
fmt.Println(Get(nil, "name", 0))
}
// TestGetWithPrefix 根据key前缀读取数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:01 上午 2021/11/24
func TestGetWithPrefix(t *testing.T) {
prefix := "/test/dir/"
for i := 0; i < 10; i++ {
_ = Put(nil, fmt.Sprintf("%s%d", prefix, i), util.GenRandomString("", 8), 0)
}
fmt.Println(GetWithPrefix(nil, prefix, 0))
}

@ -1,137 +0,0 @@
// Package etcd...
//
// Description : 监听key的变化
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-23 2:58 下午
package etcd
import (
"context"
"math"
"time"
"go.etcd.io/etcd/clientv3"
)
// WatchKeyWithOption ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:29 下午 2021/11/26
func WatchKeyWithOption(ctx context.Context, watchKey string, callbackFunc WatcherHandler, optionList ...clientv3.OpOption) {
if nil == callbackFunc {
// 变化之后,没有任何逻辑处理,视为不需要监听变化
return
}
if nil == ctx {
ctx = context.Background()
}
rch := Client.Watch(ctx, watchKey, optionList...) // <-chan WatchResponse
for watchResp := range rch {
for _, ev := range watchResp.Events {
callbackFunc(ev)
}
}
}
// WatchKey 监听key的变化,永久监听
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:58 下午 2021/11/23
func WatchKey(ctx context.Context, watchKey string, callbackFunc WatcherHandler) {
WatchKeyWithOption(ctx, watchKey, callbackFunc)
}
// WatchWithKeyPrefix ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:30 下午 2021/11/26
func WatchWithKeyPrefix(ctx context.Context, watchKey string, callbackFunc WatcherHandler) {
WatchKeyWithOption(ctx, watchKey, callbackFunc, clientv3.WithPrefix())
}
// WatchKeyWithCancel 可以随时取消的
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:16 下午 2021/11/23
func WatchKeyWithCancel(ctx context.Context, watchKey string, callbackFunc WatcherHandler, cancelChan chan interface{}, cancelDealFunc CancelWatcherHandler) {
if nil == callbackFunc {
// 变化之后,没有任何逻辑处理,视为不需要监听变化
return
}
if nil == ctx {
ctx = context.Background()
}
var (
cancelFunc context.CancelFunc
)
ctx, cancelFunc = context.WithCancel(ctx)
defer cancelFunc()
rch := Client.Watch(context.Background(), watchKey) // <-chan WatchResponse
hasFinish := false
for {
select {
case cancelData := <-cancelChan:
if nil != cancelDealFunc {
cancelDealFunc(watchKey, cancelData)
}
hasFinish = true
case watchResp := <-rch:
for _, ev := range watchResp.Events {
callbackFunc(ev)
}
}
if hasFinish {
break
}
}
}
// WatchKeyOnce 监听一次key
//
// timeout 若 <= 0 , 则会一直等待当前key发生变化为止
//
// timeout 若 > 0 , 则在制定时间内为监听到变化, 立即自动取消,
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:35 下午 2021/11/23
func WatchKeyOnce(ctx context.Context, watchKey string, callbackFunc WatcherHandler, timeout time.Duration, timeoutHandler TimeoutWatcherHandler) {
if nil == callbackFunc {
// 变化之后,没有任何逻辑处理,视为不需要监听变化
return
}
if timeout == 0 {
// 不指定超时时间默认设置成int64最大值
timeout = math.MaxInt16 * time.Second
}
if nil == ctx {
ctx = context.Background()
}
var (
cancelFunc context.CancelFunc
)
ctx, cancelFunc = context.WithCancel(ctx)
defer cancelFunc()
rch := Client.Watch(ctx, watchKey) // <-chan WatchResponse
select {
case <-time.After(timeout):
if nil != timeoutHandler {
timeoutHandler(watchKey, timeout)
}
case watchResp := <-rch:
for _, ev := range watchResp.Events {
callbackFunc(ev)
}
}
}

@ -1,134 +0,0 @@
// Package etcd...
//
// Description : etcd...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-24 11:40 上午
package etcd
import (
"fmt"
"testing"
"time"
"go.etcd.io/etcd/clientv3"
)
// TestWatchKey ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:06 下午 2021/11/23
func TestWatchKey(t *testing.T) {
key := "name"
dealFunc := func(data *clientv3.Event) {
fmt.Println(string(data.Kv.Key), string(data.Kv.Value), data.Kv.Version, data.Kv.CreateRevision, data.Kv.ModRevision)
}
go func() {
for i := 0; i < 30; i++ {
_ = Put(nil, key, fmt.Sprintf("test-%d", i), 0)
time.Sleep(time.Second)
}
}()
WatchKey(nil, key, dealFunc)
}
// TestWatchKeyWithCancel ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:57 下午 2021/11/23
func TestWatchKeyWithCancel(t *testing.T) {
key := "name"
dealFunc := func(data *clientv3.Event) {
fmt.Println(string(data.Kv.Key), string(data.Kv.Value), data.Kv.Version, data.Kv.CreateRevision, data.Kv.ModRevision)
}
cancelFunc := func(key string, data interface{}) {
fmt.Println("取消监听 : ", key, data)
}
cancelChan := make(chan interface{}, 1)
go func() {
for i := 0; i < 30; i++ {
_ = Put(nil, key, fmt.Sprintf("test-%d", i), 0)
time.Sleep(time.Second)
}
time.Sleep(10 * time.Second)
cancelChan <- "Hello World"
}()
WatchKeyWithCancel(nil, key, dealFunc, cancelChan, cancelFunc)
}
// TestWatchKeyWithCancelByChangeCallback ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:27 下午 2021/11/23
func TestWatchKeyWithCancelByChangeCallback(t *testing.T) {
key := "name"
cancelChan := make(chan interface{}, 1)
dealFunc := func(data *clientv3.Event) {
fmt.Println(string(data.Kv.Key), string(data.Kv.Value), data.Kv.Version, data.Kv.CreateRevision, data.Kv.ModRevision)
if string(data.Kv.Value) == "test-29" {
cancelChan <- "Hello World!"
}
}
cancelFunc := func(key string, data interface{}) {
fmt.Println("取消监听 : ", key, data)
}
go func() {
for i := 0; i < 30; i++ {
_ = Put(nil, key, fmt.Sprintf("test-%d", i), 0)
time.Sleep(time.Second)
}
}()
WatchKeyWithCancel(nil, key, dealFunc, cancelChan, cancelFunc)
}
// TestWatchKeyOnce ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:50 下午 2021/11/23
func TestWatchKeyOnce(t *testing.T) {
key := "name"
dealFunc := func(data *clientv3.Event) {
fmt.Println(string(data.Kv.Key), string(data.Kv.Value), data.Kv.Version, data.Kv.CreateRevision, data.Kv.ModRevision)
}
timeoutFunc := func(key string, timeout time.Duration) {
fmt.Println("监听超时", key, timeout)
}
timeout := 10 * time.Second
go func() {
for i := 0; i < 30; i++ {
_ = Put(nil, key, fmt.Sprintf("test-%d", i), 0)
time.Sleep(time.Second)
}
}()
WatchKeyOnce(nil, key, dealFunc, timeout, timeoutFunc)
}
// TestWatchKeyOnceForTimeout ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:53 下午 2021/11/23
func TestWatchKeyOnceForTimeout(t *testing.T) {
key := "name"
dealFunc := func(data *clientv3.Event) {
fmt.Println(string(data.Kv.Key), string(data.Kv.Value), data.Kv.Version, data.Kv.CreateRevision, data.Kv.ModRevision)
}
timeoutFunc := func(key string, timeout time.Duration) {
fmt.Println("监听超时", key, timeout)
}
timeout := time.Second
WatchKeyOnce(nil, key, dealFunc, timeout, timeoutFunc)
}

@ -10,107 +10,14 @@ package mysql
import (
"fmt"
"git.zhangdeman.cn/zhangdeman/gopkg/logger/wrapper"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
"git.zhangdeman.cn/zhangdeman/gopkg/logger"
gormLogger "gorm.io/gorm/logger"
"git.zhangdeman.cn/zhangdeman/gopkg/logger/wrapper"
"gorm.io/driver/mysql"
"gorm.io/gorm"
)
// NewDBClient ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:09 PM 2021/12/24
func NewDBClient(masterConf *DBConfig, slaveConf *DBConfig, logConf *LogConfig, loggerInstance *zap.Logger, extraRequestFieldList []string) (*DBClient, error) {
client := &DBClient{
extraFieldList: extraRequestFieldList,
}
var err error
// 日志初始化失败
if client.loggerInstance, err = getLogInstance(logConf, loggerInstance); nil != err {
return nil, err
}
if client.master, err = GetDatabaseClient(masterConf, nil); nil != err {
return nil, err
}
if client.slave, err = GetDatabaseClient(slaveConf, nil); nil != err {
return nil, err
}
return client, nil
}
// DBClient 包装日志实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:09 PM 2021/12/24
type DBClient struct {
loggerInstance *zap.Logger
master *gorm.DB
slave *gorm.DB
extraFieldList []string
}
// GetMaster 获取主库连接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:28 PM 2021/12/24
func (dc *DBClient) GetMaster(ctx *gin.Context) *gorm.DB {
session := dc.master.Session(&gorm.Session{})
session.Logger = dc.getLogger(ctx, session, "slave")
return session
}
// GetSlave 获取从库链接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:29 PM 2021/12/24
func (dc *DBClient) GetSlave(ctx *gin.Context) *gorm.DB {
session := dc.slave.Session(&gorm.Session{})
session.Logger = dc.getLogger(ctx, session, "slave")
return session
}
// getLogger 获取日志实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:45 PM 2021/12/24
func (dc *DBClient) getLogger(ctx *gin.Context, dbClient *gorm.DB, node string) gormLogger.Interface {
return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.loggerInstance, node, dc.extraFieldList)
}
// getLogInstance 获取日志实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:20 PM 2021/12/24
func getLogInstance(logConf *LogConfig, loggerInstance *zap.Logger) (*zap.Logger, error) {
if nil != loggerInstance {
return loggerInstance, nil
}
logConfList := []logger.SetLoggerOptionFunc{logger.WithEncoder(logConf.Encoder), logger.WithCallerSkip(logConf.Skip), logger.WithCaller()}
if logConf.ConsoleOutput {
logConfList = append(logConfList, logger.WithConsoleOutput())
}
var (
err error
)
if loggerInstance, err = logger.NewLogger(logConf.Level, logConf.SplitConfig, logConfList...); nil != err {
return nil, err
}
return loggerInstance, nil
}
// GetDatabaseClient 获取日志实例
//
// Author : go_developer@163.com<白茶清欢>
@ -118,22 +25,35 @@ func getLogInstance(logConf *LogConfig, loggerInstance *zap.Logger) (*zap.Logger
// Date : 10:49 下午 2021/3/1
func GetDatabaseClient(conf *DBConfig, logConf *LogConfig) (*gorm.DB, error) {
var (
instance *gorm.DB
err error
loggerInstance *zap.Logger
instance *gorm.DB
err error
)
if instance, err = gorm.Open(mysql.Open(buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
return nil, err
}
if nil != logConf {
if loggerInstance, err = getLogInstance(logConf, nil); nil != err {
return nil, err
}
instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, loggerInstance, "", nil)
if len(logConf.TraceFieldName) == 0 {
logConf.TraceFieldName = defaultTraceFieldName
}
splitConfigFuncList := []logger.SetRotateLogConfigFunc{
logger.WithTimeIntervalType(logConf.SplitConfig.TimeIntervalType),
logger.WithDivisionChar(logConf.SplitConfig.DivisionChar),
logger.WithMaxAge(logConf.SplitConfig.MaxAge),
}
splitConfig, _ := logger.NewRotateLogConfig(logConf.SplitConfig.LogPath, logConf.SplitConfig.LogFileName, splitConfigFuncList...)
if instance.Logger, err = wrapper.NewGormV2(
logConf.Level,
logConf.ConsoleOutput,
logConf.Encoder,
splitConfig,
logConf.TraceFieldName,
logConf.Skip); nil != err {
return nil, CreateDBLogError(err)
}
return instance, nil
}

@ -42,3 +42,8 @@ type LogConfig struct {
TraceFieldName string
Skip int
}
const (
// defaultTraceFieldName 默认trace_id字段
defaultTraceFieldName = "trace_id"
)

@ -1,139 +0,0 @@
// Package wrapper...
//
// Description : wrapper...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-15 6:02 下午
package wrapper
import (
"git.zhangdeman.cn/zhangdeman/gopkg/middleware/mysql"
"github.com/pkg/errors"
"gorm.io/gorm"
)
// DBConfig ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:00 下午 2021/11/15
type DBConfig struct {
DBConfig *mysql.DBConfig
LogConfig *mysql.LogConfig
}
// DBWrapperConfig ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:58 下午 2021/11/15
type DBWrapperConfig struct {
Write *DBConfig
Read *DBConfig
}
// NewDBWrapper 获取包装后的数据库实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:45 下午 2021/11/15
func NewDBWrapper(cf *DBWrapperConfig) (*DBWrapper, error) {
var (
err error
)
if nil == cf {
return nil, errors.New("cf is nil")
}
if nil == cf.Write {
return nil, errors.New("write cf is nil")
}
if nil == cf.Read {
return nil, errors.New("read cf is nil")
}
wrapperInstance := &DBWrapper{}
if wrapperInstance.write, err = mysql.GetDatabaseClient(cf.Write.DBConfig, cf.Write.LogConfig); nil != err {
return nil, err
}
if wrapperInstance.read, err = mysql.GetDatabaseClient(cf.Read.DBConfig, cf.Read.LogConfig); nil != err {
return nil, err
}
return wrapperInstance, nil
}
// DBWrapper 数据库包装
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:02 下午 2021/11/15
type DBWrapper struct {
read *gorm.DB // 读库实例
write *gorm.DB // 写库实例
}
// SelectDBOption 数据库实力选择时的选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:33 下午 2021/11/15
type SelectDBOption struct {
Table string
ForceMaster bool
}
// SetSelectDBOption 设置选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:34 下午 2021/11/15
type SetSelectDBOption func(o *SelectDBOption)
// GetWriteInstance 获取主库实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:31 下午 2021/11/15
func (dw *DBWrapper) GetWriteInstance(instanceOptionList ...SetSelectDBOption) *gorm.DB {
option := dw.getOption(instanceOptionList...)
instance := dw.write
if len(option.Table) > 0 {
instance.Table(option.Table)
}
return instance
}
// GetReadInstance 获取读库实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:32 下午 2021/11/15
func (dw *DBWrapper) GetReadInstance(instanceOptionList ...SetSelectDBOption) *gorm.DB {
option := dw.getOption(instanceOptionList...)
instance := dw.read
if option.ForceMaster || nil == dw.read {
instance = dw.write
}
if len(option.Table) > 0 {
instance.Table(option.Table)
}
return instance
}
// getOption 生成option
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:35 下午 2021/11/15
func (dw *DBWrapper) getOption(instanceOptionList ...SetSelectDBOption) *SelectDBOption {
o := &SelectDBOption{
Table: "",
ForceMaster: false,
}
for _, f := range instanceOptionList {
f(o)
}
return o
}

@ -1,301 +0,0 @@
// Package redis ...
//
// Description : redis ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-26 21:07 下午
package redis
// FullServerInfo 获取服务器信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:07 下午 2021/11/26
type FullServerInfo struct {
ServerInfo *ServerInfo `json:"server_info"`
ClientInfo *ClientInfo `json:"client_info"`
MemoryInfo *MemoryInfo `json:"memory_info"`
Persistence *Persistence `json:"persistence"`
Stats *Stats `json:"stats"`
Replication *Replication `json:"replication"`
CPU *CPU `json:"cpu"`
CommandStats []CmdStat `json:"command_stats"`
Keyspace []DB `json:"keyspace"`
Cluster *Cluster `json:"cluster"`
ErrorStats []Error `json:"error_stats"`
Modules interface{} `json:"modules"`
}
// ServerInfo 服务器信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:09 下午 2021/11/26
type ServerInfo struct {
RedisVersion string `json:"redis_version" yaml:"redis_version"` // Redis 服务器版本
RedisGitSha1 string `json:"redis_git_sha1" yaml:"redis_git_sha1"` // Git SHA1
RedisGitDirty string `json:"redis_git_dirty" yaml:"redis_git_dirty"` // Git dirty flag
RedisBuildID string `json:"redis_build_id" yaml:"redis_build_id"` // 构建ID
RedisMode string `json:"redis_mode" yaml:"redis_mode"` // 运行模式(“独立”,“哨兵”或“集群”)
OS string `json:"os" yaml:"os"` // Redis 服务器的宿主操作系统
ArchBits string `json:"arch_bits" yaml:"arch_bits"` // 架构32 或 64 位)
MultiplexingApi string `json:"multiplexing_api" yaml:"multiplexing_api"` // Redis 所使用的事件处理机制
AtomicvarApi string `json:"atomicvar_api" yaml:"atomicvar_api"` // 原子处理api
GCCVersion string `json:"gcc_version" yaml:"gcc_version"` // 编译 Redis 时所使用的 GCC 版本
ProcessID int `json:"process_id" yaml:"process_id"` // 服务器进程的 PID
ProcessSupervised string `json:"process_supervised" yaml:"process_supervised"` // 是否有进程监控
RunID string `json:"run_id" yaml:"run_id"` // Redis 服务器的随机标识符(用于 Sentinel 和集群)
TCPPort int `json:"tcp_port" yaml:"tcp_port"` // TCP/IP 监听端口
ServerTimeUsec int64 `json:"server_time_usec" yaml:"server_time_usec"` // 当前服务器时间,微秒
UptimeInSeconds int64 `json:"uptime_in_seconds" yaml:"uptime_in_seconds"` // 自 Redis 服务器启动以来,经过的秒数
UptimeInDays int64 `json:"uptime_in_days" yaml:"uptime_in_days"` // 自 Redis 服务器启动以来,经过的天数
Hz int64 `json:"hz" yaml:"hz"` // redis内部调度进行关闭timeout的客户端删除过期key等等频率程序规定serverCron每秒运行10次。
ConfiguredHz int64 `json:"configured_hz" yaml:"configured_hz"` // 服务器的频率设置
LRUClock int64 `json:"lru_clock" yaml:"lru_clock"` // 以秒为单位进行自增的时钟,用于 LRU 管理
Executable string `json:"executable" yaml:"executable"` // 可执行文件位置
ConfigFile string `json:"config_file" yaml:"config_file"` // 服务所使用的配置文件
IOThreadsActive int `json:"io_threads_active" yaml:"io_threads_active"` // 活跃的io线程数
}
// ClientInfo 客户端信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:29 下午 2021/11/26
type ClientInfo struct {
ConnectedClients int `json:"connected_clients" yaml:"connected_clients"` // 客户端连接数
ClusterConnections int `json:"cluster_connections" yaml:"cluster_connections"` // 集群连接数
MaxClients int `json:"maxclients" yaml:"max_clients"` // 最大客户端连接数
ClientRecentMaxInputBuffer int64 `json:"client_recent_max_input_buffer" yaml:"client_recent_max_input_buffer"` // 最近最大输入缓存
ClientRecentMaxOutputBuffer int64 `json:"client_recent_max_output_buffer" yaml:"client_recent_max_output_buffer"` // 最近最大输出缓存
BlockedClients int `json:"blocked_clients" yaml:"blocked_clients"` // 阻塞客户端数量, 正在等待阻塞命令BLPOP、BRPOP、BRPOPLPUSH的客户端的数量
TrackingClients int `json:"tracking_clients" yaml:"tracking_clients"` // tracking_clients
ClientsInTimeoutTable int `json:"clients_in_timeout_table" yaml:"clients_in_timeout_table"` // clients_in_timeout_table
}
// MemoryInfo 内存使用信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:08 下午 2021/11/26
type MemoryInfo struct {
UsedMemory int64 `json:"used_memory" yaml:"used_memory"` // 由 Redis 分配器分配的内存总量以字节byte为单位
UsedMemoryHuman string `json:"used_memory_human" yaml:"used_memory_human"` // UsedMemory 可读化表示
UsedMemoryRss int64 `json:"used_memory_rss" yaml:"used_memory_rss"` // 从操作系统的角度,返回 Redis 已分配的内存总量(俗称常驻集大小)。这个值和 top 、 ps 等命令的输出一致。
UsedMemoryRssHuman string `json:"used_memory_rss_human" yaml:"used_memory_rss_human"` // UsedMemoryRssHuman 可读化表示
UsedMemoryPeak int64 `json:"used_memory_peak" yaml:"used_memory_peak"` // Redis 的内存消耗峰值(以字节为单位)
UsedMemoryPeakHuman string `json:"used_memory_peak_human" yaml:"used_memory_peak_human"` // UsedMemoryPeak 可读化表示
UsedMemoryPeakPerc string `json:"used_memory_peak_perc" yaml:"used_memory_peak_perc"` // 峰值系统内存使用率
UsedMemoryOverhead int64 `json:"used_memory_overhead" yaml:"used_memory_overhead"` // Redis为了维护数据集的内部机制所需的内存开销包括所有客户端输出缓冲区、查询缓冲区、AOF重写缓冲区和主从复制的backlog
UsedMemoryStartup int64 `json:"used_memory_startup" yaml:"used_memory_startup"` // Redis服务器启动时消耗的内存
UsedMemoryDataset int64 `json:"used_memory_dataset" yaml:"used_memory_dataset"` // 数据占用的内存大小即used_memory-used_memory_overhead
UsedMemoryDatasetPerc int64 `json:"used_memory_dataset_perc" yaml:"used_memory_dataset_perc"` // 数据占用的内存大小的百分比100%*(used_memory_dataset/(used_memory-used_memory_startup))
AllocatorAllocated int64 `json:"allocator_allocated" yaml:"allocator_allocated"` // 内存分配器申请的内存,以字节为单位
AllocatorActive int64 `json:"allocator_active" yaml:"allocator_active"` // 内存分配器正在使用的内存,以字节为单位
AllocatorResident int64 `json:"allocator_resident" yaml:"allocator_resident"` // 内存分配器的常驻内存,以字节为单位
TotalSystemMemory int64 `json:"total_system_memory" yaml:"total_system_memory"` // 操作系统内存(以字节为单位)
TotalSystemMemoryHuman string `json:"total_system_memory_human" yaml:"total_system_memory_human"` // TotalSystemMemory 可读化表示
UsedMemoryLua int64 `json:"used_memory_lua" yaml:"used_memory_lua"` // Lua脚本存储占用的内存以字节为单位
UsedMemoryLuaHuman int64 `json:"used_memory_lua_human" yaml:"used_memory_lua_human"` // UsedMemoryLua 可读化表示
UsedMemoryScripts int64 `json:"used_memory_scripts" yaml:"used_memory_scripts"` // Lua脚本使用的内存大小以字节为单位
UsedMemoryScriptsHuman int64 `json:"used_memory_scripts_human" yaml:"used_memory_scripts_human"` // UsedMemoryScripts 可读化表示
NumberOfCachedScripts int64 `json:"number_of_cached_scripts" yaml:"number_of_cached_scripts"` // 缓存的lua脚本数量
Maxmemory int64 `json:"maxmemory" yaml:"maxmemory"` // Redis实例的最大内存配置以字节为单位
MaxmemoryHuman int64 `json:"maxmemory_human" yaml:"maxmemory_human"` // Maxmemory 可读化表示
MaxmemoryPolicy string `json:"maxmemory_policy" yaml:"maxmemory_policy"` // 当数据达到最大内存之后的淘汰策略
AllocatorFragRatio float64 `json:"allocator_frag_ratio" yaml:"allocator_frag_ratio"` // 内存分配器碎片比例
AllocatorFragBytes int64 `json:"allocator_frag_bytes" yaml:"allocator_frag_bytes"` // 内存分配器碎片大小,以字节为单位
AllocatorRssRatio float64 `json:"allocator_rss_ratio" yaml:"allocator_rss_ratio"` // 从操作系统角度看, 内存分配器碎片比例
AllocatorRssBytes int64 `json:"allocator_rss_bytes" yaml:"allocator_rss_bytes"` // 从操作系统角度看, 内存分配器碎片大小,以字节为单位
RssOverheadRatio float64 `json:"rss_overhead_ratio" yaml:"rss_overhead_ratio"` // 从操作系统角度看, 开销的比例
RssOverheadBytes int64 `json:"rss_overhead_bytes" yaml:"rss_overhead_bytes"` // 从操作系统角度看, 开销的大小, 以字节为单位
MemFragmentationRatio float64 `json:"mem_fragmentation_ratio" yaml:"mem_fragmentation_ratio"` // 碎片率used_memory_rss/ used_memory正常情况下稍大于1。低于1Redis实例可能会把部分数据交换到硬盘上内存交换会严重影响Redis的性能所以应该增加可用物理内存。大于1.5表示碎片过多。额外碎片的产生是由于Redis释放了内存块但内存分配器并没有返回内存给操作系统这个内存分配器是在编译时指定的可以是libc、jemalloc或者tcmalloc。
MemFragmentationBytes int64 `json:"mem_fragmentation_bytes" yaml:"mem_fragmentation_bytes"` // 内存碎片大小(字节表示)
MemNotCountedForEvict int64 `json:"mem_not_counted_for_evict" yaml:"mem_not_counted_for_evict"` // 不应驱逐的内存大小,以字节为单位
MemReplicationBacklog int64 `json:"mem_replication_backlog" yaml:"mem_replication_backlog"` // 复制backlog的内存大小, 以字节为单位
MemClientsSlaves int64 `json:"mem_clients_slaves" yaml:"mem_clients_slaves"` // mem_clients_slaves
MemClientsNormal int64 `json:"mem_clients_normal" yaml:"mem_clients_normal"` // mem_clients_normal
MemAofBuffer int64 `json:"mem_aof_buffer" yaml:"mem_aof_buffer"` // AOF内存缓冲区大小
MemAllocator string `json:"mem_allocator" yaml:"mem_allocator"` // 内存分配器Redis支持glibcs malloc、jemalloc11、tcmalloc几种不同的内存分配器每个分配器在内存分配和碎片上都有不同的实现。不建议普通管理员修改Redis默认内存分配器因为这需要完全理解这几种内存分配器的差异也要重新编译Redis。
ActiveDefragRunning int64 `json:"active_defrag_running" yaml:"active_defrag_running"` // defrag:表示内存碎片整理, 0表示没有活动的defrag任务正在运行1表示有活动的defrag任务正在运行
LazyfreePendingObjects int64 `json:"lazyfree_pending_objects" yaml:"lazyfree_pending_objects"` // 延迟释放的挂起对象, 0表示不存在
LazyfreedObjects int64 `json:"lazyfreed_objects" yaml:"lazyfreed_objects"` // 延迟释放的对象数量
}
// Persistence 持久化信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:15 下午 2021/11/26
type Persistence struct {
Loading int `json:"loading" yaml:"loading"` // 服务器是否正在进行持久化 0 - 否 1 -是
CurrentCowSize int `json:"current_cow_size" yaml:"current_cow_size"` // current_cow_size
CurrentCowSizeAge int `json:"current_cow_size_age" yaml:"current_cow_size_age"` // current_cow_size_age
CurrentForkPerc float64 `json:"current_fork_perc" yaml:"current_fork_perc"` // current_fork_perc
CurrentSaveKeysProcessed int `json:"current_save_keys_processed" yaml:"current_save_keys_processed"` // current_save_keys_processed
CurrentSaveKeysTotal int64 `json:"current_save_keys_total" yaml:"current_save_keys_total"` // current_save_keys_total
RdbChangesSinceLastSave int64 `json:"rdb_changes_since_last_save" yaml:"rdb_changes_since_last_save"` // 离最近一次成功生成rdb文件写入命令的个数即有多少个写入命令没有持久化
RdbBgsaveInProgress int `json:"rdb_bgsave_in_progress" yaml:"rdb_bgsave_in_progress"` // 服务器是否正在创建rdb文件 0 - 否 1 - 是
RdbLastSaveTime int64 `json:"rdb_last_save_time" yaml:"rdb_last_save_time"` // 最近一次创建rdb文件的时间戳,单位秒
RdbLastBgsaveStatus string `json:"rdb_last_bgsave_status" yaml:"rdb_last_bgsave_status"` // 最近一次rdb持久化是否成功 ok 成功
RdbLastBgsaveTimeSec int64 `json:"rdb_last_bgsave_time_sec" yaml:"rdb_last_bgsave_time_sec"` // 最近一次成功生成rdb文件耗时秒数
RdbCurrentBgsaveTimeSec int64 `json:"rdb_current_bgsave_time_sec" yaml:"rdb_current_bgsave_time_sec"` // 如果服务器正在创建rdb文件那么这个字段记录的就是当前的创建操作已经耗费的秒数
RdbLastCowSize int64 `json:"rdb_last_cow_size" yaml:"rdb_last_cow_size"` // RDB过程中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等)。
AofEnabled int `json:"aof_enabled" yaml:"aof_enabled"` // 是否开启了AOF 0 - 否 1 - 是
AofRewriteInProgress int `json:"aof_rewrite_in_progress" yaml:"aof_rewrite_in_progress"` // 标识aof的rewrite操作是否在进行中 0 - 否 1- 是
AofRewriteScheduled int `json:"aof_rewrite_scheduled" yaml:"aof_rewrite_scheduled"` // rewrite任务计划当客户端发送bgrewriteaof指令如果当前rewrite子进程正在执行那么将客户端请求的bgrewriteaof变为计划任务待aof子进程结束后执行rewrite
AofLastRewriteTimeSec int `json:"aof_last_rewrite_time_sec" yaml:"aof_last_rewrite_time_sec"` // 最近一次aof rewrite耗费的时长
AofCurrentRewriteTimeSec int `json:"aof_current_rewrite_time_sec" yaml:"aof_current_rewrite_time_sec"` // 如果rewrite操作正在进行则记录所使用的时间单位秒
AofLastBgrewriteStatus string `json:"aof_last_bgrewrite_status" yaml:"aof_last_bgrewrite_status"` // 上次 bgrewrite aof 操作的状态 ok 成功
AofLastWriteStatus string `json:"aof_last_write_status" yaml:"aof_last_write_status"` // 上次aof写入状态
AofLastCowSize int64 `json:"aof_last_cow_size" yaml:"aof_last_cow_size"` // AOF过程中父进程与子进程相比执行了多少修改(包括读缓冲区,写缓冲区,数据修改等)
IOThreadedReadsProcessed int `json:"io_threaded_reads_processed" yaml:"io_threaded_reads_processed"` // 读取线程数
IOThreadedWritesProcessed int `json:"io_threaded_writes_processed" yaml:"io_threaded_writes_processed"` // 写入线程数
}
// Stats 服务运行状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:57 下午 2021/11/26
type Stats struct {
TotalConnectionsReceived int64 `json:"total_connections_received" yaml:"total_connections_received"` // 所有连接数, 累积值, 只增不减, 连接断开也不会减少
TotalCommandsProcessed int64 `json:"total_commands_processed" yaml:"total_commands_processed"` // 服务器执行的命令数 累积值, 只增不减
InstantaneousPpsPerSec int `json:"instantaneous_pps_per_sec" yaml:"instantaneous_pps_per_sec"` // 每秒执行的命令数
TotalNetInputBytes int64 `json:"total_net_input_bytes" yaml:"total_net_input_bytes"` // 网络流量-流入 以字节byte为单位
TotalNetOutputBytes int64 `json:"total_net_output_bytes" yaml:"total_net_output_bytes"` // 网络流量-流出 以字节byte为单位
InstantaneousInputKbps float64 `json:"instantaneous_input_kbps" yaml:"instantaneous_input_kbps"` // 网络流量-流入-KB/s
InstantaneousOutputKbps float64 `json:"instantaneous_output_kbps" yaml:"instantaneous_output_kbps"` // 网络流量-流出-KB/s
RejectedConnections int64 `json:"rejected_connections" yaml:"rejected_connections"` // 因达到最大连接数而被拒绝的连接数量
SyncFull int64 `json:"sync_full" yaml:"sync_full"` // 主从全量同步的次数
SyncPartialOk int64 `json:"sync_partial_ok" yaml:"sync_partial_ok"` // 主从部分同步成功的次数
SyncPartialErr int64 `json:"sync_partial_err" yaml:"sync_partial_err"` // 主从部分同步失败次数
ExpiredKeys int `json:"expired_keys" yaml:"expired_keys"` // 过期key的数量
ExpiredStalePerc float64 `json:"expired_stale_perc" yaml:"expired_stale_perc"` // 过期过时的百分比
ExpiredTimeCapReachedCount int64 `json:"expired_time_cap_reached_count" yaml:"expired_time_cap_reached_count"` // 过期时间达到上限的数量
ExpireCycleCpuMilliseconds int64 `json:"expire_cycle_cpu_milliseconds" yaml:"expire_cycle_cpu_milliseconds"` // 过期循环CPU毫秒数
EvictedKeys int64 `json:"evicted_keys" yaml:"evicted_keys"` // 超过 maxmemory 之后, 剔除的 key 的数量
KeyspaceHits int64 `json:"keyspace_hits" yaml:"keyspace_hits"` // 访问命中次数
KeyspaceMisses int64 `json:"keyspace_misses" yaml:"keyspace_misses"` // 访问未命中次数
PubsubChannels int64 `json:"pubsub_channels" yaml:"pubsub_channels"` // 当前频道数量 发布 - 订阅 模式
PubsubPatterns int64 `json:"pubsub_patterns" yaml:"pubsub_patterns"` // 当前使用中的模式数量
LatestForkUsec int64 `json:"latest_fork_usec" yaml:"latest_fork_usec"` // 最近一次fork 操作消耗的时间, 单位微秒
TotalForks int64 `json:"total_forks" yaml:"total_forks"` // fork 的总次数
MigrateCachedSockets int64 `json:"migrate_cached_sockets" yaml:"migrate_cached_sockets"` // 记录当前 Redis 正在 migrate 操作的目标 Redis 个数, 例如 A 向 B 和 C 执行 migrate操作, 这个值为2
SlaveExpiresTrackedKeys int64 `json:"slave_expires_tracked_keys" yaml:"slave_expires_tracked_keys"` // 从实例到期的 key 的数量
ActiveDefragHits int64 `json:"active_defrag_hits" yaml:"active_defrag_hits"` // 主动碎片整理命中次数
ActiveDefragMisses int64 `json:"active_defrag_misses" yaml:"active_defrag_misses"` // 主动碎片整理未命中次数
ActiveDefragKeyHits int64 `json:"active_defrag_key_hits" yaml:"active_defrag_key_hits"` // 主动整理碎片, key命中次数
ActiveDefragKeyMisses int64 `json:"active_defrag_key_misses" yaml:"active_defrag_key_misses"` // 主动整理碎片, key未命中次数
TrackingTotalKeys int64 `json:"tracking_total_keys" yaml:"tracking_total_keys"` // key 查询的总数
TrackingTotalItems int64 `json:"tracking_total_items" yaml:"tracking_total_items"` // item查询的总数
TrackingTotalPrefixes int64 `json:"tracking_total_prefixes" yaml:"tracking_total_prefixes"` // 前缀查询的总数
UnexpectedErrorReplies int64 `json:"unexpected_error_replies" yaml:"unexpected_error_replies"` // unexpected 异常响应次数
TotalErrorReplies int64 `json:"total_error_replies" yaml:"total_error_replies"` // 异常响应总次数
DumpPayloadSanitizations int64 `json:"dump_payload_sanitizations" yaml:"dump_payload_sanitizations"` // dump_payload_sanitizations
TotalReadsProcessed int64 `json:"total_reads_processed" yaml:"total_reads_processed"` // 正在读取的请求数
TotalWritesProcessed int64 `json:"total_writes_processed" yaml:"total_writes_processed"` // 正在写入的请求数
IOThreadedReadsProcessed int64 `json:"io_threaded_reads_processed" yaml:"io_threaded_reads_processed"` // 正在读取的线程数
IOThreadedWritesProcessed int64 `json:"io_threaded_writes_processed" yaml:"io_threaded_writes_processed"` // 正在写入的线程数
}
// Replication 复制相关
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:42 上午 2021/11/27
type Replication struct {
Role string `json:"role" yaml:"role"` // 节点的角色 master / slave
ReplBacklogActive int `json:"repl_backlog_active" yaml:"repl_backlog_active"` // 复制缓冲区是否开启 0 - 未开启 1 - 已开启
ReplBacklogSize int64 `json:"repl_backlog_size" yaml:"repl_backlog_size"` // 复制缓冲区大小(以字节为单位)
ReplBacklogFirstByteOffset int64 `json:"repl_backlog_first_byte_offset" yaml:"repl_backlog_first_byte_offset"` // 复制缓冲区里偏移量的大小
ReplBacklogHistlen int64 `json:"repl_backlog_histlen" yaml:"repl_backlog_histlen"` // 此值等于 master_repl_offset - repl_backlog_first_byte_offset,该值不会超过repl_backlog_size的大小
ConnectedSlaves int `json:"connected_slaves" yaml:"connected_slaves"` // 仅主节点属性 : 连接的从节点数量
MasterFailoverState string `json:"master_failover_state" yaml:"master_failover_state"` // 仅主节点 : 故障转移状态 no-failover 无故障转移
MasterReplid string `json:"master_replid" yaml:"master_replid"` // 仅主节点 : 实例启动的随机字符串
MasterReplid2 string `json:"master_replid2" yaml:"master_replid2"` // 仅主节点 : 实例启动的随机字符串2
MasterReplOffset int64 `json:"master_repl_offset" yaml:"master_repl_offset"` // 仅主节点 : 主从同步偏移量
SecondReplOffset int64 `json:"second_repl_offset" yaml:"second_repl_offset"` // 仅主节点 : 主从同步偏移量2
MasterHost string `json:"master_host" yaml:"master_host"` // 仅从节点 : 主节点host
MasterPort int `json:"master_port" yaml:"master_port"` // 仅从节点 : 主节点端口
MasterLinkStatus string `json:"master_link_status" yaml:"master_link_status"` // 仅从节点 : 与主节点连接状态 up - 正常连接 down - 断开
MasterLastIOSecondsAgo int `json:"master_last_io_seconds_ago" yaml:"master_last_io_seconds_ago"` // 仅从节点 : 主节点与从节点最后通信的时间间隔, 单位: s
MasterSyncInProgress int `json:"master_sync_in_progress" yaml:"master_sync_in_progress"` // 仅从节点 : 从节点是否正在全量同步主节点rdb文件 0 - 否 1 - 是
SlaveReplOffset int64 `json:"slave_repl_offset" yaml:"slave_repl_offset"` // 仅从节点 : 复制偏移量
SlavePriority int `json:"slave_priority" yaml:"slave_priority"` // 仅从节点 : 从节点优先级
SlaveReadOnly int `json:"slave_read_only" yaml:"slave_read_only"` // 仅从节点 : 从节点是否只读 0 - 否 1 - 是
SlaveList []SlaveNode `json:"slave_list" yaml:"slave_list"` // 从节点列表
}
// SlaveNode 从库数据结构,基于原始数据解析 eg : slave0:ip=x.x.x.x,port=6379,state=online,offset=123456,lag=1
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:51 上午 2021/11/27
type SlaveNode struct {
ID string `json:"id" yaml:"id"` // 从库ID
IP string `json:"ip" yaml:"ip"` // 从库IP
Port int `json:"port" yaml:"port"` // 从库端口
State string `json:"state" yaml:"state"` // 从库状态 online - 在线 offline - 离线
Offset int64 `json:"offset" yaml:"offset"` // 数据偏移量
Lag int64 `json:"lag" yaml:"lag"` // 数据延迟量大小
}
// CPU 信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:54 上午 2021/11/27
type CPU struct {
UsedCpuSys float64 `json:"used_cpu_sys" yaml:"used_cpu_sys"` // Redis主进程在内核态所占用CPU时钟总和
UsedCpuUser float64 `json:"used_cpu_user" yaml:"used_cpu_user"` // Redis主进程在用户态所占用CPU时钟总和
UsedCpuSysChildren float64 `json:"used_cpu_sys_children" yaml:"used_cpu_sys_children"` // Redis子进程在内核态所占用CPU时钟总和
UsedCpuUserChildren float64 `json:"used_cpu_user_children" yaml:"used_cpu_user_children"` // Redis子进程在用户态所占用CPU时钟总和
}
// CmdStat 指令状态 eg : cmdstat_get:calls=1,usec=42121,usec_per_call=42121.00,rejected_calls=0,failed_calls=0
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:02 上午 2021/11/27
type CmdStat struct {
Cmd string `json:"cmd" yaml:"cmd"` // 执行的指令
Calls int64 `json:"calls" yaml:"calls"` // 执行了多少次
TotalUsedTime float64 `json:"total_used_time" yaml:"total_used_time"` // 累计总耗时 微秒
AvgUsedTime float64 `json:"avg_used_time" yaml:"avg_used_time"` // 平均耗时 微秒
RejectedCalls int64 `json:"rejected_calls" yaml:"rejected_calls"` // 拒绝执行指令次数
FailedCalls int64 `json:"failed_calls" yaml:"failed_calls"` // 指令执行失败次数
SuccessCalls int64 `json:"success_calls" yaml:"success_calls"` // 指令执行成功次数 Calls - FailedCalls
}
// DB 数据库的数据结构, eg : db0:keys=3,expires=0,avg_ttl=0
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:09 上午 2021/11/27
type DB struct {
Name string `json:"name" yaml:"name"` // 数据库名称
Keys int64 `json:"keys" yaml:"keys"` // 数据库key的数量
Expires int64 `json:"expires" yaml:"expires"` // 数据库过期key的数量
AvgTTL int64 `json:"avg_ttl" yaml:"avg_ttl"` // 平均存活时间
}
// Cluster ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:13 上午 2021/11/27
type Cluster struct {
ClusterEnabled int `json:"cluster_enabled" yaml:"cluster_enabled"` // 是否启用 0 - 否 1 - 是
}
// Error 错误的状态 eg : errorstat_WRONGTYPE:count=4615
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:16 上午 2021/11/27
type Error struct {
Type string `json:"type" yaml:"type"` // 错误类型
Count int64 `json:"count" yaml:"count"` // 错误出现次数
}

@ -1,326 +0,0 @@
// Package redis ...
//
// Description : redis 系统信息监控
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-27 12:22 下午
package redis
import (
"context"
"strings"
"git.zhangdeman.cn/zhangdeman/gopkg/convert"
yml "gopkg.in/yaml.v2"
"github.com/go-redis/redis/v8"
)
// GetRedisServerInfo 获取 redis server info
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:24 下午 2021/11/27
func GetRedisServerInfo(client *redis.Client) (*ServerInfo, error) {
var result ServerInfo
if err := infoToStruct(client, "server", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetMemoryInfo 获取内存信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:59 下午 2021/11/27
func GetMemoryInfo(client *redis.Client) (*MemoryInfo, error) {
var result MemoryInfo
if err := infoToStruct(client, "memory", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetClientInfo 获取客户端信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:10 下午 2021/11/27
func GetClientInfo(client *redis.Client) (*ClientInfo, error) {
var result ClientInfo
if err := infoToStruct(client, "clients", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetPersistence 获取持久化相关信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:11 下午 2021/11/27
func GetPersistence(client *redis.Client) (*Persistence, error) {
var result Persistence
if err := infoToStruct(client, "persistence", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetStats 获取状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:19 下午 2021/11/27
func GetStats(client *redis.Client) (*Stats, error) {
var result Stats
if err := infoToStruct(client, "stats", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetReplication 复制相关信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:23 下午 2021/11/27
func GetReplication(client *redis.Client) (*Replication, error) {
var result Replication
if err := infoToStruct(client, "replication", &result); nil != err {
return nil, err
}
result.SlaveList = GetSlaveList(client)
return &result, nil
}
// GetSlaveList 获取从库信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:26 下午 2021/11/27
func GetSlaveList(client *redis.Client) []SlaveNode {
// 解析从库信息
slaveList := make([]SlaveNode, 0)
var data map[string]string
_ = infoToStruct(client, "replication", &data)
slaveKey := []string{"slave0", "slave1", "slave2", "slave3", "slave4", "slave5", "slave6", "slave7", "slave8", "slave9"}
for k, v := range data {
isSlave := false
for _, item := range slaveKey {
if strings.Contains(k, item) {
isSlave = true
break
}
}
if !isSlave {
continue
}
vArr := strings.Split(v, ",")
if len(vArr) < 3 {
continue
}
slave := SlaveNode{
ID: k,
IP: "",
Port: 0,
State: "",
Offset: 0,
Lag: 0,
}
for _, prop := range vArr {
propArr := strings.Split(prop, "=")
if len(propArr) != 2 {
continue
}
switch strings.ToLower(propArr[0]) {
case "ip":
slave.IP = propArr[1]
case "port":
_ = convert.ConvertAssign(&slave.Port, propArr[1])
case "state":
slave.State = propArr[1]
case "offset":
_ = convert.ConvertAssign(&slave.Offset, propArr[1])
case "lag":
_ = convert.ConvertAssign(&slave.Lag, propArr[1])
}
}
slaveList = append(slaveList, slave)
}
return slaveList
}
// GetCPUInfo 获取cpu信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:24 下午 2021/11/27
func GetCPUInfo(client *redis.Client) (*CPU, error) {
var result CPU
if err := infoToStruct(client, "cpu", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetCommandStats 命令状态
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:27 下午 2021/11/27
func GetCommandStats(client *redis.Client) (*Stats, error) {
var result Stats
if err := infoToStruct(client, "stats", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetCommandInfo 获取命令信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 1:32 下午 2021/11/27
func GetCommandInfo(client *redis.Client) ([]CmdStat, error) {
var result map[string]string
if err := infoToStruct(client, "commandstats", &result); nil != err {
return make([]CmdStat, 0), err
}
cmdList := make([]CmdStat, 0)
// 解析数据
for cmd, detail := range result {
infoArr := strings.Split(detail, ",")
if len(infoArr) < 2 {
// 去掉开始的描述信息
continue
}
info := CmdStat{
Cmd: strings.ReplaceAll(strings.ToLower(cmd), "cmdstat_", ""),
Calls: 0,
TotalUsedTime: 0,
AvgUsedTime: 0,
RejectedCalls: 0,
FailedCalls: 0,
SuccessCalls: 0,
}
for _, item := range infoArr {
itemArr := strings.Split(item, "=")
if len(itemArr) != 2 {
continue
}
switch strings.ToLower(itemArr[0]) {
case "calls":
_ = convert.ConvertAssign(&info.Calls, itemArr[1])
case "usec":
_ = convert.ConvertAssign(&info.TotalUsedTime, itemArr[1])
case "usec_per_call":
_ = convert.ConvertAssign(&info.AvgUsedTime, itemArr[1])
case "rejected_calls":
_ = convert.ConvertAssign(&info.RejectedCalls, itemArr[1])
case "failed_calls":
_ = convert.ConvertAssign(&info.FailedCalls, itemArr[1])
}
}
info.SuccessCalls = info.Calls - info.FailedCalls - info.RejectedCalls
cmdList = append(cmdList, info)
}
return cmdList, nil
}
// GetKeyspace 获取 keyspace 信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:44 下午 2021/11/27
func GetKeyspace(client *redis.Client) ([]DB, error) {
var result map[string]string
if err := infoToStruct(client, "keyspace", &result); nil != err {
return make([]DB, 0), err
}
dbList := make([]DB, 0)
for dbName, item := range result {
itemArr := strings.Split(item, ",")
if len(itemArr) < 3 {
continue
}
dbInfo := DB{
Name: dbName,
Keys: 0,
Expires: 0,
AvgTTL: 0,
}
for _, kv := range itemArr {
kvArr := strings.Split(kv, "=")
if len(kvArr) != 2 {
continue
}
switch strings.ToLower(kvArr[0]) {
case "keys":
_ = convert.ConvertAssign(&dbInfo.Keys, kvArr[1])
case "expires":
_ = convert.ConvertAssign(&dbInfo.Expires, kvArr[1])
case "avg_ttl":
_ = convert.ConvertAssign(&dbInfo.AvgTTL, kvArr[1])
}
}
dbList = append(dbList, dbInfo)
}
return dbList, nil
}
// GetCluster 获取 cluster 信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:30 下午 2021/11/27
func GetCluster(client *redis.Client) (*Cluster, error) {
var result Cluster
if err := infoToStruct(client, "cluster", &result); nil != err {
return nil, err
}
return &result, nil
}
// GetErrorStats 获取错误状态信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:35 下午 2021/11/27
func GetErrorStats(client *redis.Client) ([]Error, error) {
var result map[string]string
if err := infoToStruct(client, "errorstats", &result); nil != err {
return make([]Error, 0), err
}
errList := make([]Error, 0)
for errType, item := range result {
itemArr := strings.Split(item, "=")
if len(itemArr) != 2 {
continue
}
errInfo := Error{
Type: strings.ReplaceAll(errType, "errorstat_", ""),
Count: 0,
}
_ = convert.ConvertAssign(&errInfo.Count, itemArr[1])
errList = append(errList, errInfo)
}
return errList, nil
}
// infoToStruct 读取到的数据,解析到结构体
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:28 下午 2021/11/27
func infoToStruct(client *redis.Client, module string, receiver interface{}) error {
data := client.Info(context.Background(), module).String()
// yaml 文件规范要求, key: val , 注意 : 的后面有空格
data = strings.ReplaceAll(data, ":", ": ")
return yml.Unmarshal([]byte(data), receiver)
}

@ -42,12 +42,6 @@ func TestCommandProxy(t *testing.T) {
panic(err.Error())
}
r, cmdErr := instance.CommandProxy(nil, "test_redis", "set", "command_proxy", "hello world")
c, _ := instance.GetRedisClient("test_redis")
fmt.Println(GetRedisServerInfo(c.Instance))
fmt.Println(GetCommandInfo(c.Instance))
fmt.Println(GetKeyspace(c.Instance))
fmt.Println(GetCluster(c.Instance))
fmt.Println(GetErrorStats(c.Instance))
assert.Nil(t, cmdErr, "命令执行成功")
assert.Equal(t, "OK", fmt.Sprintf("%v", r))
}

@ -1,8 +0,0 @@
// Package tile38 ...
//
// Description : tile38 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-18 3:45 下午
package tile38

@ -1,29 +0,0 @@
// Package oauth ...
//
// Description : oauth ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022/01/17 12:20 AM
package oauth
// Config OAuth相关配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:20 AM
type Config struct {
ClientID string `json:"client_id"` // 授权的client id
ClientSecret string `json:"client_secret"` // 授权的client_secret
CallbackURL string `json:"callback_url"` // 授权后携带code回调的地址
Domain string `json:"domain"` // 服务器域名
AuthorizationPageURI string `json:"authorization_page_uri"` // 授权页面URI
GetAccessTokenURI string `json:"get_access_token_uri"` // 获取access token地址
ValidateAccessTokenURI string `json:"validate_access_token_uri"` // 验证access_token地址
AccessTokenKey string `json:"access_token_key"` // 验证access token时的key
UseAuthorization bool `json:"use_authorization"` // 是否使用header中的Authorization
FormatAccessTokenFunc FormatAccessTokenFunc `json:"format_access_token_func"` // 格式化token的函数
}
// FormatAccessTokenFunc 格式化token
type FormatAccessTokenFunc func(token string) string

@ -1,128 +0,0 @@
// Package oauth ...
//
// Description : oauth ...
//
// Author : go_developer@163.com<张德满>
//
// Date : 2022/01/17 12:18 AM
package oauth
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"github.com/pkg/errors"
"github.com/ddliu/go-httpclient"
)
// NewOAuth 获取OAuth实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:19 AM
func NewOAuth(cfg Config) *OAuth {
return &OAuth{
cfg: cfg,
}
}
// OAuth 管理
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:19 AM
type OAuth struct {
cfg Config
}
// GetAuthorizationPageURL 获取授权页面的URL
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:33 AM
func (o *OAuth) GetAuthorizationPageURL(scope string, responseType string) string {
return fmt.Sprintf(
"%s%s?client_id=%s&redirect_uri=%s&scope=%s&response_type=%s",
o.cfg.Domain,
o.cfg.AuthorizationPageURI,
o.cfg.ClientID,
url.QueryEscape(o.cfg.CallbackURL),
scope,
responseType,
)
}
// ApplyAccessTokenByCode 通过code申请授权token
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:35 AM
func (o *OAuth) ApplyAccessTokenByCode(code string, receiver interface{}) error {
// OAuth 授权回调
parameter := map[string]string{
"code": code,
"client_id": o.cfg.ClientID,
"client_secret": o.cfg.ClientSecret,
"grant_type": "authorization_code",
}
client := httpclient.NewHttpClient()
client.WithHeader("Accept", "application/json")
resp, err := client.Post(o.cfg.Domain+o.cfg.GetAccessTokenURI+"?grant_type=authorization_code", parameter)
if nil != err {
return err
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("【%v】%v", resp.StatusCode, resp.Status)
}
body, err := io.ReadAll(resp.Body)
if nil != err {
return errors.New("读取响应体失败: " + err.Error())
}
decoder := json.NewDecoder(bytes.NewReader(body))
decoder.UseNumber()
return decoder.Decode(receiver)
}
// ValidateAccessToken 验证授权token
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:43 AM
func (o *OAuth) ValidateAccessToken(token string, receiver interface{}) error {
if nil != o.cfg.FormatAccessTokenFunc {
token = o.cfg.FormatAccessTokenFunc(token)
}
parameter := make(map[string]string)
client := httpclient.NewHttpClient()
client.WithHeader("Accept", "application/json")
if o.cfg.UseAuthorization {
client.WithHeader("Authorization", "token "+token)
} else {
parameter[o.cfg.AccessTokenKey] = token
}
resp, err := client.Get("https://git.zhangdeman.cn/api/v1/user?grant_type=authorization_code", parameter)
if nil != err {
return err
}
body, err := io.ReadAll(resp.Body)
if nil != err {
return errors.New("读取响应体失败 : " + err.Error())
}
decoder := json.NewDecoder(bytes.NewReader(body))
decoder.UseNumber()
return decoder.Decode(receiver)
}
// GetUserDetailByToken 根据 token 获取用户详情
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2022/1/17 12:55 AM
func (o *OAuth) GetUserDetailByToken(token string, receiver interface{}) error {
return o.ValidateAccessToken(token, receiver)
}

@ -8,11 +8,13 @@
package proxy
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net"
"net/http"
"net/http/httputil"
"strings"
)
@ -22,30 +24,72 @@ import (
//
// Date : 2:08 下午 2021/8/6
func Forward(rw http.ResponseWriter, req *http.Request, serverConfig *Server) {
fmt.Printf("Received request %s %s %s\n", req.Method, req.Host, req.RemoteAddr)
if !strings.HasPrefix(serverConfig.URI, "/") {
serverConfig.URI = "/" + serverConfig.URI
transport := http.DefaultTransport
// step 1
outReq := new(http.Request)
*outReq = *req // this only does shallow copies of maps
if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
if prior, ok := outReq.Header["X-Forwarded-For"]; ok {
clientIP = strings.Join(prior, ", ") + ", " + clientIP
}
outReq.Header.Set("X-Forwarded-For", clientIP)
}
// 请求重写方法
director := func(req *http.Request) {
req.URL.Scheme = serverConfig.Scheme
// req.URL.Host = projectDetail.GetProjectDetail().Domain + ":" + fmt.Sprintf("%v", projectDetail.GetProjectDetail().Port)
// req.Host = projectDetail.GetProjectDetail().Domain + ":" + fmt.Sprintf("%v", projectDetail.GetProjectDetail().Port)
req.Host = serverConfig.Host
req.URL.Host = serverConfig.Host
req.URL.Path = serverConfig.URI
req.RequestURI = serverConfig.URI
// 写入重写的请求Header
for k, v := range serverConfig.RewriteRequestHeader {
req.Header.Set(k, v)
// 写入重写的请求Header
for k, v := range serverConfig.RewriteRequestHeader {
outReq.Header.Set(k, v)
}
// 重写请求地址
outReq.Host = serverConfig.Host
outReq.URL.Path = serverConfig.URI
outReq.URL.Scheme = serverConfig.Scheme
outReq.URL.Host = serverConfig.Host
// step 2
res, err := transport.RoundTrip(outReq)
if err != nil {
rw.WriteHeader(http.StatusBadGateway)
return
}
// step 3
for key, value := range res.Header {
for _, v := range value {
if strings.ToLower(key) == "content-encoding" {
continue
}
rw.Header().Add(key, v)
}
}
// TODO : 重写响应数据
modifyResponseFunc := func(rep *http.Response) error {
return nil
rw.WriteHeader(res.StatusCode)
// 重写请求header
for k, v := range serverConfig.RewriteResponseHeader {
rw.Header().Set(k, v)
}
p := &httputil.ReverseProxy{Director: director, ModifyResponse: modifyResponseFunc}
p.ServeHTTP(rw, req)
defer res.Body.Close()
// 重写响应数据
if !strings.Contains(strings.ToLower(res.Header.Get("Content-Type")), "application/json") || nil == serverConfig.RewriteResponseData || len(serverConfig.RewriteResponseData) == 0 {
_, _ = io.Copy(rw, res.Body)
return
}
var (
responseData []byte
)
responseData, err = getResponseData(res)
fmt.Println(string(responseData), err)
bytesBuffer := bytes.NewReader([]byte(`{"data":{"permission":true}}`))
_, _ = io.Copy(rw, bytesBuffer)
}
// getResultCompressType 获取返回结果的压缩方式

@ -1,25 +0,0 @@
// Package system...
//
// Description : system...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-12 5:58 下午
package system
import (
"encoding/json"
"fmt"
"testing"
)
// TestGetCPUInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:58 下午 2021/10/12
func TestGetCPUInfo(t *testing.T) {
r, _ := GetCPUInfo()
byteData, _ := json.Marshal(r)
fmt.Println(string(byteData))
}

@ -1,25 +0,0 @@
// Package system...
//
// Description : system...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-12 9:04 下午
package system
import (
"encoding/json"
"fmt"
"testing"
)
// TestGetDiskInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:04 下午 2021/10/12
func TestGetDiskInfo(t *testing.T) {
r, _ := GetDiskInfo()
byteData, _ := json.Marshal(r)
fmt.Println(string(byteData))
}

@ -1,25 +0,0 @@
// Package system...
//
// Description : system...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-12 7:58 下午
package system
import (
"encoding/json"
"fmt"
"testing"
)
// TestGetHostInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:59 下午 2021/10/12
func TestGetHostInfo(t *testing.T) {
r, _ := GetHostInfo()
byteData, _ := json.Marshal(r)
fmt.Println(string(byteData))
}

@ -1,25 +0,0 @@
// Package system...
//
// Description : system...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-12 7:09 下午
package system
import (
"encoding/json"
"fmt"
"testing"
)
// TestGetMemoryInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 7:09 下午 2021/10/12
func TestGetMemoryInfo(t *testing.T) {
r, _ := GetMemoryInfo()
byteData, _ := json.Marshal(r)
fmt.Println(string(byteData))
}

@ -1,185 +0,0 @@
// Package json2go ...
//
// Description : json2go ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-09 3:44 下午
package json2go
import (
"fmt"
"strings"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
"github.com/pkg/errors"
"github.com/tidwall/gjson"
)
var (
// structName 结构体名称
structName = "Automatic"
)
// NewJSON2GO ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:13 下午 2021/11/9
func NewJSON2GO(name string) *JSON2GO {
if len(name) == 0 {
name = structName
}
return &JSON2GO{
structName: name,
}
}
// JSON2GO ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:12 下午 2021/11/9
type JSON2GO struct {
structName string
result string
}
// Parse ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:44 下午 2021/11/9
func (jg *JSON2GO) Parse(inputJSON string) (string, error) {
if !gjson.Valid(inputJSON) {
return "", errors.New("input json is invalid")
}
parseResult := gjson.Parse(inputJSON)
if parseResult.IsArray() {
jg.append("type " + jg.structName + " [] \n")
jg.parseArray("", parseResult)
} else {
jg.append("type " + jg.structName + " struct { \n")
jg.parseObject("", parseResult)
}
jg.append("}")
return jg.result, nil
}
// parseArray 解析array
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:37 下午 2021/11/9
func (jg *JSON2GO) parseArray(key string, parseResult gjson.Result) {
// 先遍历一遍确认所有数据类型都相同
dataType := ""
if len(parseResult.Array()) > 0 {
for _, item := range parseResult.Array() {
if len(dataType) == 0 {
dataType = jg.getDataType(item)
continue
}
currentType := jg.getDataType(item)
if currentType != dataType {
if (dataType == "int64" && currentType == "float64") || (dataType == "float64" && currentType == "int64") {
dataType = "float64"
continue
}
// 不是所有数据类型都一致
if len(key) == 0 {
jg.result += "interface{}"
return
}
jg.append(jg.buildField("[]interface{}", key))
return
}
}
} else {
dataType = "interface{}"
}
// 对象,重新
if dataType == "object" {
instance := NewJSON2GO("")
r, _ := instance.Parse(parseResult.Array()[0].String())
dataType = strings.Replace(strings.Replace(r, "type", "", 1), "Automatic", "", 1)
}
// 所有数据类型都一致
if len(key) == 0 {
jg.append("[]" + dataType)
} else {
jg.append(jg.buildField("[]"+dataType, key))
}
}
// parseObject 解析object
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:37 下午 2021/11/9
func (jg *JSON2GO) parseObject(key string, parseResult gjson.Result) string {
if len(key) > 0 {
jg.result += util.SnakeCaseToCamel(key) + " struct { \n"
}
for k, v := range parseResult.Map() {
if v.IsObject() {
jg.parseObject(k, v)
} else if v.IsArray() {
jg.parseArray(k, v)
} else {
jg.append(jg.buildField(jg.getDataType(v), k))
}
}
if len(key) > 0 {
jg.append(" } `json:\"" + key + "\"`\n")
}
return ""
}
// getDataType 获取数据类型
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:14 下午 2021/11/9
func (jg *JSON2GO) getDataType(val gjson.Result) string {
switch val.Type {
case gjson.True:
fallthrough
case gjson.False:
return "bool"
case gjson.Null:
return "map[string]interface{}"
case gjson.String:
return "string"
case gjson.Number:
tmpVal := fmt.Sprintf("%v", val.Num)
if strings.Contains(tmpVal, ".") {
return "float64"
}
return "int64"
case gjson.JSON:
return "object"
default:
return "interface{}"
}
}
// buildField 构建字段
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:20 下午 2021/11/9
func (jq *JSON2GO) buildField(fieldType string, jsonTag string) string {
return util.SnakeCaseToCamel(jsonTag) + " " + fieldType + " `json:\"" + jsonTag + "\"`\n"
}
// append 构建result
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 9:19 下午 2021/11/9
func (jq *JSON2GO) append(appendStr string) {
jq.result += appendStr
}

@ -1,301 +0,0 @@
// Package json2go...
//
// Description : json2go...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-09 4:21 下午
package json2go
import (
"fmt"
"testing"
)
func TestParse(t *testing.T) {
testJSON := `{
"name": "zhang",
"test": {
"name": "aa",
"age": 18,
"third": {"a":"1", "b":"like"},
"result": true,
"result_fail":false
},
"list": [1,2,3,4],
"list1": [2.2, 3, 4, 5],
"list_object":[{"name":"zhang"}, {"age": 18}]
}`
testJSON = `{
"ref": "refs/heads/master",
"before": "4c8219477db1e93e5540c9af4889f2633a9ca9e9",
"after": "5888c985a9afd9f1b25807fade69c53938aef485",
"compare_url": "https://git.zhangdeman.cn/zhangdeman/manager-job/compare/4c8219477db1e93e5540c9af4889f2633a9ca9e9...5888c985a9afd9f1b25807fade69c53938aef485",
"commits": [
{
"id": "5888c985a9afd9f1b25807fade69c53938aef485",
"message": "更新文案\n",
"url": "https://git.zhangdeman.cn/zhangdeman/manager-job/commit/5888c985a9afd9f1b25807fade69c53938aef485",
"author": {
"name": "白茶清欢",
"email": "go_developer@163.com",
"username": "zhangdeman"
},
"committer": {
"name": "白茶清欢",
"email": "go_developer@163.com",
"username": "zhangdeman"
},
"verification": null,
"timestamp": "2021-11-11T17:54:40+08:00",
"added": [],
"removed": [],
"modified": [
"publish_admin.sh"
]
}
],
"head_commit": {
"id": "5888c985a9afd9f1b25807fade69c53938aef485",
"message": "更新文案\n",
"url": "https://git.zhangdeman.cn/zhangdeman/manager-job/commit/5888c985a9afd9f1b25807fade69c53938aef485",
"author": {
"name": "白茶清欢",
"email": "go_developer@163.com",
"username": "zhangdeman"
},
"committer": {
"name": "白茶清欢",
"email": "go_developer@163.com",
"username": "zhangdeman"
},
"verification": null,
"timestamp": "2021-11-11T17:54:40+08:00",
"added": [],
"removed": [],
"modified": [
"publish_admin.sh"
]
},
"repository": {
"id": 24,
"owner": {"id":1,"login":"zhangdeman","full_name":"","email":"go_developer@163.com","avatar_url":"https://git.zhangdeman.cn/user/avatar/zhangdeman/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2021-05-04T12:58:50+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"zhangdeman"},
"name": "manager-job",
"full_name": "zhangdeman/manager-job",
"description": "各种管理JOB",
"empty": false,
"private": false,
"fork": false,
"template": false,
"parent": null,
"mirror": false,
"size": 24,
"html_url": "https://git.zhangdeman.cn/zhangdeman/manager-job",
"ssh_url": "www@git.zhangdeman.cn:zhangdeman/manager-job.git",
"clone_url": "https://git.zhangdeman.cn/zhangdeman/manager-job.git",
"original_url": "",
"website": "",
"stars_count": 0,
"forks_count": 0,
"watchers_count": 1,
"open_issues_count": 0,
"open_pr_counter": 0,
"release_counter": 0,
"default_branch": "master",
"archived": false,
"created_at": "2021-11-11T16:54:10+08:00",
"updated_at": "2021-11-11T17:02:22+08:00",
"permissions": {
"admin": true,
"push": true,
"pull": true
},
"has_issues": true,
"internal_tracker": {
"enable_time_tracker": false,
"allow_only_contributors_to_track_time": true,
"enable_issue_dependencies": true
},
"has_wiki": true,
"has_pull_requests": true,
"has_projects": true,
"ignore_whitespace_conflicts": false,
"allow_merge_commits": true,
"allow_rebase": true,
"allow_rebase_explicit": true,
"allow_squash_merge": true,
"default_merge_style": "merge",
"avatar_url": "",
"internal": false,
"mirror_interval": ""
},
"pusher": {"id":1,"login":"zhangdeman","full_name":"","email":"go_developer@163.com","avatar_url":"https://git.zhangdeman.cn/user/avatar/zhangdeman/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2021-05-04T12:58:50+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"zhangdeman"},
"sender": {"id":1,"login":"zhangdeman","full_name":"","email":"go_developer@163.com","avatar_url":"https://git.zhangdeman.cn/user/avatar/zhangdeman/-1","language":"","is_admin":false,"last_login":"0001-01-01T00:00:00Z","created":"2021-05-04T12:58:50+08:00","restricted":false,"active":false,"prohibit_login":false,"location":"","website":"","description":"","visibility":"public","followers_count":0,"following_count":0,"starred_repos_count":0,"username":"zhangdeman"}
}
`
fmt.Println(NewJSON2GO("DemoJSON").Parse(testJSON))
}
type DemoJSON struct {
Repository struct {
Mirror bool `json:"mirror"`
OriginalUrl string `json:"original_url"`
StarsCount int64 `json:"stars_count"`
IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"`
FullName string `json:"full_name"`
Fork bool `json:"fork"`
CreatedAt string `json:"created_at"`
Internal bool `json:"internal"`
AllowRebaseExplicit bool `json:"allow_rebase_explicit"`
MirrorInterval string `json:"mirror_interval"`
CloneUrl string `json:"clone_url"`
WatchersCount int64 `json:"watchers_count"`
OpenIssuesCount int64 `json:"open_issues_count"`
ReleaseCounter int64 `json:"release_counter"`
InternalTracker struct {
EnableTimeTracker bool `json:"enable_time_tracker"`
AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"`
EnableIssueDependencies bool `json:"enable_issue_dependencies"`
} `json:"internal_tracker"`
Name string `json:"name"`
Empty bool `json:"empty"`
SshUrl string `json:"ssh_url"`
ForksCount int64 `json:"forks_count"`
Permissions struct {
Pull bool `json:"pull"`
Admin bool `json:"admin"`
Push bool `json:"push"`
} `json:"permissions"`
Private bool `json:"private"`
Size int64 `json:"size"`
HasWiki bool `json:"has_wiki"`
HasProjects bool `json:"has_projects"`
AllowMergeCommits bool `json:"allow_merge_commits"`
AvatarUrl string `json:"avatar_url"`
Id int64 `json:"id"`
Description string `json:"description"`
Website string `json:"website"`
UpdatedAt string `json:"updated_at"`
HasPullRequests bool `json:"has_pull_requests"`
Parent map[string]interface{} `json:"parent"`
OpenPrCounter int64 `json:"open_pr_counter"`
HasIssues bool `json:"has_issues"`
AllowRebase bool `json:"allow_rebase"`
AllowSquashMerge bool `json:"allow_squash_merge"`
Owner struct {
IsAdmin bool `json:"is_admin"`
Website string `json:"website"`
Email string `json:"email"`
AvatarUrl string `json:"avatar_url"`
ProhibitLogin bool `json:"prohibit_login"`
Description string `json:"description"`
FollowingCount int64 `json:"following_count"`
StarredReposCount int64 `json:"starred_repos_count"`
Username string `json:"username"`
Login string `json:"login"`
Created string `json:"created"`
Visibility string `json:"visibility"`
FullName string `json:"full_name"`
LastLogin string `json:"last_login"`
Restricted bool `json:"restricted"`
Active bool `json:"active"`
Location string `json:"location"`
FollowersCount int64 `json:"followers_count"`
Id int64 `json:"id"`
Language string `json:"language"`
} `json:"owner"`
Template bool `json:"template"`
HtmlUrl string `json:"html_url"`
DefaultBranch string `json:"default_branch"`
Archived bool `json:"archived"`
DefaultMergeStyle string `json:"default_merge_style"`
} `json:"repository"`
Sender struct {
Created string `json:"created"`
Website string `json:"website"`
FollowingCount int64 `json:"following_count"`
StarredReposCount int64 `json:"starred_repos_count"`
FullName string `json:"full_name"`
Language string `json:"language"`
LastLogin string `json:"last_login"`
Visibility string `json:"visibility"`
FollowersCount int64 `json:"followers_count"`
Username string `json:"username"`
IsAdmin bool `json:"is_admin"`
Active bool `json:"active"`
Location string `json:"location"`
Restricted bool `json:"restricted"`
Description string `json:"description"`
AvatarUrl string `json:"avatar_url"`
ProhibitLogin bool `json:"prohibit_login"`
Id int64 `json:"id"`
Login string `json:"login"`
Email string `json:"email"`
} `json:"sender"`
Ref string `json:"ref"`
Before string `json:"before"`
After string `json:"after"`
HeadCommit struct {
Message string `json:"message"`
Url string `json:"url"`
Author struct {
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username"`
} `json:"author"`
Verification map[string]interface{} `json:"verification"`
Timestamp string `json:"timestamp"`
Id string `json:"id"`
Committer struct {
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username"`
} `json:"committer"`
Added []interface{} `json:"added"`
Removed []interface{} `json:"removed"`
Modified []string `json:"modified"`
} `json:"head_commit"`
CompareUrl string `json:"compare_url"`
Commits []struct {
Url string `json:"url"`
Author struct {
Name string `json:"name"`
Email string `json:"email"`
Username string `json:"username"`
} `json:"author"`
Committer struct {
Username string `json:"username"`
Name string `json:"name"`
Email string `json:"email"`
} `json:"committer"`
Verification map[string]interface{} `json:"verification"`
Removed []interface{} `json:"removed"`
Id string `json:"id"`
Message string `json:"message"`
Modified []string `json:"modified"`
Timestamp string `json:"timestamp"`
Added []interface{} `json:"added"`
} `json:"commits"`
Pusher struct {
Language string `json:"language"`
Website string `json:"website"`
Id int64 `json:"id"`
Email string `json:"email"`
Active bool `json:"active"`
Description string `json:"description"`
Visibility string `json:"visibility"`
Login string `json:"login"`
Restricted bool `json:"restricted"`
Location string `json:"location"`
StarredReposCount int64 `json:"starred_repos_count"`
Username string `json:"username"`
IsAdmin bool `json:"is_admin"`
ProhibitLogin bool `json:"prohibit_login"`
LastLogin string `json:"last_login"`
Created string `json:"created"`
FollowersCount int64 `json:"followers_count"`
FollowingCount int64 `json:"following_count"`
FullName string `json:"full_name"`
AvatarUrl string `json:"avatar_url"`
} `json:"pusher"`
}

@ -1,59 +0,0 @@
// Package {PACKAGE} ...
//
// Description : {PACKAGE} ...
package {PACKAGE}
import (
"gorm.io/gorm"
"errors"
)
// {DATA_STRUCT_NAME} 数据库表的数据结构
{DATA_STRUCT}
// New{DATA_STRUCT_DAO} 获取DAO实例
func New{DATA_STRUCT_DAO}() *{DATA_STRUCT_DAO} {
return &{DATA_STRUCT_DAO}{
table : "{DB_TABLE_NAME}",
}
}
// {DATA_STRUCT_DAO} sql2go tool generate
type {DATA_STRUCT_DAO} struct {
table string
}
// GetDetailBy{PRIMARY_KEY} 根据主键ID获取详情
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetDetailBy{PRIMARY_KEY}(dbInstance *gorm.DB, {PRIMARY_KEY} {PRIMARY_KEY_TYPE}) (*{DATA_STRUCT_NAME}, error) {
var (
err error
detail {DATA_STRUCT_NAME}
)
if err = dbInstance.Table({DAO_RECEIVER}.table).Where("{PRIMARY_KEY_FIELD} = ?", {PRIMARY_KEY}).Limit(1).First(&detail).Error; nil != err {
return nil, err
}
return &detail, nil
}
// GetList 获取数据列表
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetList(dbInstance *gorm.DB, condition map[string]interface{}, limit int, offset int) ([]{DATA_STRUCT_NAME}, error) {
if nil == condition {
condition = make(map[string]interface{})
}
var (
err error
list []{DATA_STRUCT_NAME}
)
if err = dbInstance.Table({DAO_RECEIVER}.table).Where(condition).Limit(limit).Offset(offset).Find(&list).Error; nil != err {
return make([]{DATA_STRUCT_NAME}, 0), err
}
return list, nil
}
// Create 创建数据
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) Create(dbInstance *gorm.DB, data *{DATA_STRUCT_NAME}) error {
if nil == data {
return errors.New("data is nil")
}
return dbInstance.Table({DAO_RECEIVER}.table).Create(data).Error
}

@ -1,59 +0,0 @@
// Package sql2go...
//
// Description : sql2go...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-17 10:14 上午
package sql2go
import (
"strings"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
)
// GenerateDao 根据sql自动生成dao
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:15 上午 2021/11/17
func GenerateDao(sql string, packageName string) (string, error) {
if len(packageName) == 0 {
packageName = "dao"
}
dataStruct, basic, err := ParseCreateTableSql(sql)
if nil != err {
return "", err
}
// 读入模板
tplByte, _ := util.ReadFileContent("dao_tpl.tpl")
tpl := string(tplByte)
replaceTable := map[string]string{
"{PACKAGE}": packageName,
"{DATA_STRUCT_DAO}": basic.ModelStruct + "Dao",
"{DATA_STRUCT_NAME}": basic.ModelStruct,
"{DATA_STRUCT}": dataStruct,
"{DAO_RECEIVER}": getDaoReceiver(basic.TableName),
"{DB_TABLE_NAME}": basic.TableName,
"{PRIMARY_KEY}": "ID",
"{PRIMARY_KEY_TYPE}": basic.PrimaryFieldType,
}
for oldVal, newVal := range replaceTable {
tpl = strings.ReplaceAll(tpl, oldVal, newVal)
}
return tpl, nil
}
func getDaoReceiver(tableName string) string {
nameArr := strings.Split(tableName, "_")
result := ""
for _, item := range nameArr {
if len(item) > 0 {
result += string([]byte(item)[0])
}
}
result += "d"
return result
}

@ -1,23 +0,0 @@
// Package sql2go...
//
// Description : sql2go...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-17 11:46 上午
package sql2go
import (
"fmt"
"testing"
)
// TestGenerateDao ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:05 下午 2021/11/17
func TestGenerateDao(t *testing.T) {
sql := "CREATE TABLE `app` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',\n `code` varchar(128) NOT NULL DEFAULT '' COMMENT '分配的app_code',\n `secret` varchar(64) NOT NULL DEFAULT '' COMMENT '分配的私钥',\n `status` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '当前状态 o - 初始化 1- 使用中 2 - 禁用 3 - 删除',\n `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述信息',\n `apply_user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '申请人姓名',\n `apply_user_contact` varchar(128) NOT NULL DEFAULT '' COMMENT '申请人联系方式',\n `create_user_id` varchar(128) NOT NULL DEFAULT '' COMMENT '创建人ID',\n `modify_user_id` varchar(128) NOT NULL DEFAULT '' COMMENT '修改人ID',\n PRIMARY KEY (`id`),\n UNIQUE KEY `uniq_code` (`code`)\n) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COMMENT='APP信息表'"
fmt.Println(GenerateDao(sql, "sql2go"))
}

@ -1,70 +0,0 @@
// Package dao ...
//
// Description : dao ...
package sql2go
import (
"errors"
"gorm.io/gorm"
)
// App 数据库表的数据结构
type App struct {
ID int64 `json:"id" gorm:"column:id;default:;NOT NULL"` // id 主键ID
Code string `json:"code" gorm:"column:code;default:;NOT NULL"` // code 分配的app_code
Secret string `json:"secret" gorm:"column:secret;default:;NOT NULL"` // secret 分配的私钥
Status int `json:"status" gorm:"column:status;default:0;NOT NULL"` // status 当前状态 o - 初始化 1- 使用中 2 - 禁用 3 - 删除
Description string `json:"description" gorm:"column:description;default:;NOT NULL"` // description 描述信息
ApplyUserName string `json:"apply_user_name" gorm:"column:apply_user_name;default:;NOT NULL"` // apply_user_name 申请人姓名
ApplyUserContact string `json:"apply_user_contact" gorm:"column:apply_user_contact;default:;NOT NULL"` // apply_user_contact 申请人联系方式
CreateUserID string `json:"create_user_id" gorm:"column:create_user_id;default:;NOT NULL"` // create_user_id 创建人ID
ModifyUserID string `json:"modify_user_id" gorm:"column:modify_user_id;default:;NOT NULL"` // modify_user_id 修改人ID
}
// NewAppDao 获取DAO实例
func NewAppDao() *AppDao {
return &AppDao{
table: "app",
}
}
// AppDao sql2go tool generate
type AppDao struct {
table string
}
// GetDetailByID 根据主键ID获取详情
func (ad *AppDao) GetDetailByID(dbInstance *gorm.DB, ID int64) (*App, error) {
var (
err error
detail App
)
if err = dbInstance.Table(ad.table).Where("{PRIMARY_KEY_FIELD} = ?", ID).Limit(1).First(&detail).Error; nil != err {
return nil, err
}
return &detail, nil
}
// GetList 获取数据列表
func (ad *AppDao) GetList(dbInstance *gorm.DB, condition map[string]interface{}, limit int, offset int) ([]App, error) {
if nil == condition {
condition = make(map[string]interface{})
}
var (
err error
list []App
)
if err = dbInstance.Table(ad.table).Where(condition).Limit(limit).Offset(offset).Find(&list).Error; nil != err {
return make([]App, 0), err
}
return list, nil
}
// Create 创建数据
func (ad *AppDao) Create(dbInstance *gorm.DB, data *App) error {
if nil == data {
return errors.New("data is nil")
}
return dbInstance.Table(ad.table).Create(data).Error
}

@ -8,7 +8,6 @@
package sql2go
import (
"errors"
"strings"
"git.zhangdeman.cn/zhangdeman/gopkg/util"
@ -21,47 +20,23 @@ const (
CreateSQLColumnTPL = "{FIELD} {TYPE} `json:\"{JSON_TAG}\" gorm:\"column:{COLUMN};default:{DEFAULT_VALUE};{NOT_NULL}\"` // {COMMENT}"
)
// BasicTableInfo ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:47 上午 2021/11/17
type BasicTableInfo struct {
TableName string
ModelStruct string
PrimaryField string
PrimaryFieldType string
}
// ParseCreateTableSql 解析建表sql
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:49 下午 2021/10/25
func ParseCreateTableSql(sql string) (string, *BasicTableInfo, error) {
func ParseCreateTableSql(sql string) (string, error) {
var (
stmt sqlparser.Statement
err error
basic *BasicTableInfo
stmt sqlparser.Statement
err error
)
basic = &BasicTableInfo{
TableName: "",
ModelStruct: "",
PrimaryField: "ID",
PrimaryFieldType: "",
}
sql = strings.ReplaceAll(strings.ReplaceAll(sql, "CURRENT_TIMESTAMP()", "CURRENT_TIMESTAMP"), "current_timestamp()", "CURRENT_TIMESTAMP")
sql = strings.ReplaceAll(strings.ToUpper(sql), "CURRENT_TIMESTAMP()", "CURRENT_TIMESTAMP")
if stmt, err = sqlparser.ParseStrictDDL(sql); nil != err {
return "", nil, err
return "", err
}
r, ok := stmt.(*sqlparser.DDL)
if !ok {
return "", nil, errors.New("input sql is not ddl")
}
basic.TableName = sqlparser.String(r.NewName)
basic.ModelStruct = util.SnakeCaseToCamel(basic.TableName)
structResult := "type " + basic.ModelStruct + " struct { \n"
r := stmt.(*sqlparser.DDL)
structResult := "type " + util.SnakeCaseToCamel(sqlparser.String(r.NewName)) + " struct { \n"
for _, item := range r.TableSpec.Columns {
data := map[string]string{
@ -72,9 +47,6 @@ func ParseCreateTableSql(sql string) (string, *BasicTableInfo, error) {
"{COMMENT}": item.Name.String() + " " + string(item.Type.Comment.Val),
"{TYPE}": sqlTypeMap[item.Type.Type],
}
if data["{FIELD}"] == "ID" {
basic.PrimaryFieldType = data["{TYPE}"]
}
if item.Type.NotNull {
data["{NOT_NULL}"] = "NOT NULL"
}
@ -88,5 +60,5 @@ func ParseCreateTableSql(sql string) (string, *BasicTableInfo, error) {
structResult += val + "\n"
}
structResult = structResult + "}"
return structResult, basic, nil
return structResult, nil
}

@ -1,18 +0,0 @@
// Package sql2go...
//
// Description : sql2go...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-10-25 5:02 下午
package sql2go
import (
"fmt"
"testing"
)
func TestParseSql(t *testing.T) {
sql := "CREATE TABLE `app` (\n `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT '主键ID',\n `code` varchar(128) NOT NULL DEFAULT '' COMMENT '分配的app_code',\n `secret` varchar(64) NOT NULL DEFAULT '' COMMENT '分配的私钥',\n `status` int(10) unsigned NOT NULL DEFAULT 0 COMMENT '当前状态 o - 初始化 1- 使用中 2 - 禁用 3 - 删除',\n `description` varchar(256) NOT NULL DEFAULT '' COMMENT '描述信息',\n `apply_user_name` varchar(64) NOT NULL DEFAULT '' COMMENT '申请人姓名',\n `apply_user_contact` varchar(128) NOT NULL DEFAULT '' COMMENT '申请人联系方式',\n `create_user_id` varchar(128) NOT NULL DEFAULT '' COMMENT '创建人ID',\n `modify_user_id` varchar(128) NOT NULL DEFAULT '' COMMENT '修改人ID',\n PRIMARY KEY (`id`),\n UNIQUE KEY `uniq_code` (`code`)\n) ENGINE=InnoDB AUTO_INCREMENT=100001 DEFAULT CHARSET=utf8mb4 COMMENT='APP信息表'"
fmt.Println(ParseCreateTableSql(sql))
}

@ -7,10 +7,7 @@
// Date : 2021-03-09 5:56 下午
package util
import (
"net"
"net/http"
)
import "net"
// GetHostIP 获取本机IP地址
//
@ -30,24 +27,3 @@ func GetHostIP() string {
}
return hostIP
}
// GetRemoteIp 获取远端IP
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:35 下午 2021/11/22
func GetRemoteIp(req *http.Request) string {
// Try via request
ip, _, err := net.SplitHostPort(req.RemoteAddr)
if err != nil {
return "::1"
}
userIP := net.ParseIP(ip)
if userIP == nil {
return "::1"
}
return userIP.String()
}

@ -10,10 +10,7 @@ package util
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"reflect"
)
// JSONUnmarshalWithNumber 解析json
@ -37,55 +34,3 @@ func JSONUnmarshalWithNumberForIOReader(ioReader io.ReadCloser, receiver interfa
decoder.UseNumber()
return decoder.Decode(receiver)
}
// JSONConsoleOutput ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:45 下午 2021/11/5
func JSONConsoleOutput(data interface{}) {
var out bytes.Buffer
switch reflect.TypeOf(data).Kind() {
case reflect.Slice:
fallthrough
case reflect.Array:
fallthrough
case reflect.Map:
fallthrough
case reflect.Ptr:
byteData, _ := json.Marshal(data)
_ = json.Indent(&out, []byte(string(byteData)+"\n"), "", "\t")
_, _ = out.WriteTo(os.Stdout)
return
case reflect.Int:
fallthrough
case reflect.Int8:
fallthrough
case reflect.Int16:
fallthrough
case reflect.Int32:
fallthrough
case reflect.Int64:
fallthrough
case reflect.Uint:
fallthrough
case reflect.Uint8:
fallthrough
case reflect.Uint16:
fallthrough
case reflect.Uint32:
fallthrough
case reflect.Uint64:
fallthrough
case reflect.Float32:
fallthrough
case reflect.Float64:
fallthrough
case reflect.String:
_ = json.Indent(&out, []byte(fmt.Sprintf("%v\n", data)), "", "\t")
_, _ = out.WriteTo(os.Stdout)
return
default:
fmt.Println("")
}
}

@ -1,22 +0,0 @@
// Package util...
//
// Description : util...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-05 6:10 下午
package util
import (
"testing"
)
// TestJSONMarshal ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 6:12 下午 2021/11/5
func TestJSONMarshal(t *testing.T) {
data := `{"name":"zhangdeman", "age":18, "height": 180}`
JSONConsoleOutput(data)
}

@ -1,209 +0,0 @@
// Package util ...
//
// Description : 从map中读取数据
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-16 4:28 下午
package util
import (
"git.zhangdeman.cn/zhangdeman/gopkg/convert"
"github.com/pkg/errors"
)
// Exist 检测一个key在map中是否存在
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:52 下午 2021/11/16
func Exist(source map[interface{}]interface{}, key interface{}) bool {
if nil == source {
return false
}
if _, exist := source[key]; !exist {
return false
}
return true
}
// GetInt 获取int值
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:30 下午 2021/11/16
func GetInt(source map[interface{}]interface{}, key interface{}, defaultVal int) int {
var result int
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetInt8 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:59 下午 2021/11/16
func GetInt8(source map[interface{}]interface{}, key interface{}, defaultVal int8) int8 {
var result int8
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetInt16 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:59 下午 2021/11/16
func GetInt16(source map[interface{}]interface{}, key interface{}, defaultVal int16) int16 {
var result int16
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetInt32 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:00 下午 2021/11/16
func GetInt32(source map[interface{}]interface{}, key interface{}, defaultVal int32) int32 {
var result int32
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetInt64 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:00 下午 2021/11/16
func GetInt64(source map[interface{}]interface{}, key interface{}, defaultVal int64) int64 {
var result int64
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetUint ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:01 下午 2021/11/16
func GetUint(source map[interface{}]interface{}, key interface{}, defaultVal uint) uint {
var result uint
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetUint8 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:01 下午 2021/11/16
func GetUint8(source map[interface{}]interface{}, key interface{}, defaultVal uint8) uint8 {
var result uint8
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetUint16 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:02 下午 2021/11/16
func GetUint16(source map[interface{}]interface{}, key interface{}, defaultVal uint16) uint16 {
var result uint16
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetUint32 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:02 下午 2021/11/16
func GetUint32(source map[interface{}]interface{}, key interface{}, defaultVal uint32) uint32 {
var result uint32
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetUint64 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:03 下午 2021/11/16
func GetUint64(source map[interface{}]interface{}, key interface{}, defaultVal uint64) uint64 {
var result uint64
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetFloat32 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:03 下午 2021/11/16
func GetFloat32(source map[interface{}]interface{}, key interface{}, defaultVal float32) float32 {
var result float32
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetFloat64 ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:04 下午 2021/11/16
func GetFloat64(source map[interface{}]interface{}, key interface{}, defaultVal float64) float64 {
var result float64
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetString ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 5:07 下午 2021/11/16
func GetString(source map[interface{}]interface{}, key interface{}, defaultVal string) string {
var result string
if err := GetDataWithReceiver(source, key, &result); nil != err {
return defaultVal
}
return result
}
// GetDataWithReceiver 使用制定的数据指针接受结果
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 4:54 下午 2021/11/16
func GetDataWithReceiver(source map[interface{}]interface{}, key interface{}, receiver interface{}) error {
if !Exist(source, key) {
return errors.New("key is not found")
}
return convert.ConvertAssign(receiver, source[key])
}

@ -54,9 +54,6 @@ func Md5(str string) string {
//
// Date : 4:58 下午 2021/10/25
func SnakeCaseToCamel(str string) string {
if len(str) == 0 {
return ""
}
builder := strings.Builder{}
index := 0
if str[0] >= 'a' && str[0] <= 'z' {
@ -71,11 +68,6 @@ func SnakeCaseToCamel(str string) string {
continue
}
}
// 将ID转为大写
if str[i] == 'd' && i-1 >= 0 && (str[i-1] == 'i' || str[i-1] == 'I') && (i+1 == len(str) || i+1 < len(str) && str[i+1] == '_') {
builder.WriteByte('d' - ('a' - 'A'))
continue
}
builder.WriteByte(str[i])
}
return builder.String()

@ -1,89 +0,0 @@
// Package util...
//
// Description : util...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-04 2:38 下午
package util
import (
"net/url"
"strings"
)
// URLParseResult url解析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:51 下午 2021/11/4
type URLParseResult struct {
Scheme string `json:"scheme"`
Domain string `json:"domain"`
URI string `json:"uri"`
Parameter map[string]string `json:"parameter"`
}
// URLEncode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:39 下午 2021/11/4
func URLEncode(inputURL string) string {
return url.QueryEscape(inputURL)
}
// URLDecode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:39 下午 2021/11/4
func URLDecode(inputURL string) (string, error) {
return url.QueryUnescape(inputURL)
}
// URLParse url解析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:39 下午 2021/11/4
func URLParse(inputURL string) (*URLParseResult, error) {
var (
parseResult *url.URL
err error
)
if parseResult, err = url.Parse(inputURL); nil != err {
return nil, err
}
detail := &URLParseResult{
Scheme: parseResult.Scheme,
Domain: parseResult.Host,
URI: parseResult.Path,
Parameter: make(map[string]string),
}
for k, v := range parseResult.Query() {
if len(v) > 1 {
detail.Parameter[k] = "[" + strings.Join(v, ",") + "]"
} else {
detail.Parameter[k] = v[0]
}
}
return detail, nil
}
// BuildQueryURL 构建GET链接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2:43 下午 2021/11/4
func BuildQueryURL(apiURL string, parameter map[string]string) string {
u := url.Values{}
for k, v := range parameter {
u.Set(k, v)
}
apiURL = strings.Trim(apiURL, "?")
if strings.Contains(apiURL, "?") {
return apiURL + "&" + u.Encode()
}
return apiURL + "?" + u.Encode()
}

@ -1,60 +0,0 @@
// Package util...
//
// Description : util...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-11-04 2:58 下午
package util
import (
"encoding/json"
"fmt"
"testing"
)
// TestBuildQueryURL ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:04 下午 2021/11/4
func TestBuildQueryURL(t *testing.T) {
url := "https://ww.baidu.com/api/detail"
parameter := map[string]string{
"name": "zhang",
"age": "18",
}
fmt.Println(BuildQueryURL(url, parameter))
url = "https://ww.baidu.com/api/detail?tag=1"
fmt.Println(BuildQueryURL(url, parameter))
}
// TestURLDecode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:04 下午 2021/11/4
func TestURLDecode(t *testing.T) {
}
// TestURLEncode ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:05 下午 2021/11/4
func TestURLEncode(t *testing.T) {
}
// TestURLParse 测试URL解析
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:00 下午 2021/11/4
func TestURLParse(t *testing.T) {
inputURL := "https://www.baidu.com/api/detail?a=1&b=2&a=3&d=qwe"
r, _ := URLParse(inputURL)
byteData, _ := json.Marshal(r)
fmt.Println(string(byteData))
}