jason 1 éve
commit
859b13323c
100 módosított fájl, 9001 hozzáadás és 0 törlés
  1. 12 0
      CHANGELOG.md
  2. 8 0
      Dockerfile.in
  3. 27 0
      Makefile
  4. 87 0
      README.md
  5. 1 0
      VERSION
  6. 28 0
      apis/common.go
  7. 45 0
      app.spec.in
  8. 136 0
      common.in/cache/cache.go
  9. 86 0
      common.in/cache/hash.go
  10. 73 0
      common.in/cache/key.go
  11. 41 0
      common.in/cache/list.go
  12. 9 0
      common.in/cache/pubsub.go
  13. 8 0
      common.in/cache/redis.go
  14. 11 0
      common.in/cache/server.go
  15. 51 0
      common.in/cache/set.go
  16. 99 0
      common.in/cache/string.go
  17. 120 0
      common.in/cache/zset.go
  18. 41 0
      common.in/clinit/elastic.go-bk
  19. 60 0
      common.in/clinit/etcd.go
  20. 21 0
      common.in/clinit/id.go
  21. 26 0
      common.in/clinit/mongo.go
  22. 60 0
      common.in/clinit/mysq-gorm.go
  23. 41 0
      common.in/clinit/mysql.go
  24. 331 0
      common.in/config/config.go
  25. 126 0
      common.in/config/ecb.go
  26. 30 0
      common.in/config/ecb_test.go
  27. 12 0
      common.in/id/id.go
  28. 12 0
      common.in/id/id_test.go
  29. 25 0
      common.in/id/init.go
  30. 49 0
      common.in/jsonrpc2/errors.go
  31. 140 0
      common.in/logger/zap.go
  32. 292 0
      common.in/mq/rabbitmq.go
  33. 122 0
      common.in/span/id.go
  34. 97 0
      common.in/span/id_test.go
  35. 113 0
      common.in/span/span.go
  36. 188 0
      common.in/span/span_test.go
  37. 63 0
      common.in/storage/db.go
  38. 7 0
      common.in/storage/error.go
  39. 29 0
      common.in/storage/redis.go
  40. 25 0
      common.in/task/access.go
  41. 11 0
      common.in/task/logger.go
  42. 48 0
      common.in/task/task.go
  43. 17 0
      common.in/utils/base64.go
  44. 74 0
      common.in/utils/consistency_hash.go
  45. 70 0
      common.in/utils/context.go
  46. 114 0
      common.in/utils/copy.go
  47. 245 0
      common.in/utils/crypto.go
  48. 80 0
      common.in/utils/distribute_lock.go
  49. 31 0
      common.in/utils/gc.go
  50. 72 0
      common.in/utils/math.go
  51. 101 0
      common.in/utils/oss.go
  52. 57 0
      common.in/utils/redis.go
  53. 25 0
      common.in/utils/signal.go
  54. 310 0
      common.in/utils/utils.go
  55. 418 0
      common.in/utils/vehicle.go
  56. 2 0
      conf/app.conf
  57. 2 0
      conf/app.conf.in
  58. 17 0
      conf/app.service.in
  59. 131 0
      conf/common.json
  60. 90 0
      confbuild.sh
  61. 163 0
      consts/common.go
  62. 14 0
      docker/compose/docker-compose.yaml.in
  63. 77 0
      docker/kubernetes/dip-vehicle.yaml.in
  64. 77 0
      errors/errors.go
  65. 29 0
      go.mod
  66. 549 0
      go.sum
  67. 111 0
      imagebuild.sh
  68. 130 0
      impl/analysis/common.go
  69. 15 0
      impl/analysis/logger.go
  70. 97 0
      impl/analysis/ods1.go
  71. 92 0
      impl/analysis/ods10.go
  72. 101 0
      impl/analysis/ods11.go
  73. 117 0
      impl/analysis/ods12.go
  74. 134 0
      impl/analysis/ods13.go
  75. 4 0
      impl/analysis/ods14.go
  76. 84 0
      impl/analysis/ods15.go
  77. 105 0
      impl/analysis/ods16.go
  78. 49 0
      impl/analysis/ods17.go
  79. 80 0
      impl/analysis/ods18.go
  80. 96 0
      impl/analysis/ods19.go
  81. 139 0
      impl/analysis/ods2.go
  82. 132 0
      impl/analysis/ods3.go
  83. 115 0
      impl/analysis/ods4.go
  84. 290 0
      impl/analysis/ods5.go
  85. 108 0
      impl/analysis/ods6.go
  86. 110 0
      impl/analysis/ods7.go
  87. 90 0
      impl/analysis/ods8.go
  88. 82 0
      impl/analysis/ods9.go
  89. 65 0
      impl/handle/common.go
  90. 32 0
      impl/handle/logger.go
  91. 57 0
      impl/handle/manual_amendment_data.go
  92. 326 0
      impl/handle/offline_data.go
  93. 70 0
      impl/handle/online_data.go
  94. 73 0
      impl/handle/run.go
  95. 13 0
      impl/rcvr.go
  96. 0 0
      log/ads-access.log
  97. 0 0
      log/ads.log
  98. 216 0
      main.go
  99. 148 0
      model/consume_fail.go
  100. 144 0
      model/fail_msg.go

+ 12 - 0
CHANGELOG.md

@@ -0,0 +1,12 @@
+## release 2.2 修改记录
+### 新特性
+    *1,Feature 3214421 本地违章v006,v008
+    *2,Feature 3214421 ZQY返回多条相同号牌数据判断
+    *3,Feature 3006587 年检到期优化
+    *4,Feature 3155994 获取车档数量
+    *5,Feature 3187523 NO272接口切换到 M00002
+    *6,Feature 3049944 请求参数实体(人名)黑名单
+### Bug修复
+    *1,Bugfix 3355954 日志没有记录msg
+    *2,Bugfix 3179191 MS数据源VIN可能错误导致加车有问题
+    *3,Bugfix 3250067 线上数据源日志,dj超时记录为成功

+ 8 - 0
Dockerfile.in

@@ -0,0 +1,8 @@
+FROM hb.autocareai.cn/library/busybox:v1
+
+WORKDIR /dip
+COPY %APP_NAME% .
+COPY conf/app.conf app.conf
+COPY conf/common.json ./conf/common.json
+EXPOSE %SERVE_PORT%
+CMD ["./%APP_NAME%","--config","app.conf"]

+ 27 - 0
Makefile

@@ -0,0 +1,27 @@
+# Go parameters
+GOCMD=go
+GOBUILD=$(GOCMD) build
+GOCLEAN=$(GOCMD) clean
+GOTEST=$(GOCMD) test
+GOGET=$(GOCMD) get
+
+SERVICE_NAME = $(shell pwd |sed 's/^\(.*\)[/]//' )
+PROJECT_NAME=${SERVICE_NAME}
+BIN_DIR=/usr/sbin
+CP_CMD=/usr/bin/cp
+COMMAND=${SERVICE_NAME}
+
+all: build-go
+
+build-go:
+	go build -o $(PROJECT_NAME) -v  -ldflags "-X main.Version=$(version) -X main.GitCommit=`git rev-parse HEAD`"
+test:
+	$(GOTEST) -v ./...
+install:build
+	$(CP_CMD) $(COMMAND) $(DESTDIR)$(BIN_DIR)
+clean:
+	$(GOCLEAN)
+	rm -f $(PROJECT_NAME)
+
+
+

+ 87 - 0
README.md

@@ -0,0 +1,87 @@
+# msdemo
+
+本工程是微服务的demo。
+
+## 编译依赖
+* 共有第三方包工程:[golangpkgs](http://gitlab.chejinjia.cn/repository/commons/golangpkgs.git)
+
+## 必要操作
+
+```shell
+# 取出共有第三方包工程,其中 /path/to 需要自己指定
+# *** 如果其它工程做了软链接,则不需重复以下三步 ***
+mkdir -P /path/to/repository/commons
+cd /path/to/repository/commons
+git clone http://gitlab.chejinjia.cn/repository/commons/golangpkgs.git
+
+# 建立业务工程,并关联golangpkgs
+mkdir -p /path/to/repository/projects/src
+export GOPATH=/path/to/repository/projects
+cd /path/to/repository/projects/src
+ln -s /path/to/repository/commons/golangpkgs/vendor/ . # 如果其它工程做了软链接,则忽略此行
+git clone git@gitlab.chejinjia.cn:repository/your_project.git # 本工程的链接
+go build
+...
+```
+
+# 关于日志
+## 日志使用步骤
+**1. 在要打印日志的包目录下,拷贝一份logger.go,并修改logger所属包名:**
+```
+./impl/arith
+└── logger.go
+./common.in/task
+└── logger.go
+```
+**2. 在`main.go`中添加如下代码:**
+```go
+// 通用logger
+commonLogger := logger.InitLogger(runmode, fmt.Sprintf("logs/%s.log", appname),
+    maxSize, maxBackups, maxAge, disableStacktrace)
+// 单独设置
+accessLogger := logger.NewInfoLogger(runmode, fmt.Sprintf("logs/%s-access.log", appname),
+    maxSize, maxBackups, maxAge)
+
+// 设置需要使用logger的地方
+// common日志
+arith.SetLogger(commonLogger)
+// access日志
+task.SetLogger(accessLogger)
+```
+
+# 错误日志规范
+```go
+l.Error("业务",
+        zap.String("操作", "操作名称"),
+        zap.String("参数或字段", "参数列表以及值"),
+        zap.String("error", err.Error()))
+```
+
+#### mysql 错误日志
+```go
+l.Error("mysql",
+    zap.String("sql", "SELECT from c_device"), // 操作+表名
+    zap.String("fields", utils.MarshalJsonString(field1, value1, field2, value2)),
+    zap.String("error", err.Error()))
+```
+#### redis 错误日志
+```go
+l.Error("redis",
+    zap.String("cmd", "SETEX"), // 命令
+    zap.String("args", utils.MarshalJsonString(key, seconds, value)),
+    zap.String("error", err.Error()))
+```
+#### 调用rpc 错误日志
+```go
+l.Error("rpc",
+    zap.String("call", "Arith.Add"), // 注册名.方法名
+    zap.String("args", utils.MarshalJsonString(req)),
+    zap.String("error", err.Error()))
+```
+#### 内部函数调用 错误日志
+```go
+l.Error("func",
+    zap.String("call", "Add"), // 函数名
+    zap.String("args", utils.MarshalJsonString(arg1, arg2, arg3)),
+    zap.String("error", err.Error()))
+```

+ 1 - 0
VERSION

@@ -0,0 +1 @@
+2.3

+ 28 - 0
apis/common.go

@@ -0,0 +1,28 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package apis
+
+type OdsMessage struct {
+	MsgType       string `json:"msg_type"`
+	SourceCode    string `json:"source_code"`     // 来源编码
+	OfflineTaskId int64  `json:"offline_task_id"` // 离线消息任务id
+	TaskList      []int  `json:"task_list"`       // 任务列表
+	From          int    `json:"from"`            // 消息来源类型  0 数据库 1 excel
+	//SourceLevel   int    `json:"source_level"`    // 源层级
+	//Step int `json:"step"` // 步数
+	Content   string `json:"content"`
+	Timestamp int64  `json:"timestamp"`
+}
+
+type DwsMessage struct {
+	OdsMsgType    string `json:"ods_msg_type"`
+	SourceCode    string `json:"source_code"`     // 来源编码
+	OfflineTaskId int64  `json:"offline_task_id"` // 离线消息任务id
+	TaskList      []int  `json:"task_list"`       // 任务列表
+	//SourceLevel   int    `json:"source_level"`    // 源层级
+	//Step int `json:"step"` // 步数
+	Content   string `json:"content"`
+	Timestamp int64  `json:"timestamp"`
+	//NeedSleep bool   `json:"-"`
+}

+ 45 - 0
app.spec.in

@@ -0,0 +1,45 @@
+Summary:  %APP_NAME% micro service
+Name: %APP_NAME%
+Version: stable
+Release: l
+License: user EULA
+Source: %APP_NAME%-%VERSION%.tgz
+Group: utimes/microservice
+BuildRoot: /tmp
+
+%define __debug_install_post   \
+   %{_rpmconfigdir}/find-debuginfo.sh %{?_find_debuginfo_opts} "%{_builddir}/%{?buildsubdir}"\
+%{nil}
+
+%description
+%prep
+%setup -c
+
+%install
+echo %VERSION%
+mkdir -p ${RPM_BUILD_ROOT}/opt/%SERVICE_NAME%/bin
+install ./%APP_NAME% ${RPM_BUILD_ROOT}/opt/%SERVICE_NAME%/bin
+
+
+mkdir -p ${RPM_BUILD_ROOT}/etc/init.d/
+install ./sh/app ${RPM_BUILD_ROOT}/etc/init.d/%SERVICE_NAME% 
+
+mkdir -p ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./sh/init-multi-mode ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./sh/start-stop-functions ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/lib/
+install ./conf/app.conf ${RPM_BUILD_ROOT}/etc/%SERVICE_NAME%/app.conf
+
+mkdir -p ${RPM_BUILD_ROOT}/usr/lib/systemd/system/
+install ./conf/%SERVICE_NAME%.service ${RPM_BUILD_ROOT}/usr/lib/systemd/system/
+
+%files
+/opt/%SERVICE_NAME%/bin/%APP_NAME%
+/etc/init.d/%SERVICE_NAME%
+/etc/%SERVICE_NAME%/lib/start-stop-functions
+/etc/%SERVICE_NAME%/lib/init-multi-mode
+/etc/%SERVICE_NAME%/app.conf
+/usr/lib/systemd/system/%SERVICE_NAME%.service
+
+%post 
+#systemctl start %SERVICE_NAME%
+#systemctl enable %SERVICE_NAME%

+ 136 - 0
common.in/cache/cache.go

@@ -0,0 +1,136 @@
+package cache
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"github.com/go-redis/redis"
+)
+
+type RedisConfig struct {
+	Addrs        []string
+	Password     string
+	DB           json.Number
+	PoolSize     json.Number
+	MinIdleConns json.Number
+	MaxRetries   json.Number
+	IsCluster    string
+}
+
+type RedisCache struct {
+	isCluster bool
+	client    *redis.Client
+	cluster   *redis.ClusterClient
+}
+
+func (p *RedisCache) Init(config RedisConfig) {
+	if len(config.Addrs) == 0 {
+		panic("Redis's addrs is empty.")
+	}
+
+	poolSize, _ := config.PoolSize.Int64()
+	minIdleConns, _ := config.MinIdleConns.Int64()
+	maxRetries, _ := config.MaxRetries.Int64()
+
+	// redis is cluster?
+	p.isCluster = (config.IsCluster == "true")
+	if p.isCluster {
+		p.cluster = redis.NewClusterClient(&redis.ClusterOptions{
+			Addrs:        config.Addrs,
+			Password:     config.Password,
+			PoolSize:     int(poolSize),
+			MinIdleConns: int(minIdleConns),
+			MaxRetries:   int(maxRetries),
+		})
+
+		// check if redis server is ok.
+		if _, err := p.cluster.Ping().Result(); err != nil {
+			panic(err)
+		}
+	} else {
+		db, _ := config.DB.Int64()
+
+		p.client = redis.NewClient(&redis.Options{
+			Addr:         config.Addrs[0],
+			Password:     config.Password,
+			DB:           int(db),
+			PoolSize:     int(poolSize),
+			MinIdleConns: int(minIdleConns),
+			MaxRetries:   int(maxRetries),
+		})
+
+		// check if redis server is ok.
+		if _, err := p.client.Ping().Result(); err != nil {
+			panic(err)
+		}
+	}
+}
+
+func (p *RedisCache) commandReturnStringSlice(args ...interface{}) (res []string, err error) {
+	if p.isCluster {
+		res, err = strings(p.cluster.Do(args...).Result())
+		if err != nil {
+			if res == nil || len(res) <= 0 {
+				return nil, redis.Nil
+			}
+		}
+
+		return
+	}
+
+	res, err = strings(p.client.Do(args...).Result())
+	if err != nil {
+		if res == nil || len(res) <= 0 {
+			return nil, redis.Nil
+		}
+	}
+
+	return
+}
+
+type Error string
+
+func (err Error) Error() string { return string(err) }
+
+func sliceHelper(reply interface{}, err error, name string, makeSlice func(int), assign func(int, interface{}) error) error {
+	if err != nil {
+		return err
+	}
+	switch reply := reply.(type) {
+	case []interface{}:
+		makeSlice(len(reply))
+		for i := range reply {
+			if reply[i] == nil {
+				continue
+			}
+			if err := assign(i, reply[i]); err != nil {
+				return err
+			}
+		}
+		return nil
+	case nil:
+		return redis.Nil
+	case Error:
+		return reply
+	}
+
+	return fmt.Errorf("cache: unexpected type for %s, got type %T", name, reply)
+}
+
+func strings(reply interface{}, err error) ([]string, error) {
+	var result []string
+	err = sliceHelper(reply, err, "Strings", func(n int) { result = make([]string, n) }, func(i int, v interface{}) error {
+		switch v := v.(type) {
+		case string:
+			result[i] = v
+			return nil
+		case []byte:
+			result[i] = string(v)
+			return nil
+		default:
+			return fmt.Errorf("cache: unexpected element type for Strings, got type %T", v)
+		}
+	})
+
+	return result, err
+}

+ 86 - 0
common.in/cache/hash.go

@@ -0,0 +1,86 @@
+package cache
+
+import (
+	"github.com/go-redis/redis"
+)
+
+func (p *RedisCache) HMGet(key string, fields ...string) ([]string, error) {
+	args := []interface{}{"hmget", key}
+	for _, val := range fields {
+		args = append(args, val)
+	}
+
+	return p.commandReturnStringSlice(args...)
+}
+
+func (p *RedisCache) HGet(key, field string) (string, error) {
+	if p.isCluster {
+		return p.cluster.HGet(key, field).Result()
+	}
+
+	return p.client.HGet(key, field).Result()
+}
+
+func (p *RedisCache) HGetAll(key string) (map[string]string, error) {
+	if p.isCluster {
+		return p.cluster.HGetAll(key).Result()
+	}
+
+	return p.client.HGetAll(key).Result()
+}
+
+func (p *RedisCache) HMSet(key string, fields map[string]interface{}) (string, error) {
+	if p.isCluster {
+		return p.cluster.HMSet(key, fields).Result()
+	}
+
+	return p.client.HMSet(key, fields).Result()
+}
+
+func (p *RedisCache) HSet(key, field string, value interface{}) (bool, error) {
+	if p.isCluster {
+		return p.cluster.HSet(key, field, value).Result()
+	}
+
+	return p.client.HSet(key, field, value).Result()
+}
+
+func (p *RedisCache) HIncrBy(key, field string, incr int64) (int64, error) {
+	if p.isCluster {
+		return p.cluster.HIncrBy(key, field, incr).Result()
+	}
+
+	return p.client.HIncrBy(key, field, incr).Result()
+}
+
+func (p *RedisCache) BatchHGetAll(keys ...string) (map[string]map[string]string, error) {
+	cmds := make(map[string]*redis.StringStringMapCmd, len(keys))
+	if p.isCluster {
+		if _, err := p.cluster.Pipelined(func(pipe redis.Pipeliner) error {
+			for _, key := range keys {
+				cmds[key] = pipe.HGetAll(key)
+			}
+			return nil
+		}); err != nil {
+			return nil, err
+		}
+	} else {
+		if _, err := p.client.Pipelined(func(pipe redis.Pipeliner) error {
+			for _, key := range keys {
+				cmds[key] = pipe.HGetAll(key)
+			}
+			return nil
+		}); err != nil {
+			return nil, err
+		}
+	}
+
+	results := make(map[string]map[string]string, len(cmds))
+	for _, key := range keys {
+		if v, ok := cmds[key]; ok && v != nil {
+			results[key] = v.Val()
+		}
+	}
+
+	return results, nil
+}

+ 73 - 0
common.in/cache/key.go

@@ -0,0 +1,73 @@
+package cache
+
+import (
+	"time"
+)
+
+func (p *RedisCache) Keys(key string) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.Keys(key).Result()
+	}
+
+	return p.client.Keys(key).Result()
+}
+
+func (p *RedisCache) Expire(key string, seconds int64) (bool, error) {
+	if p.isCluster {
+		return p.cluster.Expire(key, time.Duration(seconds)*time.Second).Result()
+	}
+
+	return p.client.Expire(key, time.Duration(seconds)*time.Second).Result()
+}
+
+func (p *RedisCache) Exists(keys ...string) (int64, error) {
+	if p.isCluster {
+		n := int64(0)
+		for _, key := range keys {
+			v, err := p.cluster.Exists(key).Result()
+			if err != nil {
+				return n, err
+			}
+
+			n += v
+		}
+
+		return n, nil
+	}
+
+	return p.client.Exists(keys...).Result()
+}
+
+func (p *RedisCache) Del(keys ...string) (int64, error) {
+	if p.isCluster {
+		n := int64(0)
+		for _, key := range keys {
+			v, err := p.cluster.Del(key).Result()
+			if err != nil {
+				return n, err
+			}
+
+			n += v
+		}
+
+		return n, nil
+	}
+
+	return p.client.Del(keys...).Result()
+}
+
+func (p *RedisCache) IncrBy(key string, value int64) (int64, error) {
+	if p.isCluster {
+		return p.cluster.IncrBy(key, value).Result()
+	}
+
+	return p.client.IncrBy(key, value).Result()
+}
+
+func (p *RedisCache) Rename(key string, newkey string) (string, error) {
+	if p.isCluster {
+		return p.cluster.Rename(key, newkey).Result()
+	}
+
+	return p.client.Rename(key, newkey).Result()
+}

+ 41 - 0
common.in/cache/list.go

@@ -0,0 +1,41 @@
+package cache
+
+func (p *RedisCache) LPush(key string, values ...interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.LPush(key, values).Result()
+	}
+
+	return p.client.LPush(key, values).Result()
+}
+
+func (p *RedisCache) RPush(key string, values ...interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.RPush(key, values).Result()
+	}
+
+	return p.client.RPush(key, values).Result()
+}
+
+func (p *RedisCache) LPop(key string) (string, error) {
+	if p.isCluster {
+		return p.cluster.LPop(key).Result()
+	}
+
+	return p.client.LPop(key).Result()
+}
+
+func (p *RedisCache) RPop(key string) (string, error) {
+	if p.isCluster {
+		return p.cluster.RPop(key).Result()
+	}
+
+	return p.client.RPop(key).Result()
+}
+
+func (p *RedisCache) LRange(key string, start, stop int64) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.LRange(key, start, stop).Result()
+	}
+
+	return p.client.LRange(key, start, stop).Result()
+}

+ 9 - 0
common.in/cache/pubsub.go

@@ -0,0 +1,9 @@
+package cache
+
+func (p *RedisCache) Publish(channel string, message interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.Publish(channel, message).Result()
+	}
+
+	return p.client.Publish(channel, message).Result()
+}

+ 8 - 0
common.in/cache/redis.go

@@ -0,0 +1,8 @@
+package cache
+
+var Redis *RedisCache
+
+func InitRedis(conf *RedisConfig) {
+	Redis = new(RedisCache)
+	Redis.Init(*conf)
+}

+ 11 - 0
common.in/cache/server.go

@@ -0,0 +1,11 @@
+package cache
+
+import "time"
+
+func (p *RedisCache) Time() (time.Time, error) {
+	if p.isCluster {
+		return p.cluster.Time().Result()
+	}
+
+	return p.client.Time().Result()
+}

+ 51 - 0
common.in/cache/set.go

@@ -0,0 +1,51 @@
+package cache
+
+import "fmt"
+
+func (p *RedisCache) SCard(key string) (int64, error) {
+	if p.isCluster {
+		return p.cluster.SCard(key).Result()
+	}
+
+	return p.client.SCard(key).Result()
+}
+
+func (p *RedisCache) SAdd(key string, members ...interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.SAdd(key, members...).Result()
+	}
+
+	return p.client.SAdd(key, members...).Result()
+}
+
+func (p *RedisCache) SMembers(key string) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.SMembers(key).Result()
+	}
+
+	return p.client.SMembers(key).Result()
+}
+
+func (p *RedisCache) SIsmember(key string, member interface{}) (bool, error) {
+	if p.isCluster {
+		return p.cluster.SIsMember(key, member).Result()
+	}
+
+	return p.client.SIsMember(key, member).Result()
+}
+
+func (p *RedisCache) SInter(keys ...string) ([]string, error) {
+	if p.isCluster {
+		return nil, fmt.Errorf("cluster unsupport <sinter>.")
+	}
+
+	return p.client.SInter(keys...).Result()
+}
+
+func (p *RedisCache) SRem(key string, members ...interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.SRem(key, members).Result()
+	}
+
+	return p.client.SRem(key, members).Result()
+}

+ 99 - 0
common.in/cache/string.go

@@ -0,0 +1,99 @@
+package cache
+
+import (
+	"time"
+
+	"github.com/go-redis/redis"
+)
+
+func (p *RedisCache) Get(key string) (string, error) {
+	if p.isCluster {
+		return p.cluster.Get(key).Result()
+	}
+
+	return p.client.Get(key).Result()
+}
+
+func (p *RedisCache) Set(key string, value interface{}) (string, error) {
+	if p.isCluster {
+		return p.cluster.Set(key, value, 0).Result()
+	}
+
+	return p.client.Set(key, value, 0).Result()
+}
+
+func (p *RedisCache) SetEx(key string, seconds int64, value interface{}) (string, error) {
+	if p.isCluster {
+		return p.cluster.Set(key, value, time.Duration(seconds)*time.Second).Result()
+	}
+
+	return p.client.Set(key, value, time.Duration(seconds)*time.Second).Result()
+}
+
+func (p *RedisCache) MGet(keys ...string) ([]string, error) {
+	if p.isCluster {
+		results := make([]string, 0, len(keys))
+		for _, key := range keys {
+			v, err := p.cluster.Get(key).Result()
+			if err != nil {
+				if err != redis.Nil {
+					return nil, err
+				}
+			}
+
+			results = append(results, v)
+		}
+
+		if len(results) <= 0 {
+			return nil, redis.Nil
+		}
+
+		return results, nil
+	}
+
+	args := []interface{}{"mget"}
+	for _, key := range keys {
+		args = append(args, key)
+	}
+	return p.commandReturnStringSlice(args...)
+}
+
+func (p *RedisCache) SetBit(key string, offset int64, value int) (int64, error) {
+	if p.isCluster {
+		return p.cluster.SetBit(key, offset, value).Result()
+	}
+
+	return p.client.SetBit(key, offset, value).Result()
+}
+
+func (p *RedisCache) GetBit(key string, offset int64) (int64, error) {
+	if p.isCluster {
+		return p.cluster.GetBit(key, offset).Result()
+	}
+
+	return p.client.GetBit(key, offset).Result()
+}
+
+func (p *RedisCache) SetNx(key string, value interface{}) (bool, error) {
+	if p.isCluster {
+		return p.cluster.SetNX(key, value, 0).Result()
+	}
+
+	return p.client.SetNX(key, value, 0).Result()
+}
+
+func (p *RedisCache) SetNxEx(key string, value interface{}, seconds int64) (bool, error) {
+	if p.isCluster {
+		return p.cluster.SetNX(key, value, time.Duration(seconds)*time.Second).Result()
+	}
+
+	return p.client.SetNX(key, value, time.Duration(seconds)*time.Second).Result()
+}
+
+func (p *RedisCache) Append(key, value string) (int64, error) {
+	if p.isCluster {
+		return p.cluster.Append(key, value).Result()
+	}
+
+	return p.client.Append(key, value).Result()
+}

+ 120 - 0
common.in/cache/zset.go

@@ -0,0 +1,120 @@
+package cache
+
+import (
+	"fmt"
+
+	"github.com/go-redis/redis"
+)
+
+func (p *RedisCache) ZRevRange(key string, start, stop int64) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.ZRevRange(key, start, stop).Result()
+	}
+
+	return p.client.ZRevRange(key, start, stop).Result()
+}
+
+func (p *RedisCache) ZRevRangeByScore(key string, max, min string, offset, count int64) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.ZRevRangeByScore(key,
+			redis.ZRangeBy{
+				Min:    min,
+				Max:    max,
+				Offset: offset,
+				Count:  count,
+			}).Result()
+	}
+
+	return p.client.ZRevRangeByScore(key,
+		redis.ZRangeBy{
+			Min:    min,
+			Max:    max,
+			Offset: offset,
+			Count:  count,
+		}).Result()
+}
+
+func (p *RedisCache) ZAdd(key string, score float64, member interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.ZAdd(key,
+			redis.Z{
+				Score:  score,
+				Member: member,
+			}).Result()
+	}
+
+	return p.client.ZAdd(key,
+		redis.Z{
+			Score:  score,
+			Member: member,
+		}).Result()
+}
+
+func (p *RedisCache) ZCount(key, min, max string) (int64, error) {
+	if p.isCluster {
+		return p.cluster.ZCount(key, min, max).Result()
+	}
+
+	return p.client.ZCount(key, min, max).Result()
+}
+
+func (p *RedisCache) ZScore(key, member string) (float64, error) {
+	if p.isCluster {
+		return p.cluster.ZScore(key, member).Result()
+	}
+
+	return p.client.ZScore(key, member).Result()
+}
+
+func (p *RedisCache) ZRange(key string, start, stop int64) ([]string, error) {
+	if p.isCluster {
+		return p.cluster.ZRange(key, start, stop).Result()
+	}
+
+	return p.client.ZRange(key, start, stop).Result()
+}
+
+func (p *RedisCache) ZIncrBy(key string, incr float64, member string) (float64, error) {
+	if p.isCluster {
+		return p.cluster.ZIncrBy(key, incr, member).Result()
+	}
+
+	return p.client.ZIncrBy(key, incr, member).Result()
+}
+
+func (p *RedisCache) ZRem(key string, members ...interface{}) (int64, error) {
+	if p.isCluster {
+		return p.cluster.ZRem(key, members).Result()
+	}
+
+	return p.client.ZRem(key, members).Result()
+}
+
+func (p *RedisCache) ZUnionStore(dest string, weights []float64, aggregate string, keys ...string) (int64, error) {
+	if p.isCluster {
+		return 0, fmt.Errorf("cluster unsupport <zunionstore>.")
+	}
+
+	return p.client.ZUnionStore(dest,
+		redis.ZStore{
+			Weights:   weights,
+			Aggregate: aggregate,
+		},
+		keys...).Result()
+}
+
+func (p *RedisCache) ZCard(key string) (int64, error) {
+	if p.isCluster {
+		return p.cluster.ZCard(key).Result()
+	}
+
+	return p.client.ZCard(key).Result()
+}
+
+func (p *RedisCache) ZRemRangeByScore(key, min, max string) (int64, error) {
+	if p.isCluster {
+		return p.cluster.ZRemRangeByScore(key, min, max).Result()
+	}
+
+	return p.client.ZRemRangeByScore(key, min, max).Result()
+}

+ 41 - 0
common.in/clinit/elastic.go-bk

@@ -0,0 +1,41 @@
+package clinit
+
+import (
+	elastic "gopkg.in/olivere/elastic.v5"
+)
+
+type ElasticErrorLog struct {
+}
+
+func (errLog *ElasticErrorLog) Printf(format string, v ...interface{}) {
+	Error("", format, v...)
+}
+
+type ElasticInfoLog struct {
+}
+
+func (infoLog *ElasticInfoLog) Printf(format string, v ...interface{}) {
+	Info("", format, v...)
+}
+
+type ElasticDebugLog struct {
+}
+
+func (debugLog *ElasticDebugLog) Printf(format string, v ...interface{}) {
+	Debug("", format, v...)
+}
+
+var elasticClient *elastic.Client
+
+func InitElastic(addr string, sniff bool) {
+	var err error
+	elasticClient, err = elastic.NewClient(elastic.SetTraceLog(new(ElasticDebugLog)), elastic.SetInfoLog(new(ElasticInfoLog)),
+		elastic.SetErrorLog(new(ElasticErrorLog)), elastic.SetSniff(sniff), elastic.SetURL(addr))
+	if err != nil {
+		panic(err)
+	}
+}
+
+func GetElasticClient() *elastic.Client {
+	return elasticClient
+}

+ 60 - 0
common.in/clinit/etcd.go

@@ -0,0 +1,60 @@
+package clinit
+
+/*
+import (
+	"fmt"
+	"os"
+	"strings"
+	"time"
+
+	"go.etcd.io/etcd/client"
+)
+
+var etcdClient client.Client
+
+func InitEtcd(etcdaddrs []string) {
+	endpoints := []string{}
+	for _, addr := range etcdaddrs {
+		if strings.HasPrefix(addr, "http://") {
+			endpoints = append(endpoints, addr)
+		} else {
+			endpoints = append(endpoints, fmt.Sprintf("http://%s", addr))
+		}
+	}
+	cli, err := client.New(client.Config{
+		Endpoints:               endpoints,
+		Transport:               client.DefaultTransport,
+		HeaderTimeoutPerRequest: 5 * time.Second,
+	})
+	if err != nil {
+		fmt.Printf("new etcd client failed. error:%s\n", err)
+		os.Exit(1)
+	}
+	etcdClient = cli
+}
+
+func GetEtcdClient() client.Client {
+	return etcdClient
+}*/
+
+import (
+	clientv3 "go.etcd.io/etcd/client/v3"
+)
+
+var cli *clientv3.Client
+
+func GetEtcdClient() *clientv3.Client {
+	return cli
+}
+
+func EctdHandler(etcdAddrs []string) {
+	var err error
+	cli, err = clientv3.New(clientv3.Config{
+		Endpoints: etcdAddrs,
+	})
+
+	if err != nil {
+		panic(err)
+	}
+	return
+}

+ 21 - 0
common.in/clinit/id.go

@@ -0,0 +1,21 @@
+package clinit
+
+import (
+	"github.com/sony/sonyflake"
+	"github.com/sony/sonyflake/awsutil"
+)
+
+var traceSF *sonyflake.Sonyflake
+
+func InitUniqueID() {
+	var st sonyflake.Settings
+	st.MachineID = awsutil.AmazonEC2MachineID
+	traceSF = sonyflake.NewSonyflake(st)
+	if traceSF == nil {
+		panic("init unique id panic")
+	}
+}
+
+func GetTraceID() (uint64, error) {
+	return traceSF.NextID()
+}

+ 26 - 0
common.in/clinit/mongo.go

@@ -0,0 +1,26 @@
+package clinit
+
+import (
+	"fmt"
+	"gopkg.in/mgo.v2"
+	"os"
+	"strings"
+	"time"
+)
+
+var MongoSession *mgo.Session
+
+func InitMongo(address, username, password string) {
+	addressList := strings.Split(address, ",")
+	dialInfo := &mgo.DialInfo{Addrs: addressList, Username: username, Password: password, Timeout: time.Second * 60}
+	session, err := mgo.DialWithInfo(dialInfo)
+	if err != nil {
+		fmt.Printf("new mongo client failed. error:%s\n", err)
+		os.Exit(1)
+	}
+	MongoSession = session
+}
+
+func GetMongoSession() *mgo.Session {
+	return MongoSession.Clone()
+}

+ 60 - 0
common.in/clinit/mysq-gorm.go

@@ -0,0 +1,60 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package clinit
+
+import (
+	"encoding/json"
+	"fmt"
+	"log"
+
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+)
+
+var db *gorm.DB
+
+// Setup 建立连接
+func InitMysqlGorm(user, passwd, addr, dbname, charset string, maxIdle, maxConn json.Number, logMode bool) *gorm.DB {
+	args := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=%s&parseTime=True&loc=Local",
+		user, passwd, addr, dbname, charset)
+	fmt.Println("args", args)
+	var err error
+	db, err = gorm.Open(mysql.Open(args), &gorm.Config{})
+	if err != nil {
+		log.Fatal("open mysql connection failed. err: ", err)
+	}
+
+	if logMode {
+		db.Debug()
+		//db.Logger.LogMode(logger.Info)
+	} else {
+		//db.Logger.LogMode(logger.Error)
+	}
+
+	maxIdleInt, _ := maxIdle.Int64()
+	maxConnInt, _ := maxConn.Int64()
+	sqlDb, err := db.DB()
+	sqlDb.SetMaxIdleConns(int(maxIdleInt))
+	sqlDb.SetMaxOpenConns(int(maxConnInt))
+
+	// 其他设置
+	//fmt.Println("log mode:", logMode)
+	/*db.LogMode(logMode)
+	db.DB().SetMaxIdleConns(maxIdle)
+	db.DB().SetMaxOpenConns(maxConn)*/
+	return db
+}
+
+// DB 获取连接
+func DB() *gorm.DB {
+	return db
+}
+
+/*
+// Close 关闭连接
+func Close() {
+	if db != nil {
+		db.Close()
+	}
+}*/

+ 41 - 0
common.in/clinit/mysql.go

@@ -0,0 +1,41 @@
+package clinit
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/astaxie/beego/orm"
+)
+
+type MysqlConfig struct {
+	User     string      `json:"user"`
+	Password string      `json:"password"`
+	Addr     string      `json:"addr"`
+	DB       string      `json:"db"`
+	Charset  string      `json:"charset"`
+	MaxIdle  json.Number `json:"max_idle"`
+	MaxConn  json.Number `json:"max_conn"`
+}
+
+func InitMySQL(conf *MysqlConfig) {
+	dataSource := fmt.Sprintf(`%s:%s@tcp(%s)/%s?charset=%s`, conf.User, conf.Password, conf.Addr, conf.DB, conf.Charset)
+	if err := orm.RegisterDriver("mysql", orm.DRMySQL); err != nil {
+		fmt.Printf("register mysql driver failed. error:%s\n", err)
+		os.Exit(1)
+	}
+
+	maxIdle, _ := conf.MaxIdle.Int64()
+	maxConn, _ := conf.MaxConn.Int64()
+	if err := orm.RegisterDataBase("default", "mysql", dataSource, int(maxIdle), int(maxConn)); err != nil {
+		fmt.Printf("register mysql data base failed. error:%s", err)
+		os.Exit(1)
+	}
+	db, err := orm.GetDB()
+	if err != nil {
+		fmt.Printf("get mysql db failed. error:%s\n", err)
+		os.Exit(1)
+	}
+	db.SetConnMaxLifetime(time.Hour * 3)
+}

+ 331 - 0
common.in/config/config.go

@@ -0,0 +1,331 @@
+package config
+
+import (
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"time"
+
+	"github.com/fsnotify/fsnotify"
+)
+
+var Conf *Configure
+
+var ConfigPath = "conf/common.json"
+
+type KeepaliveConfig struct {
+	ClientTime     json.Number
+	ClientTimeout  json.Number
+	ServerTime     json.Number
+	ServerTimeout  json.Number
+	ServerMiniTime json.Number
+}
+
+type OssConfig struct {
+	AccessKey          string `json:"access_key"`
+	AccessSecret       string `json:"access_secret"`
+	Bucket             string `json:"bucket"`
+	EndPoint           string `json:"end_point"`
+	DefaultBrandImage  string `json:"default_brand_image"`
+	BrandImage         string `json:"brand_image"`
+	DefaultSeriesImage string `json:"default_series_image"`
+	SeriesImage        string `json:"series_image"`
+}
+
+// mysql 配置
+type MysqlConfig struct {
+	User     string      `json:"user"`
+	Password string      `json:"password"`
+	Addr     string      `json:"addr"`
+	Db       string      `json:"db"`
+	Charset  string      `json:"charset"`
+	MaxIdle  json.Number `json:"max_idle"`
+	MaxConn  json.Number `json:"max_conn"`
+	LogMode  string      `json:"log_mode"`
+}
+
+type MongoConfig struct {
+	User     string `json:"user"`
+	Password string `json:"password"`
+	Addr     string `json:"addr"`
+}
+
+// redis 配置
+type RedisConfig struct {
+	Addrs        string      `json:"addrs"`
+	Password     string      `json:"password"`
+	Db           json.Number `json:"db"`
+	PoolSize     json.Number `json:"pool_size"`
+	MinIdleConns json.Number `json:"min_idle_conns"`
+	MaxRetries   json.Number `json:"max_retries"`
+	IsCluster    string      `json:"is_cluster"`
+}
+
+type ElasticConfig struct {
+	Addrs string `json:"addrs"`
+	Sniff string `json:"sniff"`
+}
+
+type LogConfig struct {
+	MaxSize    json.Number `json:"max_size"`
+	MaxBackups json.Number `json:"max_backups"`
+	MaxAge     json.Number `json:"max_age"`
+	Level      string      `json:"level"`
+	Stacktrace string      `json:"stacktrace"`
+	Path       string      `json:"path"`
+}
+
+type RPCNode struct {
+	ServiceName   string      `json:"service_name"`
+	ServicePort   string      `json:"service_port"`
+	ServiceIp     string      `json:"service_ip"`
+	MysqlDb       string      `json:"mysql_db"`
+	RedisDb       json.Number `json:"redis_db"`
+	LogLevel      string      `json:"log_level"`
+	LogStacktrace string      `json:"log_stacktrace"`
+}
+
+type RPCConfig struct {
+	Prefix    string
+	Keepalive KeepaliveConfig
+	AdmDws    RPCNode `json:"adm_dws"`
+	AdmOds    RPCNode `json:"adm_ods"`
+	AdmAds    RPCNode `json:"adm_ads"`
+	AdmTask   RPCNode `json:"adm_task"`
+}
+
+type RabbitmqConfig struct {
+	Addr          string      `json:"addr"`
+	Username      string      `json:"username"`
+	Passwrod      string      `json:"passwrod"`
+	Vhost         string      `json:"vhost"`
+	ExchangeName  string      `json:"exchange_name"`
+	QueueName     string      `json:"queue_name"`
+	RouteBindKey  string      `json:"route_bind_key"`
+	ConsumerCount json.Number `json:"consumer_count"`
+}
+
+type Configure struct {
+	RunMode     string         `json:"run_mode"`
+	Log         LogConfig      `json:"log"`
+	Mysql       MysqlConfig    `json:"mysql"`
+	Mongo       MongoConfig    `json:"mongo"`
+	Redis       RedisConfig    `json:"redis"`
+	Elastic     ElasticConfig  `json:"elastic"`
+	Oss         OssConfig      `json:"oss"`
+	Rpc         RPCConfig      `json:"rpc"`
+	OdsRabbitmq RabbitmqConfig `json:"ods_rabbitmq"`
+	DwsRabbitmq RabbitmqConfig `json:"dws_rabbitmq"`
+	AdsRabbitmq RabbitmqConfig `json:"ads_rabbitmq"`
+}
+
+/*
+func watchEtcd(keysAPI client.KeysAPI, index uint64, runmode, key string, cli client.Client) {
+	basePath := fmt.Sprintf("/%s/config", runmode)
+	watcherOptions := &client.WatcherOptions{
+		AfterIndex: index,
+		Recursive:  true,
+	}
+	watcher := keysAPI.Watcher(basePath, watcherOptions)
+	for {
+		r, err := watcher.Next(context.Background())
+		if err != nil || r == nil {
+			break
+		}
+		if r.Node != nil && r.PrevNode != nil {
+			if r.Node.Key == r.PrevNode.Key && (r.Node.Value != r.PrevNode.Value) {
+				reloadEtcd(runmode, key, cli)
+			}
+		}
+		if r.Node != nil && r.PrevNode == nil {
+			reloadEtcd(runmode, key, cli)
+		}
+	}
+}
+
+func reloadEtcd(runmode, key string, cli client.Client) {
+	keysAPI := client.NewKeysAPI(cli)
+	basePath := fmt.Sprintf("/%s/config", runmode)
+	if resp, err := keysAPI.Get(context.Background(), basePath, &client.GetOptions{
+		Recursive: true,
+	}); err == nil && resp != nil && resp.Node != nil {
+		conf := &Configure{}
+		value := getNodeData(key, resp.Node)
+		if jsonStr, err := json.Marshal(value); err == nil {
+			if err := json.Unmarshal(jsonStr, &conf); err == nil {
+				*Conf = *conf
+				return
+			} else {
+				fmt.Printf("json Unmarshal failed. error:%s", err)
+			}
+		} else {
+			fmt.Printf("json Marshal failed. error:%s", err)
+		}
+	} else {
+		fmt.Printf("get %s failed. error:%s", basePath, err)
+	}
+}
+
+// key 为加密密钥
+func GetConfig(runmode, key string, cli client.Client) *Configure {
+	keysAPI := client.NewKeysAPI(cli)
+	basePath := fmt.Sprintf("/%s/config", runmode)
+	if resp, err := keysAPI.Get(context.Background(), basePath, &client.GetOptions{
+		Recursive: true,
+	}); err == nil && resp != nil && resp.Node != nil {
+		Conf = &Configure{}
+		value := getNodeData(key, resp.Node)
+		if jsonStr, err := json.Marshal(value); err == nil {
+			if err := json.Unmarshal(jsonStr, &Conf); err == nil {
+				go watchEtcd(keysAPI, resp.Index, runmode, key, cli)
+				return Conf
+			} else {
+				fmt.Printf("json Unmarshal failed. error:%s", err)
+			}
+		} else {
+			fmt.Printf("json Marshal failed. error:%s", err)
+		}
+	} else {
+		fmt.Printf("get %s failed. error:%s", basePath, err)
+		os.Exit(1)
+	}
+
+	return nil
+}
+
+// 递归取出node的叶子节点值
+func getNodeData(key string, head *client.Node) (value interface{}) {
+	s0 := strings.Split(head.Key, "/")
+	len0 := len(s0)
+	if len0 == 0 {
+		return
+	}
+
+	if head.Dir {
+		mapData := map[string]interface{}{}
+		for _, node := range head.Nodes {
+			s1 := strings.Split(node.Key, "/")
+			len1 := len(s1)
+			if len1 == 0 {
+				break
+			}
+
+			mapData[s1[len1-1]] = getNodeData(key, node)
+		}
+
+		value = mapData
+	} else {
+		if key != "" && head.Value != "" {
+			if bytesData, err := Base64URLDecode(head.Value); err != nil {
+				fmt.Printf("Base64URLDecode(%s) failed. error:%s", head.Value, err)
+				os.Exit(1)
+			} else {
+				if data, err := AesDecrypt(bytesData, []byte(key)); err != nil {
+					fmt.Printf("AesDecrypt failed. error:%s", err)
+					os.Exit(1)
+				} else {
+					value = string(data)
+				}
+			}
+		} else {
+			// 无加密,直接取值
+			value = head.Value
+		}
+	}
+
+	return
+}
+*/
+// 适配k8s 方式
+// 公共配置会以configmap的方式映射到容器的conf/common.json中
+
+func SetConfigFile(configPath string) {
+	ConfigPath = configPath
+}
+func GetConfigForK8s() *Configure {
+	buffer, err := ioutil.ReadFile(ConfigPath)
+	if err != nil {
+		fmt.Printf("get %s failed. error:%s", ConfigPath, err)
+		return nil
+	}
+	Conf = &Configure{}
+	if err := json.Unmarshal(buffer, Conf); err != nil {
+		fmt.Printf("json Unmarshal failed. error:%s", err)
+		return nil
+	}
+
+	go watchConfigFileForK8s()
+	return Conf
+}
+
+func ReloadConfigForK8s() {
+	buffer, err := ioutil.ReadFile(ConfigPath)
+	if err != nil {
+		fmt.Printf("get %s failed. error:%s", ConfigPath, err)
+	}
+	confTmp := &Configure{}
+	if err := json.Unmarshal(buffer, confTmp); err != nil {
+		fmt.Printf("json Unmarshal failed. error:%s", err)
+	}
+	Conf = confTmp
+}
+
+// 判断路径文件/文件夹是否存在
+func Exists(path string) bool {
+	_, err := os.Stat(path)
+	if err != nil {
+		if os.IsExist(err) {
+			return true
+		}
+		return false
+	}
+	return true
+}
+
+func watchConfigFileForK8s() {
+	fileExist := true
+	watch, err := fsnotify.NewWatcher()
+	if err != nil {
+		fmt.Printf("new file watcher failed\n\n")
+		os.Exit(1)
+	}
+
+	defer watch.Close()
+
+	/*err = watch.Add(ConfigPath)
+	if err != nil {
+		fmt.Printf("add file watcher failed\n\n")
+		os.Exit(1)
+	}*/
+
+	for {
+		// 判断文件是否存在
+		if !Exists(ConfigPath) {
+			time.Sleep(10 * time.Second)
+			fileExist = false
+			continue
+		} else {
+			watch.Remove(ConfigPath)
+			watch.Add(ConfigPath)
+			if !fileExist { // 文件重新创建
+				ReloadConfigForK8s()
+			}
+			fileExist = true
+		}
+
+		select {
+		case ev := <-watch.Events:
+			{
+				fmt.Println("op : ", ev.Op)
+				ReloadConfigForK8s()
+
+			}
+		case err := <-watch.Errors:
+			{
+				fmt.Println("error : ", err)
+				continue
+			}
+		}
+	}
+}

+ 126 - 0
common.in/config/ecb.go

@@ -0,0 +1,126 @@
+package config
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"encoding/base64"
+	"fmt"
+	"strings"
+)
+
+func Base64URLDecode(data string) ([]byte, error) {
+	var missing = (4 - len(data)%4) % 4
+	data += strings.Repeat("=", missing)
+	return base64.URLEncoding.DecodeString(data)
+}
+
+func Base64UrlSafeEncode(source []byte) string {
+	// Base64 Url Safe is the same as Base64 but does not contain '/' and '+' (replaced by '_' and '-') and trailing '=' are removed.
+	bytearr := base64.StdEncoding.EncodeToString(source)
+	safeurl := strings.Replace(string(bytearr), "/", "_", -1)
+	safeurl = strings.Replace(safeurl, "+", "-", -1)
+	safeurl = strings.Replace(safeurl, "=", "", -1)
+	return safeurl
+}
+
+func AesDecrypt(crypted, key []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+	blockMode := NewECBDecrypter(block)
+	origData := make([]byte, len(crypted))
+	blockMode.CryptBlocks(origData, crypted)
+	origData = PKCS5UnPadding(origData)
+	return origData, nil
+}
+
+func AesEncrypt(src, key string) ([]byte, error) {
+	block, err := aes.NewCipher([]byte(key))
+	if err != nil {
+		return nil, err
+	}
+
+	ecb := NewECBEncrypter(block)
+	content := []byte(src)
+	content = PKCS5Padding(content, block.BlockSize())
+	crypted := make([]byte, len(content))
+	ecb.CryptBlocks(crypted, content)
+
+	return crypted, nil
+}
+
+func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+func PKCS5UnPadding(origData []byte) []byte {
+	length := len(origData)
+	unpadding := int(origData[length-1])
+	return origData[:(length - unpadding)]
+}
+
+type ecb struct {
+	b         cipher.Block
+	blockSize int
+}
+
+func newECB(b cipher.Block) *ecb {
+	return &ecb{
+		b:         b,
+		blockSize: b.BlockSize(),
+	}
+}
+
+type ecbEncrypter ecb
+
+// NewECBEncrypter returns a BlockMode which encrypts in electronic code book mode, using the given Block.
+func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
+	return (*ecbEncrypter)(newECB(b))
+}
+
+func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
+
+func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
+	if len(src)%x.blockSize != 0 {
+		fmt.Println("crypto/cipher: input not full blocks")
+		return
+	}
+	if len(dst) < len(src) {
+		fmt.Println("crypto/cipher: output smaller than input")
+		return
+	}
+	for len(src) > 0 {
+		x.b.Encrypt(dst, src[:x.blockSize])
+		src = src[x.blockSize:]
+		dst = dst[x.blockSize:]
+	}
+}
+
+type ecbDecrypter ecb
+
+// NewECBDecrypter returns a BlockMode which decrypts in electronic code book mode, using the given Block.
+func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
+	return (*ecbDecrypter)(newECB(b))
+}
+
+func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
+
+func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
+	if len(src)%x.blockSize != 0 {
+		fmt.Println("crypto/cipher: input not full blocks")
+		return
+	}
+	if len(dst) < len(src) {
+		fmt.Println("crypto/cipher: output smaller than input")
+		return
+	}
+	for len(src) > 0 {
+		x.b.Decrypt(dst, src[:x.blockSize])
+		src = src[x.blockSize:]
+		dst = dst[x.blockSize:]
+	}
+}

+ 30 - 0
common.in/config/ecb_test.go

@@ -0,0 +1,30 @@
+package config
+
+import (
+	"encoding/base64"
+	"testing"
+)
+
+func Test_AesEncryt(t *testing.T) {
+	if data, err := AesEncrypt("abc", "08d77a89db24d8a6271f488462b500ce"); err == nil {
+		baseData := base64.StdEncoding.EncodeToString(data)
+		t.Log(baseData)
+	} else {
+		t.Error(err)
+	}
+}
+
+func Test_AesDecryt(t *testing.T) {
+	if src, err := base64.StdEncoding.DecodeString("RDlKksejWobcc/2V8XBVHQ=="); err == nil {
+		if data, err := AesDecrypt(src, []byte("08d77a89db24d8a6271f488462b500ce")); err == nil {
+			t.Log(string(data))
+			if string(data) != "abc" {
+				t.Errorf("AesDecrypt failed. data:%s", string(data))
+			}
+		} else {
+			t.Error(err)
+		}
+	} else {
+		t.Error(err)
+	}
+}

+ 12 - 0
common.in/id/id.go

@@ -0,0 +1,12 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package id
+
+// 获取分布式唯一id
+func GetDistributedUniqueID() (uint64, error) {
+	if sf == nil {
+		return 0, nil
+	}
+	return sf.NextID()
+}

+ 12 - 0
common.in/id/id_test.go

@@ -0,0 +1,12 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package id
+
+import (
+	"testing"
+)
+
+func Test_ID(t *testing.T) {
+	t.Log(GetDistributedUniqueID())
+}

+ 25 - 0
common.in/id/init.go

@@ -0,0 +1,25 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+// package id 初始化uniqueId,并提供方法
+package id
+
+import (
+	"time"
+
+	"github.com/sony/sonyflake"
+)
+
+var sf *sonyflake.Sonyflake
+
+func init() {
+	st := sonyflake.Settings{
+		StartTime: time.Date(2010, time.October, 10, 10, 10, 10, 0, time.UTC),
+	}
+
+	sf = sonyflake.NewSonyflake(st)
+	if sf == nil {
+		panic("init unique id panic")
+	}
+
+}

+ 49 - 0
common.in/jsonrpc2/errors.go

@@ -0,0 +1,49 @@
+package jsonrpc2
+
+import (
+	"encoding/json"
+	"fmt"
+)
+
+var (
+	errServerError = NewError(-1, "Error: json.Marshal failed")
+)
+
+// Error represent JSON-RPC 2.0 "Error object".
+type Error struct {
+	Code    int    `json:"code"`
+	Message string `json:"message"`
+}
+
+// NewError returns an Error with given code and message.
+func NewError(code int, message string) *Error {
+	return &Error{Code: code, Message: message}
+}
+
+// NewError returns an Error with given code and message.
+func NewJsonError(code int, message string) error {
+	e := &Error{Code: code, Message: message}
+	buf, err := json.Marshal(e)
+	if err != nil {
+		msg, err := json.Marshal(err.Error())
+		if err != nil {
+			msg = []byte(`"` + errServerError.Message + `"`)
+		}
+		return fmt.Errorf(`{"code":%d,"message":%s}`, errServerError.Code, string(msg))
+	}
+	return fmt.Errorf(string(buf))
+
+}
+
+// Error returns JSON representation of Error.
+func (e *Error) Error() string {
+	buf, err := json.Marshal(e)
+	if err != nil {
+		msg, err := json.Marshal(err.Error())
+		if err != nil {
+			msg = []byte(`"` + errServerError.Message + `"`)
+		}
+		return fmt.Sprintf(`{"code":%d,"message":%s}`, errServerError.Code, string(msg))
+	}
+	return string(buf)
+}

+ 140 - 0
common.in/logger/zap.go

@@ -0,0 +1,140 @@
+package logger
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+	"time"
+
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"gopkg.in/natefinch/lumberjack.v2"
+)
+
+type ZapConfig struct {
+	Level             zap.AtomicLevel `json:"level" yaml:"level"`
+	Development       bool            `json:"development" yaml:"development"`
+	DisableCaller     bool            `json:"disableCaller" yaml:"disableCaller"`
+	DisableStacktrace bool            `json:"disableStacktrace" yaml:"disableStacktrace"`
+	Encoding          string          `json:"encoding" yaml:"encoding"`
+	OutputPath        string          `json:"outputPath" yaml:"outputPath"`
+}
+
+// init log
+func InitLogger(runmode, logPath string, appname, level string, maxSize, maxBackups, maxAge int, disableStacktrace bool) *zap.Logger {
+	encoding := "json"
+	development := true
+	if runmode == "prod" {
+		level = "info"
+		development = false
+	}
+
+	js := fmt.Sprintf(`{
+        "level": "%s",
+        "encoding": "%s",
+        "outputPath": "%s"
+        }`, level, encoding, logPath)
+
+	var zcfg ZapConfig
+	if err := json.Unmarshal([]byte(js), &zcfg); err != nil {
+		panic(err)
+	}
+
+	zcfg.Development = development
+	zcfg.DisableStacktrace = disableStacktrace
+
+	return zcfg.New(appname, maxSize, maxBackups, maxAge)
+}
+
+func NewInfoLogger(runmode, logPath string, appname, level string, maxSize, maxBackups, maxAge int) *zap.Logger {
+	encoding := "json"
+	development := true
+	if runmode == "prod" {
+		development = false
+	}
+
+	js := fmt.Sprintf(`{
+        "level": "%s",
+        "encoding": "%s",
+        "outputPath": "%s"
+        }`, level, encoding, logPath)
+
+	var zcfg ZapConfig
+	if err := json.Unmarshal([]byte(js), &zcfg); err != nil {
+		panic(err)
+	}
+
+	zcfg.Development = development
+	zcfg.DisableStacktrace = true
+
+	return zcfg.New(appname, maxSize, maxBackups, maxAge)
+}
+
+func (zc *ZapConfig) New(appname string, maxSize, maxBackups, maxAge int) *zap.Logger {
+	// if nil, default the values
+	if maxSize == 0 {
+		maxSize = 100
+	}
+
+	if maxBackups == 0 {
+		maxBackups = 7
+	}
+
+	if maxAge == 0 {
+		maxAge = 30
+	}
+
+	hook := lumberjack.Logger{
+		Filename:   zc.OutputPath,
+		MaxSize:    maxSize,    // megabytes
+		MaxBackups: maxBackups, // backups number
+		MaxAge:     maxAge,     // days
+		Compress:   true,       // disabled by default
+	}
+
+	fileWriter := zapcore.AddSync(&hook)
+	var EncoderConfig zapcore.EncoderConfig
+	if zc.Development {
+		EncoderConfig = zap.NewDevelopmentEncoderConfig()
+	} else {
+		EncoderConfig = zap.NewProductionEncoderConfig()
+	}
+	// 自定义
+	EncoderConfig.EncodeTime = CustomTimeEncoder
+
+	var encoder zapcore.Encoder
+	if strings.ToLower(zc.Encoding) == "console" {
+		encoder = zapcore.NewConsoleEncoder(EncoderConfig)
+	} else {
+		encoder = zapcore.NewJSONEncoder(EncoderConfig)
+	}
+	core := zapcore.NewCore(encoder, fileWriter, zc.Level)
+
+	opts := zc.buildOptions(fileWriter)
+	// 设置初始化字段
+	opts = append(opts, zap.Fields(zap.String("appname", appname)))
+	return zap.New(core, opts...)
+}
+
+func (zc *ZapConfig) buildOptions(fileWriter zapcore.WriteSyncer) []zap.Option {
+	opts := []zap.Option{zap.ErrorOutput(fileWriter)}
+	if !zc.DisableCaller {
+		opts = append(opts, zap.AddCaller())
+	}
+
+	if !zc.DisableStacktrace {
+		stackLevel := zap.ErrorLevel
+		if zc.Development {
+			stackLevel = zap.WarnLevel
+		}
+
+		opts = append(opts, zap.AddStacktrace(stackLevel))
+	}
+
+	return opts
+}
+
+// 自定义时间格式
+func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
+	enc.AppendString(t.Format("2006-01-02 15:04:05.000"))
+}

+ 292 - 0
common.in/mq/rabbitmq.go

@@ -0,0 +1,292 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package mq
+
+import (
+	"gadm-ods/common.in/jsonrpc2"
+	"encoding/json"
+	"fmt"
+	"log"
+
+	//"github.com/go-kit/kit/transport/amqp"
+	//"github.com/go-kit/kit/transport/amqp"
+	"github.com/streadway/amqp"
+	"os"
+	"time"
+)
+
+type RabbitmqClient struct {
+	Addr                 string
+	Username             string
+	Passwrod             string
+	Vhost                string
+	ExchangeName         string
+	QueueName            string
+	RouteBindKey         string
+	Connection           *amqp.Connection
+	PubChannel           *amqp.Channel
+	CallFunc             func([]byte) error
+	IsPub                bool
+	ConsumerChannelCount int
+}
+
+var DwsMq *RabbitmqClient
+var OdsMq *RabbitmqClient
+
+func SetDwsMq(client *RabbitmqClient) {
+	DwsMq = client
+}
+
+func SetOdsMq(client *RabbitmqClient) {
+	OdsMq = client
+}
+
+func InitRabbitmq(addr, username, passwrod, vhost, exchangeName, queueName, routeBindKey string, callFunc func([]byte) error, isPub bool, consumerChannelCount int) *RabbitmqClient {
+	// 创建连接
+	rabbitmqClient := &RabbitmqClient{Addr: addr,
+		Username: username, Passwrod: passwrod,
+		Vhost: vhost, ExchangeName: exchangeName,
+		RouteBindKey: routeBindKey, QueueName: queueName,
+		CallFunc: callFunc, IsPub: isPub,
+		ConsumerChannelCount: consumerChannelCount}
+	var err error
+	rabbitmqClient.Connection, err = rabbitmqClient.Connect()
+	if err != nil {
+		fmt.Println("dial err :", err)
+		os.Exit(1)
+	}
+
+	// 启动发送channel
+	if isPub {
+		err = rabbitmqClient.InitPubChannel()
+		if err != nil {
+			os.Exit(2)
+		}
+	}
+
+	return rabbitmqClient
+}
+
+func (r *RabbitmqClient) StartConsumer() {
+	// 启动消费channel
+	if r.CallFunc != nil {
+		for i := 0; i < r.ConsumerChannelCount; i++ {
+			err := r.InitConsumerChannel(i)
+			if err != nil {
+				os.Exit(3)
+			}
+		}
+	}
+}
+
+func (r *RabbitmqClient) Connect() (*amqp.Connection, error) {
+	// 创建连接
+	connectAddr := fmt.Sprintf("amqp://%s:%s@%s/%s", r.Username, r.Passwrod, r.Addr, r.Vhost)
+	connect, err := amqp.Dial(connectAddr)
+	if err != nil {
+		return nil, err
+	}
+
+	go func() {
+		log.Printf("Closing: %s", <-connect.NotifyClose(make(chan *amqp.Error)))
+		log.Printf("Trying to reconnect")
+		for {
+			err := r.Reconnect()
+			if err != nil {
+				log.Println("reconnect:", err)
+				time.Sleep(3 * time.Second)
+			} else {
+				break
+			}
+		}
+	}()
+
+	return connect, nil
+}
+
+func (r *RabbitmqClient) InitPubChannel() error {
+	// 获取通道
+	var err error
+	r.PubChannel, err = r.Connection.Channel()
+	if err != nil {
+		return err
+	}
+	// 创建交换机
+	err = r.PubChannel.ExchangeDeclare(r.ExchangeName, "topic", true, false, false, true, nil)
+	if err != nil {
+		return err
+	}
+
+	// 创建队列
+	_, err = r.PubChannel.QueueDeclare(r.QueueName, true, false, false, true, nil)
+	if err != nil {
+		return err
+	}
+	//  绑定交换机和队列
+	err = r.PubChannel.QueueBind(r.QueueName, r.RouteBindKey, r.ExchangeName, true, nil)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func (r *RabbitmqClient) InitConsumerChannel(index int) error {
+	// 获取通道
+	channel, err := r.Connection.Channel()
+	if err != nil {
+		return err
+	}
+	// 创建交换机
+	err = channel.ExchangeDeclare(r.ExchangeName, "topic", true, false, false, true, nil)
+	if err != nil {
+		return err
+	}
+
+	// 创建队列
+	_, err = channel.QueueDeclare(r.QueueName, true, false, false, true, nil)
+	if err != nil {
+		return err
+	}
+	//  绑定交换机和队列
+	err = channel.QueueBind(r.QueueName, r.RouteBindKey, r.ExchangeName, true, nil)
+	if err != nil {
+		return err
+	}
+
+	channel.Qos(1, 0, false)
+
+	go ConsumeMsg(r.CallFunc, index, r.QueueName, channel)
+
+	return nil
+}
+
+func ConsumeMsg(callFunc func([]byte) error, index int, queueName string, channel *amqp.Channel) {
+
+	defer func() {
+		if r := recover(); r != nil {
+			err := fmt.Errorf("%+v", r)
+			e := &jsonrpc2.Error{}
+			if er := json.Unmarshal([]byte(err.Error()), e); er != nil {
+				fmt.Println("panic:", err.Error())
+			}
+		}
+	}()
+
+	name := fmt.Sprintf("consumer%d", index)
+	deliveries, err := channel.Consume(queueName, name, false, false, false, true, nil)
+	if err != nil {
+		fmt.Println("consume fail")
+		return
+	}
+
+	//defer channel.Close()
+	closeChan := make(chan *amqp.Error, 1)
+	notifyClose := channel.NotifyClose(closeChan)
+
+	for {
+		select {
+		case e := <-notifyClose:
+			fmt.Printf("chan通道错误,e:%s", e.Error())
+			close(closeChan)
+			return
+		case delivery := <-deliveries:
+			// 手动ack确认
+			// 注意: 这里只要调用了ack就是手动确认模式,
+			// multiple 表示的是在此channel中先前所有未确认的deliveries都将被确认
+			// 并不是表示设置为false就不进行当前ack确认,multiple=true: 用于设置批量消息确认;
+			// 假设:在Channel(ch)上有5,6,7,8这4个delivery(ack) tags未确认;
+			//情况1,delivery_tag=8 & multiple=true: 则5,6,7,8这4个tags都将被确认;
+			//情况2,delivery_tag=8 & multiple=false:则只有8被确认,而5,6,7将不会被被确认;
+			// 不做ack的话,在该消费者连接中断后,会重新投递给其他消费者
+			err := callFunc(delivery.Body)
+			if err != nil {
+				fmt.Println("callFunc:", err.Error())
+			} else {
+				if err := delivery.Ack(false); err != nil {
+					fmt.Println(err.Error())
+				}
+			}
+		}
+		/*v, ok := <-deliveries
+		if ok {
+			// 手动ack确认
+			// 注意: 这里只要调用了ack就是手动确认模式,
+			// multiple 表示的是在此channel中先前所有未确认的deliveries都将被确认
+			// 并不是表示设置为false就不进行当前ack确认,multiple=true: 用于设置批量消息确认;
+			// 假设:在Channel(ch)上有5,6,7,8这4个delivery(ack) tags未确认;
+			//情况1,delivery_tag=8 & multiple=true: 则5,6,7,8这4个tags都将被确认;
+			//情况2,delivery_tag=8 & multiple=false:则只有8被确认,而5,6,7将不会被被确认;
+			// 不做ack的话,在该消费者连接中断后,会重新投递给其他消费者
+			err := callFunc(v.Body)
+			if err != nil {
+				fmt.Println("callFunc:",err.Error())
+			}else{
+				if err := v.Ack(false); err != nil {
+					fmt.Println(err.Error())
+				}
+			}
+		} else {
+			fmt.Println("Channel close")
+			return
+		}*/
+	}
+
+}
+
+func (r *RabbitmqClient) Reconnect() error {
+	// 创建连接
+	var err error
+	r.Connection, err = r.Connect()
+	if err != nil {
+		fmt.Println("dial err :", err)
+		return err
+	}
+
+	// 启动发送channel
+	if r.IsPub {
+		err = r.InitPubChannel()
+		if err != nil {
+			return err
+		}
+	}
+
+	// 启动消费channel
+	if r.CallFunc != nil {
+		for i := 0; i < r.ConsumerChannelCount; i++ {
+			err = r.InitConsumerChannel(i)
+			if err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func (r *RabbitmqClient) PublishMsg(data []byte) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			err = fmt.Errorf("%+v", r)
+			e := &jsonrpc2.Error{}
+			if er := json.Unmarshal([]byte(err.Error()), e); er != nil {
+				fmt.Println("panic:", err.Error())
+			}
+		}
+	}()
+
+	err = r.PubChannel.Publish(r.ExchangeName, r.RouteBindKey, true, false, amqp.Publishing{
+		DeliveryMode: amqp.Persistent, // 消息持久化
+		Timestamp:    time.Now(),
+		ContentType:  "text/plain",
+		Body:         data,
+	})
+
+	if err != nil {
+		fmt.Println("publish msg err:", err)
+	} else {
+		fmt.Println("publish success!!!!")
+	}
+
+	return err
+}

+ 122 - 0
common.in/span/id.go

@@ -0,0 +1,122 @@
+package span
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"crypto/rand"
+	"encoding/json"
+	"fmt"
+	"io"
+	"strconv"
+	"sync"
+	"unsafe"
+)
+
+// An ID is a unique, uniformly distributed 64-bit ID.
+type ID uint64
+
+// String returns the ID as a hex string.
+func (id ID) String() string {
+	return fmt.Sprintf("%016x", uint64(id))
+}
+
+// MarshalJSON encodes the ID as a hex string.
+func (id ID) MarshalJSON() ([]byte, error) {
+	return json.Marshal(id.String())
+}
+
+// UnmarshalJSON decodes the given data as either a hexadecimal string or JSON
+// integer.
+func (id *ID) UnmarshalJSON(data []byte) error {
+	i, err := parseJSONString(data)
+	if err == nil {
+		*id = i
+		return nil
+	}
+	i, err = parseJSONInt(data)
+	if err == nil {
+		*id = i
+		return nil
+	}
+	return fmt.Errorf("%s is not a valid ID", data)
+}
+
+// ParseID parses the given string as a hexadecimal string.
+func ParseID(s string) (ID, error) {
+	i, err := strconv.ParseUint(s, 16, 64)
+	if err != nil {
+		return 0, err
+	}
+	return ID(i), nil
+}
+
+// generateID returns a randomly-generated 64-bit ID. This function is
+// thread-safe.  IDs are produced by consuming an AES-CTR-128 keystream in
+// 64-bit chunks. The AES key is randomly generated on initialization, as is the
+// counter's initial state. On machines with AES-NI support, ID generation takes
+// ~30ns and generates no garbage.
+func generateID() ID {
+	m.Lock()
+	if n == aes.BlockSize {
+		c.Encrypt(b, ctr)
+		for i := aes.BlockSize - 1; i >= 0; i-- { // increment ctr
+			ctr[i]++
+			if ctr[i] != 0 {
+				break
+			}
+		}
+		n = 0
+	}
+	id := *(*ID)(unsafe.Pointer(&b[n])) // zero-copy b/c we're arch-neutral
+	n += idSize
+	m.Unlock()
+	return id
+}
+
+const (
+	idSize  = aes.BlockSize / 2 // 64 bits
+	keySize = aes.BlockSize     // 128 bits
+)
+
+var (
+	ctr []byte
+	n   int
+	b   []byte
+	c   cipher.Block
+	m   sync.Mutex
+)
+
+func init() {
+	buf := make([]byte, keySize+aes.BlockSize)
+	_, err := io.ReadFull(rand.Reader, buf)
+	if err != nil {
+		panic(err) // /dev/urandom had better work
+	}
+	c, err = aes.NewCipher(buf[:keySize])
+	if err != nil {
+		panic(err) // AES had better work
+	}
+	n = aes.BlockSize
+	ctr = buf[keySize:]
+	b = make([]byte, aes.BlockSize)
+}
+
+func parseJSONString(data []byte) (ID, error) {
+	var s string
+	if err := json.Unmarshal(data, &s); err != nil {
+		return 0, err
+	}
+	i, err := ParseID(s)
+	if err != nil {
+		return 0, err
+	}
+	return i, nil
+}
+
+func parseJSONInt(data []byte) (ID, error) {
+	var i uint64
+	if err := json.Unmarshal(data, &i); err != nil {
+		return 0, err
+	}
+	return ID(i), nil
+}

+ 97 - 0
common.in/span/id_test.go

@@ -0,0 +1,97 @@
+package span
+
+import (
+	"bytes"
+	"encoding/json"
+	"strings"
+	"testing"
+)
+
+func TestIDMarshalJSON(t *testing.T) {
+	id := ID(10018820)
+	buf := bytes.NewBuffer(nil)
+	json.NewEncoder(buf).Encode(id)
+	want := `"000000000098e004"`
+	got := strings.TrimSpace(buf.String())
+	if got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestIDUnmarshalJSONHexString(t *testing.T) {
+	j := []byte(`"000000000098e004"`)
+	var got ID
+	if err := json.Unmarshal(j, &got); err != nil {
+		t.Fatal(err)
+	}
+	want := ID(10018820)
+	if got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestIDUnmarshalJSONInt(t *testing.T) {
+	j := []byte(`10018820`)
+	var got ID
+	if err := json.Unmarshal(j, &got); err != nil {
+		t.Fatal(err)
+	}
+	want := ID(10018820)
+	if got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestIDUnmarshalJSONNonInt(t *testing.T) {
+	j := []byte(`[]`)
+	var got ID
+	err := json.Unmarshal(j, &got)
+	if err == nil {
+		t.Fatalf("unexpectedly unmarshalled %v", got)
+	}
+}
+
+func TestIDUnmarshalJSONNonHexString(t *testing.T) {
+	j := []byte(`"woo"`)
+	var got ID
+	err := json.Unmarshal(j, &got)
+	if err == nil {
+		t.Fatalf("unexpectedly unmarshalled %v", got)
+	}
+}
+
+func TestIDGeneration(t *testing.T) {
+	n := 10000
+	ids := make(map[ID]bool, n)
+	for i := 0; i < n; i++ {
+		id := generateID()
+		if ids[id] {
+			t.Errorf("duplicate ID: %v", id)
+		}
+		ids[id] = true
+	}
+}
+
+func TestParseID(t *testing.T) {
+	want := ID(10018181901)
+	got, err := ParseID(want.String())
+	if err != nil {
+		t.Error(err)
+	}
+	if got != want {
+		t.Errorf("got %v, want %v", got, want)
+	}
+}
+
+func TestParseIDError(t *testing.T) {
+	id, err := ParseID("woo")
+	if err == nil {
+		t.Errorf("unexpectedly parsed value: %v", id)
+	}
+}
+
+func BenchmarkIDGeneration(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		generateID()
+	}
+}

+ 113 - 0
common.in/span/span.go

@@ -0,0 +1,113 @@
+package span
+
+import (
+	"errors"
+	"fmt"
+	"strings"
+)
+
+// A SpanID refers to a single span.
+type SpanID struct {
+	// Trace is the root ID of the tree that contains all of the spans
+	// related to this one.
+	Trace ID
+
+	// Span is an ID that probabilistically uniquely identifies this
+	// span.
+	Span ID
+
+	// Parent is the ID of the parent span, if any.
+	Parent ID
+}
+
+var (
+	// ErrBadSpanID is returned when the span ID cannot be parsed.
+	ErrBadSpanID = errors.New("bad span ID")
+)
+
+// String returns the SpanID as a slash-separated, set of hex-encoded
+// parameters (root, ID, parent). If the SpanID has no parent, that value is
+// elided.
+func (id SpanID) String() string {
+	if id.Parent == 0 {
+		return fmt.Sprintf("%s%s%s", id.Trace, SpanIDDelimiter, id.Span)
+	}
+	return fmt.Sprintf(
+		"%s%s%s%s%s",
+		id.Trace,
+		SpanIDDelimiter,
+		id.Span,
+		SpanIDDelimiter,
+		id.Parent,
+	)
+}
+
+// Format formats according to a format specifier and returns the
+// resulting string. The receiver's string representation is the first
+// argument.
+func (id SpanID) Format(s string, args ...interface{}) string {
+	args = append([]interface{}{id.String()}, args...)
+	return fmt.Sprintf(s, args...)
+}
+
+// IsRoot returns whether id is the root ID of a trace.
+func (id SpanID) IsRoot() bool {
+	return id.Parent == 0
+}
+
+// NewRootSpanID generates a new span ID for a root span. This should
+// only be used to generate entries for spans caused exclusively by
+// spans which are outside of your system as a whole (e.g., a root
+// span for the first time you see a user request).
+func NewRootSpanID() SpanID {
+	return SpanID{
+		Trace: generateID(),
+		Span:  generateID(),
+	}
+}
+
+// NewSpanID returns a new ID for an span which is the child of the
+// given parent ID. This should be used to track causal relationships
+// between spans.
+func NewSpanID(parent SpanID) SpanID {
+	return SpanID{
+		Trace:  parent.Trace,
+		Span:   generateID(),
+		Parent: parent.Span,
+	}
+}
+
+const (
+	// SpanIDDelimiter is the delimiter used to concatenate an
+	// SpanID's components.
+	SpanIDDelimiter = "/"
+)
+
+// ParseSpanID parses the given string as a slash-separated set of parameters.
+func ParseSpanID(s string) (*SpanID, error) {
+	parts := strings.Split(s, SpanIDDelimiter)
+	if len(parts) != 2 && len(parts) != 3 {
+		return nil, ErrBadSpanID
+	}
+	root, err := ParseID(parts[0])
+	if err != nil {
+		return nil, ErrBadSpanID
+	}
+	id, err := ParseID(parts[1])
+	if err != nil {
+		return nil, ErrBadSpanID
+	}
+	var parent ID
+	if len(parts) == 3 {
+		i, err := ParseID(parts[2])
+		if err != nil {
+			return nil, ErrBadSpanID
+		}
+		parent = i
+	}
+	return &SpanID{
+		Trace:  root,
+		Span:   id,
+		Parent: parent,
+	}, nil
+}

+ 188 - 0
common.in/span/span_test.go

@@ -0,0 +1,188 @@
+package span
+
+import (
+	"database/sql"
+	"fmt"
+	"testing"
+)
+
+func TestNewRootSpanID(t *testing.T) {
+	id := NewRootSpanID()
+	if id.Parent != 0 {
+		t.Errorf("unexpected parent: %+v", id)
+	}
+	if id.Span == 0 {
+		t.Errorf("zero Span: %+v", id)
+	}
+	if id.Trace == 0 {
+		t.Errorf("zero root: %+v", id)
+	}
+	if id.Trace == id.Span {
+		t.Errorf("duplicate IDs: %+v", id)
+	}
+}
+
+func TestNewSpanID(t *testing.T) {
+	root := NewRootSpanID()
+	id := NewSpanID(root)
+	if id.Parent != root.Span {
+		t.Errorf("unexpected parent: %+v", id)
+	}
+	if id.Span == 0 {
+		t.Errorf("zero Span: %+v", id)
+	}
+	if id.Trace != root.Trace {
+		t.Errorf("mismatched root: %+v", id)
+	}
+}
+
+func TestSpanIDString(t *testing.T) {
+	id := SpanID{
+		Trace: 100,
+		Span:  300,
+	}
+	got := id.String()
+	want := "0000000000000064/000000000000012c"
+	if got != want {
+		t.Errorf("got %#v, want %#v", got, want)
+	}
+}
+
+func TestSpanIDStringWithParent(t *testing.T) {
+	id := SpanID{
+		Trace:  100,
+		Parent: 200,
+		Span:   300,
+	}
+	actual := id.String()
+	expected := "0000000000000064/000000000000012c/00000000000000c8"
+	if actual != expected {
+		t.Errorf("Was %#v, but expected %#v", actual, expected)
+	}
+}
+
+func TestSpanIDFormat(t *testing.T) {
+	id := SpanID{
+		Trace: 100,
+		Span:  300,
+	}
+	got := id.Format("/* %s */ %s", "SELECT 1")
+	want := "/* 0000000000000064/000000000000012c */ SELECT 1"
+	if got != want {
+		t.Errorf("got %#v, want %#v", got, want)
+	}
+}
+
+func ExampleSpanID_Format() {
+	// Assume we're connected to a database.
+	var (
+		event  SpanID
+		db     *sql.DB
+		userID int
+	)
+	// This passes the root ID and the parent event ID to the database, which
+	// allows us to correlate, for example, slow queries with the web requests
+	// which caused them.
+	query := event.Format(`/* %s/%s */ %s`, `SELECT email FROM users WHERE id = ?`)
+	r := db.QueryRow(query, userID)
+	if r == nil {
+		panic("user not found")
+	}
+	var email string
+	if err := r.Scan(&email); err != nil {
+		panic("couldn't read email")
+	}
+	fmt.Printf("User's email: %s\n", email)
+}
+
+func TestParseSpanID(t *testing.T) {
+	id, err := ParseSpanID("0000000000000064/000000000000012c")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if id.Trace != 100 || id.Span != 300 {
+		t.Errorf("unexpected ID: %+v", id)
+	}
+}
+
+func TestParseSpanIDWithParent(t *testing.T) {
+	id, err := ParseSpanID("0000000000000064/000000000000012c/0000000000000096")
+	if err != nil {
+		t.Fatalf("unexpected error: %v", err)
+	}
+	if id.Trace != 100 || id.Parent != 150 || id.Span != 300 {
+		t.Errorf("unexpected event ID: %+v", id)
+	}
+}
+
+func TestParseSpanIDMalformed(t *testing.T) {
+	id, err := ParseSpanID(`0000000000000064000000000000012c`)
+	if id != nil {
+		t.Errorf("unexpected ID: %+v", id)
+	}
+	if err != ErrBadSpanID {
+		t.Error(err)
+	}
+}
+
+func TestParseSpanIDBadTrace(t *testing.T) {
+	id, err := ParseSpanID("0000000000g000064/000000000000012c")
+	if id != nil {
+		t.Errorf("unexpected ID: %+v", id)
+	}
+	if err != ErrBadSpanID {
+		t.Error(err)
+	}
+}
+
+func TestParseSpanIDBadID(t *testing.T) {
+	id, err := ParseSpanID("0000000000000064/0000000000g00012c")
+	if id != nil {
+		t.Errorf("unexpected ID: %+v", id)
+	}
+	if err != ErrBadSpanID {
+		t.Error(err)
+	}
+}
+
+func TestParseSpanIDBadParent(t *testing.T) {
+	id, err := ParseSpanID("0000000000000064/000000000000012c/00000000000g0096")
+	if id != nil {
+		t.Errorf("unexpected event ID: %+v", id)
+	}
+	if err != ErrBadSpanID {
+		t.Errorf("unexpected error: %v", err)
+	}
+}
+func BenchmarkNewRootSpanID(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		NewRootSpanID()
+	}
+}
+
+func BenchmarkNewSpanID(b *testing.B) {
+	root := NewRootSpanID()
+	for i := 0; i < b.N; i++ {
+		NewSpanID(root)
+	}
+}
+
+func BenchmarkSpanIDString(b *testing.B) {
+	id := SpanID{
+		Trace:  100,
+		Parent: 200,
+		Span:   300,
+	}
+	for i := 0; i < b.N; i++ {
+		_ = id.String()
+	}
+}
+
+func BenchmarkParseSpanID(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		_, err := ParseSpanID("0000000000000064/000000000000012c")
+		if err != nil {
+			b.Fatal(err)
+		}
+	}
+}

+ 63 - 0
common.in/storage/db.go

@@ -0,0 +1,63 @@
+package storage
+
+import (
+	"github.com/astaxie/beego/orm"
+)
+
+type DbaTasker interface {
+	Exec(db orm.Ormer) error
+	Rollback(db orm.Ormer) error
+}
+
+type Task func(db orm.Ormer) error
+
+func (t *Task) Exec(db orm.Ormer) error {
+	return (*t)(db)
+}
+
+func (t *Task) Rollback(db orm.Ormer) error {
+	return nil
+}
+
+func GenerateDbaTask(task Task) DbaTasker {
+	return &task
+}
+
+func ExecTrans(tasks ...DbaTasker) error {
+	mysqlORM := orm.NewOrm()
+	successTask := make([]DbaTasker, 0)
+	isCommit := false
+	defer func() {
+		// 捕获异常,并执行回滚操作
+		if v := recover(); v != nil || !isCommit {
+			for _, task := range successTask {
+				task.Rollback(mysqlORM)
+			}
+			mysqlORM.Rollback()
+			//向上层抛出异常
+			if v != nil {
+				panic(v)
+			}
+		}
+	}()
+
+	if err := mysqlORM.Begin(); err != nil {
+		panic("db error")
+	}
+
+	for _, task := range tasks {
+		if task != nil {
+			if err := task.Exec(mysqlORM); err != nil {
+				if err == EXIT_QUERY {
+					return err
+				}
+				panic(err)
+			}
+		}
+		successTask = append(successTask, task)
+	}
+
+	mysqlORM.Commit()
+	isCommit = true
+	return nil
+}

+ 7 - 0
common.in/storage/error.go

@@ -0,0 +1,7 @@
+package storage
+
+import "errors"
+
+var (
+	EXIT_QUERY = errors.New("exit transcation")
+)

+ 29 - 0
common.in/storage/redis.go

@@ -0,0 +1,29 @@
+package storage
+
+import "github.com/astaxie/beego/orm"
+
+type cacheTask struct {
+	execTask     Task
+	rollbackTask Task
+}
+
+func (ct *cacheTask) Exec(db orm.Ormer) error {
+	if ct.execTask != nil {
+		return ct.execTask(db)
+	}
+	return nil
+}
+
+func (ct *cacheTask) Rollback(db orm.Ormer) error {
+	if ct.rollbackTask != nil {
+		return ct.rollbackTask(db)
+	}
+	return nil
+}
+
+func GenerateCacheTask(execTask Task, rollbackTask Task) DbaTasker {
+	return &cacheTask{
+		execTask:     execTask,
+		rollbackTask: rollbackTask,
+	}
+}

+ 25 - 0
common.in/task/access.go

@@ -0,0 +1,25 @@
+package task
+
+import (
+	"context"
+	"fmt"
+	"time"
+
+	"gadm-ods/common.in/utils"
+	"go.uber.org/zap"
+)
+
+func printAccessLog(ctx context.Context, startTime uint64, status string) {
+	if l != nil {
+		// 取出跟踪日志id息
+		spanID := utils.GetSpanFromRpcCtx(ctx)
+		// 结束时间
+		endTime := uint64(time.Now().UnixNano())
+		l.Info(utils.GetMethodFromRpcCtx(ctx),
+			zap.Uint64("trace_id", uint64(spanID.Trace)),
+			zap.Uint64("span_id", uint64(spanID.Span)),
+			zap.Uint64("parent_id", uint64(spanID.Parent)),
+			zap.String("status", status),
+			zap.String("elapsed", fmt.Sprintf("%fms", float64(endTime-startTime)/1000000)))
+	}
+}

+ 11 - 0
common.in/task/logger.go

@@ -0,0 +1,11 @@
+package task
+
+import (
+	"go.uber.org/zap"
+)
+
+var l *zap.Logger
+
+func SetLogger(logger *zap.Logger) {
+	l = logger
+}

+ 48 - 0
common.in/task/task.go

@@ -0,0 +1,48 @@
+package task
+
+import (
+	"context"
+	"gadm-ods/errors"
+	"fmt"
+	"time"
+
+	"gadm-ods/common.in/utils"
+)
+
+type Task func() error
+
+func Do(ctx context.Context, tasks ...Task) (returnErr error) {
+	utils.GC()
+	if utils.ExitRecieved == true {
+		return errors.SystemError
+	}
+	// 开始时间
+	startTime := uint64(time.Now().UnixNano())
+
+	// 抓异常代码
+	defer func() {
+		status := "SUCCESS"
+		if r := recover(); r != nil {
+			if e, ok := r.(error); ok {
+				returnErr = e
+			} else {
+				returnErr = fmt.Errorf("%+v", r)
+			}
+		}
+		if returnErr != nil {
+			status = "FAIL"
+		}
+		printAccessLog(ctx, startTime, status)
+	}()
+
+	for _, task := range tasks {
+		if task != nil {
+			if err := task(); err != nil {
+				returnErr = err
+				return
+			}
+		}
+	}
+
+	return
+}

+ 17 - 0
common.in/utils/base64.go

@@ -0,0 +1,17 @@
+package utils
+
+import (
+	"encoding/base64"
+)
+
+func Base64Encode(src []byte) []byte {
+	return []byte(base64.StdEncoding.EncodeToString(src))
+}
+
+func Base64Decode(src []byte) ([]byte, error) {
+	return base64.StdEncoding.DecodeString(string(src))
+}
+
+func Base64DecodeStr(src string) ([]byte, error) {
+	return base64.StdEncoding.DecodeString(src)
+}

+ 74 - 0
common.in/utils/consistency_hash.go

@@ -0,0 +1,74 @@
+package utils
+
+import (
+	"fmt"
+	"hash/crc32"
+	"sort"
+)
+
+type ConsistentHash struct {
+	VirtualNodes    int
+	virtualNodesMap map[uint32]string
+	actNodes        map[string]string
+	sortRing        []uint32
+}
+
+func NewConsistentHash(virtualNodeNumber int) *ConsistentHash {
+	return &ConsistentHash{
+		VirtualNodes:    virtualNodeNumber,
+		virtualNodesMap: make(map[uint32]string),
+		actNodes:        make(map[string]string),
+		sortRing:        []uint32{},
+	}
+}
+
+func (c *ConsistentHash) Add(node string) bool {
+
+	if _, ok := c.actNodes[node]; ok {
+		return false
+	}
+
+	for i := 0; i < c.VirtualNodes; i++ {
+		c.virtualNodesMap[c.hashStr(fmt.Sprintf("%s#%d", node, i))] = node
+	}
+	c.actNodes[node] = node
+	c.sortHashRing()
+	return true
+}
+
+func (c *ConsistentHash) sortHashRing() {
+	c.sortRing = []uint32{}
+	for k := range c.virtualNodesMap {
+		c.sortRing = append(c.sortRing, k)
+	}
+	sort.Slice(c.sortRing, func(i, j int) bool {
+		return c.sortRing[i] < c.sortRing[j]
+	})
+}
+
+func (c *ConsistentHash) Get(key string) string {
+	hash := c.hashStr(key)
+
+	if len(c.virtualNodesMap) == 0 {
+		return ""
+	}
+	i := c.search(hash)
+	return c.virtualNodesMap[c.sortRing[i]]
+}
+
+func (c *ConsistentHash) hashStr(key string) uint32 {
+	return crc32.ChecksumIEEE([]byte(key))
+}
+
+func (c *ConsistentHash) search(hash uint32) int {
+	i := sort.Search(len(c.sortRing), func(i int) bool { return c.sortRing[i] >= hash })
+	if i < len(c.sortRing) {
+		if i == len(c.sortRing)-1 {
+			return 0
+		} else {
+			return i
+		}
+	} else {
+		return len(c.sortRing) - 1
+	}
+}

+ 70 - 0
common.in/utils/context.go

@@ -0,0 +1,70 @@
+package utils
+
+import (
+	"context"
+
+	"gadm-ods/common.in/span"
+	beegoctx "github.com/astaxie/beego/context"
+	"github.com/smallnest/rpcx/share"
+)
+
+func NewContextFromBeegoCtx(ctx *beegoctx.Context) context.Context {
+	// request metadata
+	metadata := make(map[string]string)
+	if spanID, err := span.ParseSpanID(ctx.Input.Header("span")); err == nil {
+		metadata["span"] = span.NewSpanID(*spanID).String()
+	}
+
+	return context.WithValue(context.Background(), share.ReqMetaDataKey, metadata)
+}
+
+func NewContextFromRpcCtx(ctx context.Context) context.Context {
+	metadata := make(map[string]string)
+	metadata["span"] = span.NewRootSpanID().String() // default new a root spanid
+	if reqMetadata := ctx.Value(share.ReqMetaDataKey); reqMetadata != nil {
+		if mapMD := reqMetadata.(map[string]string); mapMD != nil {
+			if v, ok := mapMD["span"]; ok {
+				if spanID, err := span.ParseSpanID(v); err == nil {
+					metadata["span"] = span.NewSpanID(*spanID).String()
+				}
+			}
+		}
+	}
+
+	return context.WithValue(context.Background(), share.ReqMetaDataKey, metadata)
+}
+
+func GetSpanFromRpcCtx(ctx context.Context) (spanID span.SpanID) {
+	if reqMetadata := ctx.Value(share.ReqMetaDataKey); reqMetadata != nil {
+		if mapMD := reqMetadata.(map[string]string); mapMD != nil {
+			if v, ok := mapMD["span"]; ok {
+				if s, err := span.ParseSpanID(v); err == nil {
+					spanID = *s
+				}
+			}
+		}
+	}
+
+	return
+}
+
+func GetMethodFromRpcCtx(ctx context.Context) (method string) {
+	if reqMetadata := ctx.Value(share.ReqMetaDataKey); reqMetadata != nil {
+		if mapMD := reqMetadata.(map[string]string); mapMD != nil {
+			if v, ok := mapMD["method"]; ok {
+				method = v
+			}
+		}
+	}
+
+	return
+}
+
+func SetMethodToRpcCtx(ctx context.Context, method string) {
+
+	if reqMetadata := ctx.Value(share.ReqMetaDataKey); reqMetadata != nil {
+		if mapMD := reqMetadata.(map[string]string); mapMD != nil {
+			mapMD["method"] = method
+		}
+	}
+}

+ 114 - 0
common.in/utils/copy.go

@@ -0,0 +1,114 @@
+package utils
+
+import (
+	"errors"
+	"reflect"
+)
+
+func DeepFields(ifaceType reflect.Type) []reflect.StructField {
+	var fields []reflect.StructField
+
+	for i := 0; i < ifaceType.NumField(); i++ {
+		v := ifaceType.Field(i)
+		if v.Anonymous && v.Type.Kind() == reflect.Struct {
+			fields = append(fields, DeepFields(v.Type)...)
+		} else {
+			fields = append(fields, v)
+		}
+	}
+
+	return fields
+}
+
+func StructCopy(DstStructPtr interface{}, SrcStructPtr interface{}) error {
+	srcv := reflect.ValueOf(SrcStructPtr)
+	dstv := reflect.ValueOf(DstStructPtr)
+	srct := reflect.TypeOf(SrcStructPtr)
+	dstt := reflect.TypeOf(DstStructPtr)
+	if srct.Kind() != reflect.Ptr || dstt.Kind() != reflect.Ptr ||
+		srct.Elem().Kind() == reflect.Ptr || dstt.Elem().Kind() == reflect.Ptr {
+		return errors.New("Fatal error:type of parameters must be Ptr of value")
+	}
+	if srcv.IsNil() || dstv.IsNil() {
+		return errors.New("Fatal error:value of parameters should not be nil")
+	}
+	srcV := srcv.Elem()
+	dstV := dstv.Elem()
+	srcfields := DeepFields(reflect.ValueOf(SrcStructPtr).Elem().Type())
+	for _, v := range srcfields {
+		if v.Anonymous {
+			continue
+		}
+		dst := dstV.FieldByName(v.Name)
+		src := srcV.FieldByName(v.Name)
+		if !dst.IsValid() {
+			continue
+		}
+		if src.Type() == dst.Type() && dst.CanSet() {
+			dst.Set(src)
+			continue
+		}
+		if src.Kind() == reflect.Ptr && !src.IsNil() && src.Type().Elem() == dst.Type() {
+			dst.Set(src.Elem())
+			continue
+		}
+		if dst.Kind() == reflect.Ptr && dst.Type().Elem() == src.Type() {
+			dst.Set(reflect.New(src.Type()))
+			dst.Elem().Set(src)
+			continue
+		}
+	}
+	return nil
+}
+
+func FieldByTag(value reflect.Value, tag string, tagName string) reflect.Value {
+	itype := value.Type()
+	for i := 0; i < itype.NumField(); i++ {
+		v := itype.Field(i)
+		if v.Tag.Get(tagName) == tag && tag != "" {
+			return value.FieldByIndex(v.Index)
+		}
+	}
+	return reflect.Value{}
+}
+
+func StructCopyByTag(DstStructPtr interface{}, SrcStructPtr interface{}, tagName string) error {
+	srcv := reflect.ValueOf(SrcStructPtr)
+	dstv := reflect.ValueOf(DstStructPtr)
+	srct := reflect.TypeOf(SrcStructPtr)
+	dstt := reflect.TypeOf(DstStructPtr)
+	if srct.Kind() != reflect.Ptr || dstt.Kind() != reflect.Ptr ||
+		srct.Elem().Kind() == reflect.Ptr || dstt.Elem().Kind() == reflect.Ptr {
+		return errors.New("Fatal error:type of parameters must be Ptr of value")
+	}
+	if srcv.IsNil() || dstv.IsNil() {
+		return errors.New("Fatal error:value of parameters should not be nil")
+	}
+	srcV := srcv.Elem()
+	dstV := dstv.Elem()
+	srcfields := DeepFields(reflect.ValueOf(SrcStructPtr).Elem().Type())
+	for _, v := range srcfields {
+		if v.Anonymous {
+			continue
+		}
+		dst := FieldByTag(dstV, v.Tag.Get(tagName), tagName)
+		src := FieldByTag(srcV, v.Tag.Get(tagName), tagName)
+		if !dst.IsValid() {
+			continue
+		}
+		if src.Type() == dst.Type() && dst.CanSet() {
+			dst.Set(src)
+			continue
+		}
+		if src.Kind() == reflect.Ptr && !src.IsNil() && src.Type().Elem() == dst.Type() {
+			dst.Set(src.Elem())
+			continue
+		}
+		if dst.Kind() == reflect.Ptr && dst.Type().Elem() == src.Type() {
+			dst.Set(reflect.New(src.Type()))
+			dst.Elem().Set(src)
+			continue
+		}
+	}
+	return nil
+}

+ 245 - 0
common.in/utils/crypto.go

@@ -0,0 +1,245 @@
+package utils
+
+import (
+	"bytes"
+	"crypto"
+	//"crypto/aes"
+	"crypto/cipher"
+	"crypto/des"
+	"crypto/rand"
+	"crypto/rsa"
+	"crypto/sha1"
+	"crypto/x509"
+	"encoding/base64"
+	"encoding/pem"
+	"errors"
+	//"io"
+	"strings"
+	//"fmt"
+)
+
+//des加密
+func DesEncrypt(key, iv, plainText []byte) ([]byte, error) {
+	block, err := des.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+
+	blockSize := block.BlockSize()
+	origData := pkcs5Padding(plainText, blockSize)
+	blockMode := cipher.NewCBCEncrypter(block, iv)
+	cryted := make([]byte, len(origData))
+	blockMode.CryptBlocks(cryted, origData)
+	return cryted, nil
+}
+
+//des解密
+func DesDecrypt(key, iv, cipherText []byte) ([]byte, error) {
+	block, err := des.NewCipher(key)
+	if err != nil {
+		return nil, err
+	}
+
+	blockMode := cipher.NewCBCDecrypter(block, iv)
+	origData := make([]byte, len(cipherText))
+	blockMode.CryptBlocks(origData, cipherText)
+	origData = pkcs5UnPadding(origData)
+	return origData, nil
+}
+
+//func AesEncrypt(key []byte, plaintext string) (string, error) {
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return "", err
+//	}
+//	ciphertext := make([]byte, aes.BlockSize+len(plaintext))
+//	iv := ciphertext[:aes.BlockSize]
+//	if _, err := io.ReadFull(rand.Reader, iv); err != nil {
+//		return "", err
+//	}
+//	cipher.NewCFBEncrypter(block, iv).XORKeyStream(ciphertext[aes.BlockSize:],
+//		[]byte(plaintext))
+//
+//	return base64.StdEncoding.EncodeToString(ciphertext), nil
+//}
+//
+//func AesDecrypt(key []byte, cipherText string) (string, error) {
+//	// ciphertext, err := hex.DecodeString(cipherText)
+//	ciphertext, err := base64.StdEncoding.DecodeString(cipherText)
+//	if err != nil {
+//		return "", err
+//	}
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return "", err
+//	}
+//	if len(ciphertext) < aes.BlockSize {
+//		return "", errors.New("ciphertext too short")
+//	}
+//	iv := ciphertext[:aes.BlockSize]
+//	ciphertext = ciphertext[aes.BlockSize:]
+//	cipher.NewCFBDecrypter(block, iv).XORKeyStream(ciphertext, ciphertext)
+//	return string(ciphertext), nil
+//}
+//
+//func AesEncrypt1(origData, key []byte) ([]byte, error) {
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return nil, err
+//	}
+//	blockSize := block.BlockSize()
+//	origData = pkcs5Padding(origData, blockSize)
+//	// origData = ZeroPadding(origData, block.BlockSize())
+//	blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
+//	crypted := make([]byte, len(origData))
+//	// 根据CryptBlocks方法的说明,如下方式初始化crypted也可以
+//	// crypted := origData
+//	blockMode.CryptBlocks(crypted, origData)
+//
+//	return crypted, nil
+//}
+//
+//func AesDecrypt1(crypted, key []byte) ([]byte, error) {
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return nil, err
+//	}
+//	blockSize := block.BlockSize()
+//	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
+//	origData := make([]byte, len(crypted))
+//	// origData := crypted
+//	blockMode.CryptBlocks(origData, crypted)
+//	origData = pkcs5UnPadding(origData)
+//	// origData = ZeroUnPadding(origData)
+//	return origData, nil
+//}
+//
+//func AesEncrypt2(origData, key []byte) ([]byte, error) {
+//	txt := string(origData)
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return nil, err
+//	}
+//	encrypter := cipher.NewECBEncrypter(block)
+//	blockSize := block.BlockSize()
+//	origData = pkcs5Padding(origData, blockSize)
+//
+//	dest := make([]byte, (len(txt)/len(key)+1)*len(key))
+//	encrypter.CryptBlocks(dest, origData)
+//	return dest, nil
+//}
+//
+//func AesDecrypt2(crypted, key []byte) ([]byte, error) {
+//	block, err := aes.NewCipher(key)
+//	if err != nil {
+//		return nil, err
+//	}
+//
+//	blockMode := cipher.NewECBDecrypter(block)
+//	origData := make([]byte, len(crypted))
+//	// origData := crypted
+//	blockMode.CryptBlocks(origData, crypted)
+//	origData = pkcs5UnPadding(origData)
+//	// origData = ZeroUnPadding(origData)
+//	return origData, nil
+//}
+
+func RSASign(data string, privKey string) (string, error) {
+	pemBlock, _ := pem.Decode([]byte(privKey))
+
+	if pemBlock == nil {
+		return "", errors.New("PARSE PRIVATE KEY FAILED")
+	}
+
+	if pemBlock.Type != "RSA PRIVATE KEY" {
+		return "", errors.New("RSA PRIVATE KEY")
+	}
+
+	key, err := x509.ParsePKCS1PrivateKey(pemBlock.Bytes)
+
+	if err != nil {
+		return "", err
+	}
+
+	h := sha1.New()
+	h.Write([]byte(data))
+
+	signature, err := rsa.SignPKCS1v15(rand.Reader, key, crypto.SHA1, h.Sum(nil))
+	if err != nil {
+		return "", err
+	}
+
+	return string(Base64Encode(signature)), nil
+}
+
+func SignPKCS1v15(src, key []byte, hash crypto.Hash) ([]byte, error) {
+	var h = hash.New()
+	h.Write(src)
+	var hashed = h.Sum(nil)
+
+	var err error
+	var block *pem.Block
+	block, _ = pem.Decode(key)
+	if block == nil {
+		return nil, errors.New("private key error")
+	}
+
+	var pri *rsa.PrivateKey
+	pri, err = x509.ParsePKCS1PrivateKey(block.Bytes)
+	if err != nil {
+		return nil, err
+	}
+	return rsa.SignPKCS1v15(rand.Reader, pri, hash, hashed)
+}
+
+func SignRSA2(keys []string, param map[string]string, privateKey []byte) (s string, err error) {
+	var pList = make([]string, 0, 0)
+	for _, key := range keys {
+		var value = strings.TrimSpace(param[key])
+		if len(value) > 0 {
+			pList = append(pList, key+"="+value)
+		}
+	}
+
+	var src = strings.Join(pList, "&")
+	sig, err := SignPKCS1v15([]byte(src), privateKey, crypto.SHA256)
+	if err != nil {
+		return "", err
+	}
+	s = base64.StdEncoding.EncodeToString(sig)
+	return s, nil
+}
+
+func VerifyPKCS1v15(src, sig, key []byte, hash crypto.Hash) error {
+	var h = hash.New()
+	h.Write(src)
+	var hashed = h.Sum(nil)
+
+	var err error
+	var block *pem.Block
+	block, _ = pem.Decode(key)
+	if block == nil {
+		return errors.New("public key error")
+	}
+
+	var pubInterface interface{}
+	pubInterface, err = x509.ParsePKIXPublicKey(block.Bytes)
+	if err != nil {
+		return err
+	}
+	var pub = pubInterface.(*rsa.PublicKey)
+
+	return rsa.VerifyPKCS1v15(pub, hash, hashed, sig)
+}
+
+func pkcs5Padding(src []byte, blockSize int) []byte {
+	padding := blockSize - len(src)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(src, padtext...)
+}
+
+func pkcs5UnPadding(src []byte) []byte {
+	length := len(src)
+	unpadding := int(src[length-1])
+	return src[:(length - unpadding)]
+}

+ 80 - 0
common.in/utils/distribute_lock.go

@@ -0,0 +1,80 @@
+package utils
+
+import (
+	"context"
+	"gadm-ods/common.in/cache"
+	"fmt"
+	"time"
+)
+
+type Lock struct {
+	Key    string
+	Ttl    int64
+	cancel context.CancelFunc
+}
+
+// 监听过期
+func (lock *Lock) WatchLock(ctx context.Context) {
+	sleepTime := time.Duration(lock.Ttl * 2 / 3)
+	t1 := time.Tick(sleepTime * time.Second)
+	for {
+		select {
+		case <-t1:
+			cache.Redis.Expire(lock.Key, lock.Ttl)
+		case <-ctx.Done():
+			return
+		}
+	}
+
+}
+
+// 停止监听
+func (lock *Lock) StopWatch() {
+	lock.cancel()
+}
+
+
+// 加分布式锁
+func (lock *Lock) RedisLock() (bool,error) {
+	if lock.Key == "" || lock.Ttl <= 0 {
+		return false,fmt.Errorf("参数错误")
+	}
+
+	res, err := cache.Redis.SetNxEx(lock.Key, "lock", lock.Ttl)
+	// redis 操作失败
+	if err != nil {
+		return false,err
+	}
+
+	// 上锁成功
+	if res {
+		ctx, cancel := context.WithCancel(context.Background())
+		lock.cancel = cancel
+		go lock.WatchLock(ctx)
+		return true,nil
+	}
+
+	return false,nil
+}
+
+func (lock *Lock) TryRedisLock() error {
+	for{
+		islock ,err := lock.RedisLock()
+		if err != nil{
+			return err
+		}else{
+			if islock{
+				return nil
+			}else{
+				time.Sleep(1*time.Second)
+			}
+		}
+	}
+}
+// 释放分布式锁
+func (lock *Lock) RedisUnlock() {
+	// 停止监听
+	lock.StopWatch()
+	// 删除分布式锁
+	cache.Redis.Del(lock.Key)
+}

+ 31 - 0
common.in/utils/gc.go

@@ -0,0 +1,31 @@
+package utils
+
+import (
+	"runtime"
+	"runtime/debug"
+	"sync"
+	"time"
+)
+
+var Gmutex sync.Mutex
+
+var ReqCount int
+
+func GC() {
+	Gmutex.Lock()
+	defer Gmutex.Unlock()
+	ReqCount++
+	if ReqCount >= 100 {
+		runtime.GC()
+		ReqCount = 0
+	}
+}
+
+func Free() {
+	for {
+		if time.Now().Hour() == 1 {
+			debug.FreeOSMemory()
+		}
+		time.Sleep(1 * time.Hour)
+	}
+}

+ 72 - 0
common.in/utils/math.go

@@ -0,0 +1,72 @@
+package utils
+
+var UtimesNumberMap = map[string]int{
+	"一":  1,
+	"二":  2,
+	"三":  3,
+	"四":  4,
+	"五":  5,
+	"六":  6,
+	"七":  7,
+	"八":  8,
+	"九":  9,
+	"十":  10,
+	"两":  2,
+	"1":  1,
+	"2":  2,
+	"3":  3,
+	"4":  4,
+	"5":  5,
+	"6":  6,
+	"7":  7,
+	"8":  8,
+	"9":  9,
+	"10": 10}
+
+//返回最小值 int
+func MinIntBetween(left int, right int) int {
+	if left <= right {
+		return left
+	}
+	return right
+}
+
+//返回最小值 int64
+func MinInt64Between(left int64, right int64) int64 {
+	if left <= right {
+		return left
+	}
+	return right
+}
+
+//返回最大值 int
+func MaxIntBetween(left int, right int) int {
+	if left >= right {
+		return left
+	}
+	return right
+}
+
+//返回最大值 int64
+func MaxInt64Between(left int64, right int64) int64 {
+	if left >= right {
+		return left
+	}
+	return right
+}
+
+//返回最大值 Float64
+func MinFloat64Between(left float64, right float64) float64 {
+	if left >= right {
+		return right
+	}
+	return left
+}
+
+//返回最小值 Float64
+func MaxFloat64Between(left float64, right float64) float64 {
+	if left <= right {
+		return right
+	}
+	return left
+}

+ 101 - 0
common.in/utils/oss.go

@@ -0,0 +1,101 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package utils
+
+import (
+	"gadm-ods/common.in/config"
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+const (
+	ossId       = "LTAI4Fjs1bNMd8k8LT41Fxxn"
+	ossSecret   = "WlQJwVbxHIY4TIZwMIFA5QCye6n0Pl"
+	ossEndpoint = "oss-cn-shanghai.aliyuncs.com"
+	ossBucket   = "dip-image"
+)
+
+/*
+func isOssConfigured() bool {
+	if config.Conf.Oss.Endpoint == "" ||
+		config.Conf.Oss.Bucket == "" ||
+		config.Conf.Oss.Id == "" ||
+		config.Conf.Oss.Secret == "" {
+		return false
+	}
+	return true
+}
+
+// OssGetUrl 获取oss下载地址url前缀
+func OssGetUrl() string {
+	if isOssConfigured() == false {
+		return "https://" + ossBucket + "." + ossEndpoint
+	}
+	return "https://" + config.Conf.Oss.Bucket + "." + config.Conf.Oss.Endpoint
+}
+
+func ossGetInfo() (id, secret, bucket, endpoint string) {
+	id = ossId
+	secret = ossSecret
+	bucket = ossBucket
+	endpoint = ossEndpoint
+	if isOssConfigured() {
+		id = config.Conf.Oss.Id
+		secret = config.Conf.Oss.Secret
+		bucket = config.Conf.Oss.Bucket
+		endpoint = config.Conf.Oss.Endpoint
+	}
+	return id, secret, bucket, endpoint
+}
+
+// OssUploadFile 上传文件到oss服务器
+func OssUploadFile(path string, fileName string) error {
+	id, secret, buck, endpoint := ossGetInfo()
+	client, err := oss.New(endpoint, id, secret)
+	if err != nil {
+		return err
+	}
+
+	// 获取存储空间。
+	bucket, err := client.Bucket(buck)
+	if err != nil {
+		return err
+	}
+	err = bucket.PutObjectFromFile(fileName, path)
+	return err
+}
+
+// OssDeleteFile 在oss服务器上删除文件
+func OssDeleteFile(fileName string) error {
+	id, secret, buck, endpoint := ossGetInfo()
+	client, err := oss.New(endpoint, id, secret)
+	if err != nil {
+		return err
+	}
+
+	// 获取存储空间。
+	bucket, err := client.Bucket(buck)
+	if err != nil {
+		return err
+	}
+	err = bucket.DeleteObject(fileName)
+	return err
+}*/
+
+func OssDownloadFile(objectName, downloadedFileName string) error {
+	client, err := oss.New(config.Conf.Oss.EndPoint, config.Conf.Oss.AccessKey, config.Conf.Oss.AccessSecret)
+	if err != nil {
+		return err
+	}
+	// 获取存储空间。
+	bucket, err := client.Bucket(config.Conf.Oss.Bucket)
+	if err != nil {
+		return err
+	}
+	// 下载文件。
+	err = bucket.GetObjectToFile(objectName, downloadedFileName)
+	if err != nil {
+		return err
+	}
+	return nil
+}

+ 57 - 0
common.in/utils/redis.go

@@ -0,0 +1,57 @@
+package utils
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+
+	"gadm-ods/common.in/cache"
+)
+
+func RedisSet(tabname string, tail string, value interface{}) error {
+	bytes, err := json.Marshal(value)
+	if err != nil {
+		return err
+	}
+	key := fmt.Sprintf("%s-%s", tabname, tail)
+	_, err = cache.Redis.Set(key, string(bytes))
+	return err
+}
+
+func RedisSetEx(key string, seconds int64, value interface{}) error {
+	bytes, err := json.Marshal(value)
+	if err != nil {
+		return err
+	}
+	_, err = cache.Redis.SetEx(key, seconds, string(bytes))
+	return err
+}
+
+func RedisGet(key string, value interface{}) error {
+	ret, err := cache.Redis.Get(key)
+	if err != nil {
+		return err
+	}
+	if ret == "" {
+		return errors.New("empty")
+	}
+	err = json.Unmarshal([]byte(ret), value)
+	return err
+}
+
+func RedisDelKey(key string) error {
+	_, err := cache.Redis.Del(key)
+	return err
+}
+
+func RedisSadd(key string, members ...interface{}) (int64, error){
+	return cache.Redis.SAdd(key,members...)
+}
+
+func RedisSIsmember(key string, member interface{}) (bool, error) {
+	return cache.Redis.SIsmember(key,member)
+}
+
+func RedisRem(key string, members ...interface{}) (int64, error){
+	return cache.Redis.SRem(key,members)
+}

+ 25 - 0
common.in/utils/signal.go

@@ -0,0 +1,25 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package utils
+
+import (
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+)
+
+var ExitRecieved bool
+
+func HandleExitSignal() {
+	sigs := make(chan os.Signal, 1)
+	signal.Notify(sigs, syscall.SIGTERM)
+	go func() {
+		<-sigs
+		ExitRecieved = true
+		t := time.NewTimer(5 * time.Second)
+		<-t.C
+		os.Exit(0)
+	}()
+}

+ 310 - 0
common.in/utils/utils.go

@@ -0,0 +1,310 @@
+package utils
+
+import (
+	"crypto/md5"
+	"crypto/sha256"
+	"encoding/base64"
+	"encoding/hex"
+	"encoding/json"
+	"errors"
+	"fmt"
+	"math/rand"
+	"net"
+	"os"
+	"reflect"
+	"strconv"
+	"strings"
+	"time"
+)
+
+var (
+	DB_QUERY_WHERE_EMPTY = errors.New("where is empty")
+	DB_RECORD_NOT_EXISTS = errors.New("record is not exists")
+	DB_DUPLICATE         = errors.New("database duplicate")
+)
+
+var (
+	idCertMatrix       = []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
+	idCertVerifyCode   = []byte("10X98765432")
+	socialCreditMatrix = []int{1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28}
+	socialCreditMap    = map[int]int{'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,
+		'A': 10, 'B': 11, 'C': 12, 'D': 13, 'E': 14, 'F': 15, 'G': 16, 'H': 17, 'J': 18,
+		'K': 19, 'L': 20, 'M': 21, 'N': 22, 'P': 23, 'Q': 24, 'R': 25, 'T': 26, 'U': 27,
+		'W': 28, 'X': 29, 'Y': 30}
+
+	socialCreditMapKeys   = []int{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'U', 'W', 'X', 'Y'}
+	socialCreditMapValues = []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30}
+)
+
+const DAYSECOND = 24 * 60 * 60
+const MINUTESECOND = 60
+
+func GetUniqueLogID(cmd string) string {
+	return fmt.Sprintf("%d_%s", time.Now().Unix(), cmd)
+}
+
+// 获取偏移天数的某天的某个时刻的时间戳
+func GetTimestampInOffsetDay(offsetDay, hour, min, sec int) int64 {
+	nowTime := time.Now()
+
+	todayZeroTime := time.Date(nowTime.Year(), nowTime.Month(), nowTime.Day(), 0, 0, 0, 0, time.Local)
+	offsetDayZeroTime := todayZeroTime.AddDate(0, 0, offsetDay)
+
+	return offsetDayZeroTime.Unix() + int64(hour*3600+min*60+sec)
+}
+
+func BeToday(ts int64) bool {
+	theTime := time.Unix(ts, 0)
+	nowTime := time.Now()
+
+	// 年、月、日都相同,则是同一天
+	return (theTime.Year() == nowTime.Year() && theTime.Month() == nowTime.Month() && theTime.Day() == nowTime.Day())
+}
+
+// 获取某个时间戳的0点时间戳、24点时间戳、日期
+func Get0_24TimestampAndDate(ts int64) (ts0, ts24 int64, date string) {
+	theTime := time.Unix(ts, 0)
+
+	theZeroTime := time.Date(theTime.Year(), theTime.Month(), theTime.Day(), 0, 0, 0, 0, time.Local)
+
+	ts0 = theZeroTime.Unix()                   // 0点时间戳
+	ts24 = theZeroTime.AddDate(0, 0, 1).Unix() // 24点时间戳
+	date = theZeroTime.Format("2006-01-02")    // 日期
+
+	return
+}
+
+// 获取args对应的json字符串
+func MarshalJsonString(args interface{}) (result string) {
+		if r, err := json.Marshal(args); err == nil {
+			result = string(r)
+		}
+	return
+}
+
+func GetStructNotZeroField(arg interface{}) []string {
+	v := reflect.ValueOf(arg)
+	if v.Kind() == reflect.Ptr {
+		v = v.Elem()
+	}
+	result := make([]string, 0)
+	if v.Kind() == reflect.Struct {
+		numField := v.NumField()
+		for i := 0; i < numField; i++ {
+			if !reflect.DeepEqual(v.Field(i).Interface(), reflect.Zero(v.Field(i).Type()).Interface()) {
+				result = append(result, v.Type().Field(i).Name)
+			}
+		}
+	}
+	return result
+}
+
+// 生成订单号
+func GenOrderNO(orderType int) string {
+	nowTime := time.Now()
+	nowDate := nowTime.Format("20060102")
+	ym := string([]byte(nowDate)[3:6])
+	d := string([]byte(nowDate)[6:])
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+
+	return fmt.Sprintf("%d%s%d%s%05d", orderType, ym, r.Intn(10), d, r.Intn(10000))
+}
+
+func GetRuneSubstring(src string, start, length int) string {
+	rs := []rune(src)
+	rl := len(rs)
+	end := 0
+
+	if start < 0 {
+		start = rl - 1 + start
+	}
+	end = start + length
+
+	if start > end {
+		start, end = end, start
+	}
+
+	if start < 0 {
+		start = 0
+	}
+	if start > rl {
+		start = rl
+	}
+	if end < 0 {
+		end = 0
+	}
+	if end > rl {
+		end = rl
+	}
+	return string(rs[start:end])
+}
+
+func MD5(text string) string {
+	h := md5.New()
+	h.Write([]byte(text))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func SHA256(text string) string {
+	h := sha256.New()
+	h.Write([]byte(text))
+	return hex.EncodeToString(h.Sum(nil))
+}
+
+func GetEnv(name string) string {
+	value := os.Getenv(name)
+	if value == "" {
+		fmt.Printf(`hasn't %s env var\n`, name)
+		os.Exit(1)
+	}
+	return value
+}
+
+func Base64URLDecode(data string) ([]byte, error) {
+	var missing = (4 - len(data)%4) % 4
+	data += strings.Repeat("=", missing)
+	return base64.URLEncoding.DecodeString(data)
+}
+
+func Base64UrlSafeEncode(source []byte) string {
+	// Base64 Url Safe is the same as Base64 but does not contain '/' and '+' (replaced by '_' and '-') and trailing '=' are removed.
+	bytearr := base64.StdEncoding.EncodeToString(source)
+	safeurl := strings.Replace(string(bytearr), "/", "_", -1)
+	safeurl = strings.Replace(safeurl, "+", "-", -1)
+	safeurl = strings.Replace(safeurl, "=", "", -1)
+	return safeurl
+}
+
+//生成reportId 唯一标识
+func GenReportID(module string) string {
+	macAddr := ""
+	for _, addr := range macAddrs() {
+		macAddr += addr
+	}
+
+	if macAddr == "" {
+		macAddr = RandomString(10)
+	}
+
+	reportId := macAddr + module + strconv.FormatInt(time.Now().UnixNano(), 10)
+	return MD5(reportId)
+}
+
+func macAddrs() (macAddrs []string) {
+	netInterfaces, err := net.Interfaces()
+	if err != nil {
+		return macAddrs
+	}
+
+	for _, netInterface := range netInterfaces {
+		macAddr := netInterface.HardwareAddr.String()
+		if len(macAddr) == 0 {
+			continue
+		}
+
+		macAddrs = append(macAddrs, macAddr)
+	}
+	return macAddrs
+}
+
+//根据length获取随机字符串
+func RandomString(length int) string {
+	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	bytes := []byte(str)
+	result := []byte{}
+	r := rand.New(rand.NewSource(time.Now().UnixNano()))
+	for i := 0; i < length; i++ {
+		result = append(result, bytes[r.Intn(len(bytes))])
+	}
+	return string(result)
+}
+
+//身份证
+func CheckIDCert(str string) (res bool) {
+
+	defer func() {
+		if r := recover(); r != nil {
+			res = false
+		}
+	}()
+
+	idCert := strings.ToUpper(str)
+	if len(idCert) != 18 {
+		return false
+	}
+
+	year, err := strconv.Atoi(idCert[6:10])
+
+	if err != nil {
+		return false
+	}
+
+	if !(1900 < year && year < 2100) {
+		return false
+	}
+
+	check := 0
+	lastLetter := 0
+	for index, value := range []byte(idCert) {
+		if index == 17 {
+			lastLetter = int(value)
+			break
+		}
+
+		if !(value >= 48 && value <= 57) {
+			return false
+		}
+
+		v := value - 48
+		check = check + idCertMatrix[index]*int(v)
+	}
+
+	if !((lastLetter >= 48 && lastLetter <= 57) || lastLetter == 'X') {
+		return false
+	}
+
+	timeLayout := "20060102"                                     //转化所需模板
+	loc, _ := time.LoadLocation("Local")                         //重要:获取时区
+	_, err = time.ParseInLocation(timeLayout, idCert[6:14], loc) //使用模板在对应时区转化为time.time类型
+
+	if err != nil {
+		return false
+	}
+
+	verifyCode := int(idCertVerifyCode[check%11])
+	if lastLetter == verifyCode {
+		return true
+	}
+	return false
+}
+
+func IsReuseTime(reuseTime int64, timestamp int64) bool {
+	if timestamp == 0 {
+		return false
+	}
+	reuseSencod := reuseTime * DAYSECOND
+
+	t := time.Unix(timestamp, 0)
+	now := time.Now()
+
+	// 60s短时复用
+	if reuseTime == 0 {
+		if now.Unix()-timestamp > MINUTESECOND {
+			return false
+		}
+		return true
+	}
+
+	start := time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location()).Unix()
+	end := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).Unix()
+	if end-start >= reuseSencod {
+		return false
+	}
+	return true
+}
+
+// 时间字符串转化成时间戳
+func TimeStringToTs(s string) int64 {
+	location, _ := time.ParseInLocation("2006-01-02", s, time.Local)
+	return location.Unix()
+}

+ 418 - 0
common.in/utils/vehicle.go

@@ -0,0 +1,418 @@
+package utils
+
+import (
+	"regexp"
+	"strconv"
+	"strings"
+	"unicode"
+	"unicode/utf8"
+
+	"gadm-ods/common.in/jsonrpc2"
+)
+
+const (
+	VINLEN      = 17
+	PLATELEN    = 6
+	PLATENEWLEN = 7
+	SF          = "京津冀晋蒙辽吉黑沪苏浙皖闽赣鲁豫鄂湘粤桂琼渝川贵云藏陕甘青宁新"
+)
+
+var RUNMODE = "prod"
+
+func SetRunmode(runMode string) {
+	RUNMODE = runMode
+}
+
+func ParsePlate(plate string) (string, string) {
+	var index int
+	for i, r := range plate {
+		if !unicode.Is(unicode.Scripts["Han"], r) {
+			index = i
+			break
+		}
+	}
+	sf := plate[0:index]
+	hphm := plate[index:]
+
+	return sf, hphm
+}
+
+func CheckVinFormat(vin string) (string, error) {
+	vin = strings.TrimSpace(vin)
+	vin = strings.ToUpper(vin)
+
+	strconv.Itoa(len(vin))
+	r, _ := regexp.Compile(`[0-9A-Z]{` + strconv.Itoa(len(vin)) + `}`)
+	if r.MatchString(vin) {
+		return vin, nil
+	}
+
+	return vin, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车架号包含特殊字符")
+
+}
+
+func CheckVinFormat17Bit(vin string) (string, error) {
+	vin = strings.TrimSpace(vin)
+	vin = strings.ToUpper(vin)
+	if len(vin) != VINLEN {
+		return vin, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车架号不为17位")
+	}
+
+	r, _ := regexp.Compile(`[0-9A-Z]{17}`)
+	if r.MatchString(vin) {
+		return vin, nil
+	}
+
+	return vin, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车架号包含特殊字符")
+
+}
+
+func IsCommonPlateType(plateType string) bool {
+	switch plateType {
+	case "51":
+		return true
+	case "52":
+		return true
+	case "01":
+		return true
+	case "02":
+		return true
+	default:
+		return false
+	}
+}
+
+func isUnknowPlateType(plateType string) bool {
+	switch plateType {
+	case "02":
+		return false
+	case "03":
+		return false
+	case "04":
+		return false
+	case "15":
+		return false
+	case "16":
+		return false
+	case "23":
+		return false
+	case "24":
+		return false
+	case "26":
+		return false
+	case "27":
+		return false
+	default:
+		return true
+	}
+}
+
+func getPlateType(plateNo string) string {
+	switch {
+	case strings.HasPrefix(plateNo, "使"):
+		return "03"
+	case strings.HasSuffix(plateNo, "使"):
+		return "03"
+		// 领馆汽车
+	case strings.HasSuffix(plateNo, "领"):
+		return "04"
+	case strings.HasSuffix(plateNo, "挂"):
+		return "15"
+	case strings.HasSuffix(plateNo, "学"):
+		return "16"
+	case strings.HasSuffix(plateNo, "警"):
+		return "23"
+	case strings.HasSuffix(plateNo, "港"):
+		return "26"
+	case strings.HasSuffix(plateNo, "澳"):
+		return "27"
+	default:
+		return "02"
+	}
+}
+
+func isSupportPlateType(plateType, plateNumber string) bool {
+	/*switch plateType {
+	case "01":
+		return true
+	case "02":
+		return true
+	case "03":
+		return true
+	case "04":
+		return true
+	case "05":
+		return true
+	case "06":
+		return true
+	case "07":
+		return true
+	case "08":
+		return true
+	case "09":
+		return true
+	case "10":
+		return true
+	case "11":
+		return true
+	case "12":
+		return true
+	case "13":
+		return true
+	case "14":
+		return true
+	case "15":
+		return true
+	case "16":
+		return true
+	case "17":
+		return true
+	case "20":
+		return true
+	case "21":
+		return true
+	case "22":
+		return true
+	case "23":
+		return true
+	case "24":
+		return true
+	case "25":
+		return true
+	case "26":
+		return true
+	case "27":
+		return true
+	case "51":
+		return true
+	case "52":
+		return true
+	case "99":
+		return true
+	default:
+		return false
+	}*/
+
+	if plateType != "" {
+		if plateType != "02" && plateType != "52" {
+			return false
+		}
+	} else {
+		for _, r := range plateNumber {
+			if unicode.Is(unicode.Scripts["Han"], r) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+func CheckPlateNoFormat(plateNo *string, plateType string, merchantId int64) (string, error) {
+	*plateNo = strings.TrimSpace(*plateNo)
+	*plateNo = strings.ToUpper(*plateNo)
+
+	return plateType, nil
+
+	if RUNMODE != "prod" {
+		if strings.HasPrefix(*plateNo, "测") {
+			return plateType, nil
+		}
+	}
+
+	sf, plateNumber := ParsePlate(*plateNo)
+	plateLen := utf8.RuneCountInString(plateNumber)
+
+	if strings.HasSuffix(*plateNo, "使") {
+		if plateType == "" {
+			plateType = "03"
+		}
+	}
+
+	if plateType == "" && plateLen == PLATENEWLEN {
+		if plateNumber[1] > 64 && plateNumber[1] < 91 {
+			plateType = "52"
+		} else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 {
+			plateType = "51"
+		} else {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源汽车车牌格式不正确")
+		}
+	}
+
+	if !isSupportPlateType(plateType, plateNumber) && merchantId != 15 { //15号DIP商户
+		return "", jsonrpc2.NewJsonError(20004, "参数错误,暂不支持非小型车号牌调用")
+	}
+
+	// 判断车牌号码合法性
+	if len(sf) == 0 && !strings.HasSuffix(*plateNo, "使") {
+		return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌号码格式不正确")
+	} else if sf != "使" {
+		if !strings.Contains(SF, sf) {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,不支持的省份")
+		}
+	}
+
+	// 检查车牌是否包含i o字符
+	if strings.Contains(plateNumber, "I") || strings.Contains(plateNumber, "O") {
+		return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌号码包含I或O字符")
+	}
+
+	count := 0
+	for _, v := range plateNumber {
+		if 64 < v && v < 91 {
+			count++
+		}
+	}
+
+	if strings.HasPrefix(*plateNo, "使") || strings.HasSuffix(*plateNo, "使") {
+		if plateType == "" {
+			return "03", nil
+		} else {
+			if plateType != "03" {
+				return "", jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌和车牌类型不匹配")
+			}
+			return plateType, nil
+		}
+	} else if strings.HasSuffix(*plateNo, "领") {
+		if plateType == "" {
+			return "04", nil
+		} else {
+			if plateType != "04" {
+				return "", jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌和车牌类型不匹配")
+			}
+			return plateType, nil
+		}
+	} else if plateType == "51" || plateType == "52" {
+		if count > 3 {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源车牌号码超过3个字母")
+		} else if plateLen != PLATENEWLEN {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源车牌号码长度不正确")
+		} else {
+			if plateNumber[1] > 64 && plateNumber[1] < 91 {
+				if plateType != "52" {
+					return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配")
+				}
+			} else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 {
+				if plateType != "51" {
+					return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源车牌号码和车牌类型不匹配")
+				}
+			} else {
+				return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,新能源汽车车牌格式不正确")
+			}
+		}
+
+	} else {
+		if count > 3 {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌号码超过3个字母")
+		} else if plateLen != PLATELEN {
+			return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌号码长度不正确")
+		}
+	}
+
+	if strings.Contains("0123456789", plateNumber[0:1]) {
+		return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,号牌号码第一位不能为数字")
+	}
+
+	if plateType == "51" || plateType == "52" {
+		return plateType, nil
+	}
+
+	if plateType != "" && isUnknowPlateType(plateType) {
+		return plateType, nil
+	}
+
+	plateTypeNew := getPlateType(*plateNo)
+
+	if plateType != "" && plateTypeNew != plateType {
+		return plateType, jsonrpc2.NewJsonError(20003, "请求参数格式不对,车牌号码和车牌类型不匹配")
+	}
+	return plateTypeNew, nil
+}
+
+func VehicleDefaultPlateType(plateNo *string) (plateType string, err error) {
+	*plateNo = strings.TrimSpace(*plateNo)
+	*plateNo = strings.ToUpper(*plateNo)
+	sf, plateNumber := ParsePlate(*plateNo)
+
+	if len(sf) == 0 {
+		return "", jsonrpc2.NewJsonError(20003, "参数格式不对,车牌号码无省份")
+	}
+	plateLen := utf8.RuneCountInString(plateNumber)
+	if plateLen == PLATENEWLEN {
+		if plateNumber[1] > 64 && plateNumber[1] < 91 {
+			plateType = "52"
+			return plateType, nil
+		} else if plateNumber[PLATENEWLEN-1] > 64 && plateNumber[PLATENEWLEN-1] < 91 {
+			plateType = "51"
+			return plateType, nil
+		} else {
+			return plateType, jsonrpc2.NewJsonError(20003, "参数格式不对,新能源汽车车牌格式不正确")
+		}
+	} else if plateLen == PLATELEN {
+		plateType = getPlateType(*plateNo)
+		return plateType, nil
+	} else {
+		return plateType, jsonrpc2.NewJsonError(20003, "参数格式不对,车牌长度不正确")
+	}
+
+	return plateType, nil
+}
+
+func CheckEngineFormat(engineNo string) error {
+	if len(engineNo) < 6 {
+		return jsonrpc2.NewJsonError(20003, "参数格式不对,发动机号小于6位")
+	}
+	for _, r := range engineNo {
+		if unicode.Is(unicode.Scripts["Han"], r) {
+			return jsonrpc2.NewJsonError(20003, "参数格式不对,发动机号包括汉字")
+		}
+	}
+	return nil
+}
+
+// 获取车辆是车型,0 烧油,1 电动
+func GetDefaultPlateType(plateno string) int {
+	_, plateNumber := ParsePlate(plateno)
+	plateLen := len(strings.TrimSpace(plateNumber))
+	if plateLen == PLATELEN {
+		return 0
+	} else if plateLen == PLATENEWLEN {
+		return 1
+	}
+	return 2
+}
+
+func AddSySource(sySoucre, sourceName string) string {
+	if sySoucre == "" {
+		sySoucre = sourceName
+	} else if !strings.Contains(sySoucre, sourceName) {
+		sySoucre = sySoucre + "," + sourceName
+	}
+	return sySoucre
+}
+
+func ParseEngineNo(engineNo, engineType string) string {
+	if strings.HasPrefix(engineNo, engineType) {
+		engineNo = strings.TrimSpace(engineNo[len(engineType):])
+	}
+
+	var re string
+	for _, v := range engineNo {
+		if (v >= '0' && v <= '9') || (v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z') || v == '/' {
+			re = re + string(v)
+		}
+	}
+	return re
+}
+
+func GetSeat(seatStr string) int {
+	seatList := strings.Split(seatStr, "-")
+	maxSeat := 0
+	for _, v := range seatList {
+		seat, _ := strconv.Atoi(v)
+		if seat > maxSeat {
+			maxSeat = seat
+		}
+	}
+
+	return maxSeat
+}

+ 2 - 0
conf/app.conf

@@ -0,0 +1,2 @@
+etcd_addrs="47.103.201.156:2379"
+discovery_type=etcd

+ 2 - 0
conf/app.conf.in

@@ -0,0 +1,2 @@
+etcd_addrs="%ETCD_ADDRS%"
+discovery_type=%DISCOVERY_TYPE%

+ 17 - 0
conf/app.service.in

@@ -0,0 +1,17 @@
+[Unit]
+Description=Start dip_vehicle Server
+Requires=network-online.target
+# We disable the wants service, because it spams the log files
+
+[Service]
+ExecStart=/etc/init.d/%APP_NAME% start
+ExecStop=/etc/init.d/%APP_NAME% stop
+Type=forking
+# We disable PIDFile= because it doesn't work with multi-mode configurations
+#PIDFile=/var/run/camera.pid
+
+TimeoutStopSec=300
+
+[Install]
+WantedBy=multi-user.target
+

+ 131 - 0
conf/common.json

@@ -0,0 +1,131 @@
+{
+  "run_mode":"dev",
+  "oss":{
+    "access_key": "LTAI5tMXuRXxUbNuD69ryfUh",
+    "access_secret": "Pujpylo7AJ57pUCJR0NJR6HROLhVYb",
+    "bucket": "dip-dev-test",
+    "end_point": "oss-cn-shanghai.aliyuncs.com"
+  },
+  "log":{
+    "stacktrace":"true",
+    "max_age":"30",
+    "max_backups":"7",
+    "max_size":"100",
+    "level":"info",
+    "path":"./"
+  },
+  "ods_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"ods-ex",
+    "queue_name":"ods-queue",
+    "route_bind_key":"ods",
+    "consumer_count":"3"
+  },
+  "dws_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"dws-ex",
+    "queue_name":"dws-queue",
+    "route_bind_key":"dws",
+    "consumer_count":"5"
+  },
+  "ads_rabbitmq":{
+    "addr":"47.100.68.39:5672",
+    "username":"admin",
+    "passwrod":"syznno1",
+    "vhost":"",
+    "exchange_name":"ads-ex",
+    "queue_name":"ads-queue",
+    "route_bind_key":"ads",
+    "consumer_count":"5"
+  },
+  "mysql":{
+    "addr":"47.103.201.156:3306",
+    "charset":"utf8",
+    "db":"default",
+    "max_conn":"100",
+    "max_idle":"10",
+    "password":"1353406",
+    "user":"root"
+  },
+  "redis":{
+    "addr":"47.103.130.208:63779",
+    "addrs":"47.103.130.208:63779",
+    "connect_timeout":"2",
+    "default_db":"0",
+    "idle_timeout":"600",
+    "is_cluster":"false",
+    "max_active":"20000",
+    "max_idle":"50",
+    "max_retries":"1",
+    "min_idle_conns":"10",
+    "network":"tcp",
+    "password":"dip_prv",
+    "pool_size":"100",
+    "read_timeout":"2",
+    "wait_flag":"true",
+    "write_timeout":"2"
+  },
+  "rpc":{
+    "prefix": "adm",
+    "keepalive":{
+      "client_time":"30",
+      "client_timeout":"30",
+      "server_time":"30",
+      "server_timeout":"30",
+      "server_mini_time":"30"},
+    "adm_dws":{
+      "log_mode":"info",
+      "mysql_db":"dip-dws",
+      "name":"adm_dws",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-access-log-svc",
+      "service_port":"60005"
+    },
+    "adm_ods":{
+      "log":{
+        "disable_stacktrace":"true",
+        "max_age":"30",
+        "max_backups":"7",
+        "max_size":"100"
+      },
+      "log_mode":"info",
+      "mysql_db":"db_adm_ods",
+      "name":"dip_admin",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-admin-svc",
+      "service_port":"60003"
+    },
+    "adm_ads":{
+      "log":{
+        "disable_stacktrace":"true",
+        "max_age":"30",
+        "max_backups":"7",
+        "max_size":"100"
+      },
+      "log_mode":"info",
+      "mysql_db":"dip-ads",
+      "name":"dip_auth_check",
+      "redis_db":"4",
+      "scheme":"tcp",
+      "update_interval":"60",
+      "service_name":"dip-auth-check-svc",
+      "service_port":"60006"
+    },
+    "adm_task":{
+      "service_name": "adm-task",
+      "service_ip": "127.0.0.1",
+      "service_port": "10004"
+    }
+  }
+
+}

+ 90 - 0
confbuild.sh

@@ -0,0 +1,90 @@
+#!/bin/bash
+
+set -e
+
+show_usage="args: [--runmode=,--serve-addr=,--serve-port=,--etcd-addr=,--etcd-port=,--encrypt-key=,--log-dir=]"
+
+#-o或--options选项后面是可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,
+#其中-a选项不接参数,-b选项后必须接参数,-c选项的参数为可选的
+#-l或--long选项后面是可接受的长选项,用逗号分开,冒号的意义同短选项。
+#-n选项后接选项解析错误时提示的脚本名字
+ARGS=`getopt -o -h --long help,runmode:,serve-addr:,serve-port:,etcd-addr:,etcd-port:,encrypt-key:,log-dir:,project-name: -n "$0" -- "$@"`
+
+if [ $? != 0 ]; then
+    exit 1
+fi
+
+#将规范化后的命令行参数分配至位置参数($1,$2,...)
+eval set -- "${ARGS}"
+
+while [ -n "$1" ]
+do
+    case "$1" in
+        -h|--help)
+            echo "$show_usage"; exit 1 ;;
+        --runmode) 
+            RUNMODE=$2; shift ;;
+        --serve-addr)
+            SERVE_ADDR=$2; shift ;;
+        --serve-port)
+            SERVE_PORT=$2; shift ;;
+        --etcd-addr)
+            ETCD_ADDR=$2; shift ;;
+        --etcd-port)
+            ETCD_PORT=$2; shift ;;
+        --encrypt-key)
+            ENCRYPT_KEY=$2; shift ;;
+        --log-dir)
+            LOG_DIR=$2; shift ;;
+        --project-name)
+            PROJECT_NAME=$2; shift ;;
+        *) shift ;;
+    esac
+done
+
+if [ -z $SERVE_PORT ];then
+    echo "The serve-port must be specified."
+    exit 1
+fi
+if [ -z $PROJECT_NAME ];then
+    echo "The project-name must be specified."
+    exit 1
+fi
+if [ -z $RUNMODE ];then
+    RUNMODE="dev"
+fi
+if [ -z $SERVE_ADDR ];then
+    SERVE_ADDR="0.0.0.0"
+fi
+if [ -z $ETCD_ADDR ];then
+    ETCD_ADDR="127.0.0.1"
+fi
+if [ -z $ETCD_PORT ];then
+    ETCD_PORT="2379"
+fi
+if [ -z $ENCRYPT_KEY ];then
+    ENCRYPT_KEY="a95cbb574bdc905f9bf457820f1fa602"
+fi
+if [ -z $LOG_DIR ];then
+    LOG_DIR="logs"
+fi
+
+PROJECT_PATH=$(cd `dirname $0`; pwd)
+SERVICE_NAME="${PROJECT_PATH##*/}"
+APP_NAME="${PROJECT_PATH##*/}"
+APP_SERVICE=conf/app.service
+APP_CONF=conf/app.conf
+
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${APP_CONF}.in > ${APP_CONF}
+sed -i "s/%APP_NAME%/$APP_NAME/g" ${APP_CONF}
+sed -i "s/%RUNMODE%/$RUNMODE/g" ${APP_CONF}
+sed -i "s/%SERVE_ADDR%/$SERVE_ADDR/g" ${APP_CONF}
+sed -i "s/%SERVE_PORT%/$SERVE_PORT/g" ${APP_CONF}
+sed -i "s/%ETCD_ADDR%/$ETCD_ADDR/g" ${APP_CONF}
+sed -i "s/%ETCD_PORT%/$ETCD_PORT/g" ${APP_CONF}
+sed -i "s/%ENCRYPT_KEY%/$ENCRYPT_KEY/g" ${APP_CONF}
+sed -i "s/%LOG_DIR%/$LOG_DIR/g" ${APP_CONF}
+sed -i "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${APP_CONF}
+sed -i "s/%PROJECT_NAME%/$PROJECT_NAME/g" ${APP_CONF}
+
+cat ${APP_CONF}

+ 163 - 0
consts/common.go

@@ -0,0 +1,163 @@
+// Copyright 2019 getensh.com. All rights reserved.
+// Use of this source code is governed by getensh.com.
+
+package consts
+
+// 定义数据来源大类
+const (
+	ODSPROVIDERLOG     = "provider-log"   // 数据源日志
+	ODSDIPLOG          = "dip-log"        // dip日志
+	ODSMANUALAMENDMENT = "manual"         // 人工修正
+	ODSOFFLINEIMPORT   = "offline-import" // 离线导入
+)
+
+// 定义过期或不过期数据
+const (
+	NORMALDATA = "0"
+	EXPIREDATA = "1"
+)
+
+// 数据源编码
+const (
+	VINRELATION                  = "102-002" //VIN解析车型
+	VEHICLEINFO                  = "103-001" //全国车辆信息查询
+	VEHICLEINFOZZX               = "122-001" //全国车辆信息查询-中智信
+	VEHICLEOWNER                 = "103-002" //车辆所有人验证
+	CXYVIOLATION                 = "117-001" //车行易违章
+	VIOLATIONSC                  = "109-001" //四川违章
+	VEHICLEZT                    = "114-001" // 直通车辆信息
+	VIOLATIONCHINA               = "101-001" //全国违章
+	C300VALUATION                = "102-001" // 车300估值
+	MJLIMIT                      = "105-001" //限行数据
+	MJAQI                        = "105-002" //空气质量指数
+	MJFORECAST24HOURS            = "105-003" //天气预报24小时
+	MJAQI5DAYS                   = "105-004" //AQI预报5天
+	MJFORECAST15DAYS             = "105-005" //天气预报15天
+	MJCONDITION                  = "105-006" //天气实况
+	MJLIFTINDEX                  = "105-007" //生活指数
+	MJALERT                      = "105-008" //天气预警
+	JHLICENSESCORE               = "106-001" //驾驶员累计计分查询
+	ZXDRIVERLICENSEQUERY         = "107-003" //驾驶证核查
+	EDCREATEORDER                = "104-002" //e代审创建订单
+	EDGETTOKEN                   = "104-001" //e代审获取token
+	EDCANCELORDER                = "104-003" //e代审取消订单
+	EDPAYORDER                   = "104-004" //e代审通知订单已支付
+	NJGETCITY                    = "108-001" //鑫首创获取城市列表
+	NJCREATEORDER                = "108-002" //鑫首创创建自行预约订单
+	NJCANCELORDER                = "108-003" //鑫首创取消订单
+	NJGETSTATION                 = "108-004" //鑫首创获取年审机构
+	NJGETSTATIONITEM             = "108-005" //鑫首创获取年审项目
+	NJGETVEHICLETYPE             = "108-006" //鑫首创获取机构支持的车辆类型
+	NJGETRESERVATIONOFFDATE      = "108-007" //鑫首创获取机构不可预约日期
+	NJGETRESERVATIONTIME         = "108-008" //获取可预约时段
+	NJGETORDERSTATE              = "108-009" //鑫首创获取自行预约订单状态
+	CHECHEGETITEM                = "110-001" //车车网获取年审报价
+	CHECHECREATEORDER            = "110-002" //车车网创建免检年审在线处理订单
+	CHECHECHANGEORDERSTATUS      = "110-003" //车车修改订单状态(只支持“确认要办”)
+	ZQYPLATEVEHICLE              = "119-001"
+	ZQYVINVEHICLE                = "119-002"
+	ZQYPLATEVEHICLENEW           = "119-003"
+	MSNO272OWNERBUYORDER         = "115-005" //所有人验证-购买报告
+	MSNO272OWNERQUERYORDER       = "115-006" //所有人验证-发起查询
+	MSNO272OWNERGETRESULT        = "115-007" //所有人验证-获取查询结果
+	MSNO272OWNERQREFRESHORDER    = "115-008" //所有人验证-读取报告状态
+	MS315OWNERVERIFY             = "115-023" // MS 315所有人验证
+	MSNO272VECHICLEBUYORDER      = "115-009" //所有人验证获取车档-购买报告
+	MSNO272VECHICLEQUERYORDER    = "115-010" //所有人验证获取车档-发起查询
+	MSNO272VECHICLEGETRESULT     = "115-011" //所有人验证获取车档-获取查询结果
+	MSNO272VECHICLEQREFRESHORDER = "115-012" //所有人验证获取车档-读取报告状态
+	MSNO274BUYORDER              = "115-013" //VIN发动机查车牌-购买报告
+	MSNO274QUERYORDER            = "115-014" //VIN发动机查车牌-发起查询
+	MSNO274GETRESULT             = "115-015" //VIN发动机查车牌-获取查询结果
+	MSNO274QREFRESHORDER         = "115-016" //VIN发动机查车牌-读取报告状态
+	MSNO273BUYORDER              = "115-017" //三要素验证-购买报告
+	MSNO273QUERYORDER            = "115-018" //三要素验证-发起查询
+	MSNO273GETRESULT             = "115-019" //三要素验证-获取查询结果
+	MSNO273QREFRESHORDER         = "115-020" //三要素验证-读取报告状态
+	ZHVIN                        = "120-001" //智慧数据
+	HNVIN6                       = "120-002" //智慧(haoniu)数据
+	CQZJTVEHICLEOWNER            = "123-001" //重庆中交投所有人验证
+	CQZJTVEHICLE                 = "123-002" //重庆中交投全国车档
+	DJVEHICLE                    = "124-001" //DJ车档
+	DJSCVEHICLE                  = "124-002" //DJ四川车档
+	DJVEHICLEOWNER               = "124-003" //DJ所有人验证
+	DJSCVEHICLEOWNER             = "124-004" //DJ四川所有人验证
+	LOCALDATA                    = "125-001" //本地数据
+	LOCALVIOLATION               = "125-002" //本地违章
+	LOCALVIN11                   = "125-003" //本地vin11数据
+	LOCALSCW                     = "125-004" //本地scw数据
+	JISUPLATEVERIFY              = "127-002" //极速车牌号验证
+	BIHUGETINSURANCE             = "116-001" //壁虎获取上年投保信息
+	SPYDEFINEVIN                 = "128-001" // SPY VIN码获取车型信息
+	SPYVINNEW                    = "128-011"
+	SPYVIN6                      = "128-003"
+	SPYTWODATE                   = "128-004"
+	SPYINSUREDATE                = "128-009"
+	BJCVIN                       = "129-001" //BJC vin
+	DJVIN                        = "124-007" //DJ vin
+	LOCALVIN                     = "125-005" //本地VIN相关数据
+	LOCALRESPONSE                = "125-006" //本地响应参数检查
+	ZQYINSUREDATE                = "119-005"
+	ZQYVINVEHICLETRANSFER        = "119-006"
+	ZQYVIN6VEHICLE               = "119-007"
+	SPYTRANSFERRECORD            = "128-010"
+	ZHDVEHICLEOWNER              = "137-001"
+	ZHDVEHICLEOWNERVERIFY        = "137-002"
+	ZHDVEHICLEFIVE               = "137-003"
+	ENGINENOCHECKE               = "125-007" //发动机号检测
+	PLATETYPEPOLL                = "125-008" //发动机号检测
+	LOCALBJDATA                  = "125-009" //本地bj数据
+	LOCALVEHICLEMODEL            = "125-010"
+	DRVVEHICLEOWNERVERIFY        = "138-002"
+	SPYTWODATE2                  = "128-006"
+	SPYTRANSFERBYVIN             = "128-007"
+	SPYTWOELEMENTVERIFY          = "128-008"
+	ZCRKTWOELEMENTVERIFY         = "141-001"
+	ZCRKTWOELEMENTVERIFY2        = "141-002"
+	ONLINECARACTIVE              = "138-003"
+	DRVVEHICLEOWNERVERIFYBAK     = "138-004"
+	ZJCVEHICLEOWNER              = "142-001"
+	DYIDCERTVERIFY               = "139-001"
+	ZCRKENGINENO                 = "141-003"
+	ZCRKBYVIN                = "141-004"
+)
+
+// 定义数据来源编号
+const (
+	SOURCEODS1  = "ODS001" // 车行易违章
+	SOURCEODS2  = "ODS002"
+	SOURCEODS3  = "ODS003"
+	SOURCEODS4  = "ODS004"
+	SOURCEODS5  = "ODS005"
+	SOURCEODS6  = "ODS006"
+	SOURCEODS7  = "ODS007"
+	SOURCEODS8  = "ODS008"
+	SOURCEODS9  = "ODS009"
+	SOURCEODS10 = "ODS010"
+	SOURCEODS11 = "ODS011"
+	SOURCEODS12 = "ODS012"
+	SOURCEODS13 = "ODS013"
+	SOURCEODS15 = "ODS015"
+	SOURCEODS16 = "ODS016"
+	SOURCEODS17 = "ODS017"
+	SOURCEODS18 = "ODS018"
+	SOURCEODS19 = "ODS019"
+	SOURCEDWS1  = "DWS001"
+	SOURCEDWS2  = "DWS001"
+	SOURCEADS1  = "ADS001"
+	SOURCEADS2  = "ADS002"
+)
+
+const (
+	ONLINE  = "online"
+	OFFLINE = "offline"
+)
+
+const (
+	DEFAULTPAGESIZE = 1000
+)
+
+const (
+	FromDb    = 1
+	FromExcel = 2
+)

+ 14 - 0
docker/compose/docker-compose.yaml.in

@@ -0,0 +1,14 @@
+version: '3'
+services:
+  %SERVICE_NAME%:
+    image: %SERVICE_NAME%
+    container_name: %SERVICE_NAME%
+    volumes:
+        - /huaweidata/conf/:/dip/conf/
+    networks:
+      dip_network:
+        aliases:
+        - %SERVICE_NAME%
+networks:
+  dip_network:
+    driver: bridge

+ 77 - 0
docker/kubernetes/dip-vehicle.yaml.in

@@ -0,0 +1,77 @@
+kind: Deployment
+apiVersion: extensions/v1beta1
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %RUNMODE%
+  creationTimestamp: null
+  labels:
+    app: %SERVICE_NAME%
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: %SERVICE_NAME%
+  template:
+    metadata:
+      creationTimestamp: null
+      labels:
+        app: %SERVICE_NAME%
+    spec:
+      volumes:
+      - name: app-logs
+        emptyDir: {}
+      - name: app-config
+        configMap:
+          name: %SERVICE_NAME%
+          defaultMode: 420
+      - name: common-config
+        configMap:
+          name: dip-common
+          defaultMode: 420
+      containers:
+      - name: %SERVICE_NAME%
+        image: %SERVICE_NAME%
+        resources:
+          limits:
+            cpu: 500m
+            memory: 512Mi
+          requests:
+            cpu: 250m
+            memory: 512Mi
+        volumeMounts:
+        - name: app-logs
+          mountPath: /dip/logs/%SERVICE_NAME%
+        - name: app-config
+          mountPath: /dip/conf/app.conf
+          subPath: app.conf
+        - name: common-config
+          mountPath: /dip/conf/common.json
+          subPath: common.json
+        imagePullPolicy: IfNotPresent
+        securityContext:
+          privileged: false
+  strategy:
+    type: RollingUpdate
+    rollingUpdate:
+      maxUnavailable: 1
+      maxSurge: 20%
+status: {}
+
+
+---
+
+apiVersion: v1
+kind: Service
+metadata:
+  name: %SERVICE_NAME%
+  namespace: %RUNMODE%
+  labels:
+    app: %SERVICE_NAME%
+spec:
+  selector:
+    app: %SERVICE_NAME%
+  ports:
+  - protocol: TCP
+    targetPort: %SERVE_PORT%
+    port: %SERVE_PORT%
+  type: ClusterIP

+ 77 - 0
errors/errors.go

@@ -0,0 +1,77 @@
+// Copyright 2019 getensh.com. All rights reserved.
+// Use of this source code is governed by getensh.com.
+
+// package error code define
+package errors
+
+import "gadm-ods/common.in/jsonrpc2"
+
+var (
+	// 通用错误
+	SystemError                     = jsonrpc2.NewJsonError(10001, "系统错误")
+	ServiceError                    = jsonrpc2.NewJsonError(10002, "内部服务错误")
+	NoAuthAccess                    = jsonrpc2.NewJsonError(10003, "无访问权限")
+	IpNoAuthAccess                  = jsonrpc2.NewJsonError(10004, "IP无访问权限")
+	CheckApiNotExist                = jsonrpc2.NewJsonError(10005, "该API不存在")
+	CheckMerchantNotExist           = jsonrpc2.NewJsonError(10006, "AppKey错误")
+	CheckApiInfoNotExist            = jsonrpc2.NewJsonError(10007, "商户未购买该API")
+	CheckSignFailed                 = jsonrpc2.NewJsonError(10008, "签名验证失败")
+	CheckDecryptFailed              = jsonrpc2.NewJsonError(10009, "解密参数失败")
+	CheckApiNotEnable               = jsonrpc2.NewJsonError(10010, "API未启用")
+	CheckApiDayCountLimit           = jsonrpc2.NewJsonError(10011, "API 达到当日上限")
+	CheckApiDaysLimit               = jsonrpc2.NewJsonError(10012, "API 已过有效期")
+	CheckApiTotalCountLimit         = jsonrpc2.NewJsonError(10013, "API 达到总次数上限")
+	CheckApiParamConfParseFailed    = jsonrpc2.NewJsonError(10014, "参数配置解析失败")
+	CheckApiProviderConfParseFailed = jsonrpc2.NewJsonError(10015, "数据源配置解析失败")
+	CheckParamShouldDecrypt         = jsonrpc2.NewJsonError(10016, "参数不应加密")
+	CheckParamShouldEncrypt         = jsonrpc2.NewJsonError(10017, "参数应加密")
+	ApiTimeOut                      = jsonrpc2.NewJsonError(10020, "接口响应超时")
+	// 业务错误 -00 通用
+	NoRecord    = jsonrpc2.NewJsonError(20001, "查无记录")
+	VendorError = jsonrpc2.NewJsonError(20002, "第三方发生错误")
+
+	// 业务错误 --平台错误(无效)
+	BadParaFormat   = jsonrpc2.NewJsonError(20003, "请求参数格式不对")
+	ArgsError       = jsonrpc2.NewJsonError(20004, "参数错误")
+	SenMqError      = jsonrpc2.NewJsonError(10001, "发送消息失败")
+	UpdateTaskError = jsonrpc2.NewJsonError(10002, "更新任务状态失败")
+	// 业务错误 --平台错误(有效)
+	DataBaseError                              = jsonrpc2.NewJsonError(20005, "数据库操作失败")
+	ProviderApiNotFound                        = jsonrpc2.NewJsonError(20006, "未找到数据源")
+	ProviderUnavailable                        = jsonrpc2.NewJsonError(20007, "数据源不可用")
+	VinPlateError                              = jsonrpc2.NewJsonError(20008, "车架号和车牌号码不匹配")
+	OrderNotExist                              = jsonrpc2.NewJsonError(20009, "订单不存在")
+	OrderCreateFailed                          = jsonrpc2.NewJsonError(20010, "订单创建失败")
+	OrderExistError                            = jsonrpc2.NewJsonError(20011, "不能重复创建订单")
+	OrderStateCanNotBeChange                   = jsonrpc2.NewJsonError(20012, "该订单状态不能被修改")
+	OrderStatusNotSupported                    = jsonrpc2.NewJsonError(20013, "不支持的订单状态")
+	NotSupportArea                             = jsonrpc2.NewJsonError(20014, "该地区不支持查询")
+	DriverLicenseAndNameNotMatch               = jsonrpc2.NewJsonError(20015, "驾驶证和驾驶人不匹配")
+	OrderParseTimeFailed                       = jsonrpc2.NewJsonError(20016, "时间格式解析错误,正确时间格式为 2006-01-02 15:04:05")
+	OrderWrongTime                             = jsonrpc2.NewJsonError(20017, "不能预约该时段,预约时间为未来7天 9:00至18:00,节假日和周日不办理")
+	ServiceNotSupport                          = jsonrpc2.NewJsonError(20018, "不支持的服务类型")
+	VehicleNotBeUncheckInspectionTypeFailed    = jsonrpc2.NewJsonError(20019, "无法免检年审,车辆性质或类型不匹配")
+	VehicleNotBeUncheckInspectionSeatFailed    = jsonrpc2.NewJsonError(20020, "无法免检年审,车座数不能大于7")
+	RegDateParseFailed                         = jsonrpc2.NewJsonError(20021, "初登日期解析失败")
+	VehicleNotBeUncheckInspectionRegDateFailed = jsonrpc2.NewJsonError(20022, "无法免检年审,初登日期不能在2010年9月1日之前")
+	CityIdNotExist                             = jsonrpc2.NewJsonError(20023, "未找到城市ID")
+	OrderNoGernerateFailed                     = jsonrpc2.NewJsonError(20024, "订单号生产失败")
+	TimeCalcFailed                             = jsonrpc2.NewJsonError(20025, "时间计算错误")
+	VehicleNoPlateNo                           = jsonrpc2.NewJsonError(20026, "暂未上牌")
+	DataError                                  = jsonrpc2.NewJsonError(20027, "数据异常")
+	AnnualInspectionUnsupportVehicle           = jsonrpc2.NewJsonError(20028, "该车辆种类不支持年检时间查询")
+	TemporarilyDissupport                      = jsonrpc2.NewJsonError(20029, "暂不支持查询")
+	NoRecordMustSelect                         = jsonrpc2.NewJsonError(20030, "查无记录,必选项为空")
+	NoRecordProviderLimit                      = jsonrpc2.NewJsonError(20031, "查无记录,数据源达到上限")
+	NoRecordProviderError                      = jsonrpc2.NewJsonError(20032, "查无记录,数据源最后一步异常")
+	NoRecordDataError                          = jsonrpc2.NewJsonError(20033, "查无记录,数据异常")
+	// 业务错误-02 vehicle
+	NoAuthQuery             = jsonrpc2.NewJsonError(20201, "无权查询非川牌车辆")
+	PlateNoError            = jsonrpc2.NewJsonError(20202, "号牌号码错误")
+	ProvinceError           = jsonrpc2.NewJsonError(20203, "省份错误")
+	PlateTypeNotExist       = jsonrpc2.NewJsonError(20204, "不存在的号牌种类")
+	PlateOrVehicleTypeError = jsonrpc2.NewJsonError(20205, "车牌号码与车牌种类不匹配")
+	QueryError              = jsonrpc2.NewJsonError(20344, "查询错误")
+	VehicleInfoQuery        = jsonrpc2.NewJsonError(20206, "车辆数据查询中,请稍后获取结果")
+	MoreStyle               = jsonrpc2.NewJsonError(20207, "无法唯一定型")
+)

+ 29 - 0
go.mod

@@ -0,0 +1,29 @@
+module gadm-ods
+
+go 1.12
+
+require (
+	github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible
+	github.com/astaxie/beego v1.12.0
+	github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
+	github.com/fsnotify/fsnotify v1.4.7
+	github.com/go-redis/redis v6.15.2+incompatible
+	github.com/go-sql-driver/mysql v1.6.0
+	github.com/golang/protobuf v1.5.2
+	github.com/lib/pq v1.1.1 // indirect
+	github.com/mattn/go-sqlite3 v1.14.0 // indirect
+	github.com/satori/go.uuid v1.2.0
+	github.com/smallnest/rpcx v0.0.0-20190830042331-e50181fc75b4
+	github.com/sony/sonyflake v1.0.0
+	github.com/streadway/amqp v1.0.0
+	github.com/tealeg/xlsx v1.0.5
+	github.com/tidwall/gjson v1.14.0
+	go.etcd.io/etcd/client/v3 v3.5.4
+	go.uber.org/zap v1.17.0
+	google.golang.org/grpc v1.38.0
+	gopkg.in/ini.v1 v1.46.0
+	gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
+	gopkg.in/natefinch/lumberjack.v2 v2.0.0
+	gorm.io/driver/mysql v1.3.3
+	gorm.io/gorm v1.23.4
+)

+ 549 - 0
go.sum

@@ -0,0 +1,549 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/DataDog/datadog-go v2.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+github.com/RoaringBitmap/roaring v0.4.7/go.mod h1:8khRDP4HmeXns4xIj9oGrKSz7XTQiJx2zgh7AcNke4w=
+github.com/abronan/valkeyrie v0.0.0-20190822142731-f2e1850dc905/go.mod h1:hTreU6x9m2IP2h8e0TGrSzAXSCI3lxic8/JT5CMknjY=
+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/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible h1:9gWa46nstkJ9miBReJcN8Gq34cBFbzSpQZVVT9N09TM=
+github.com/aliyun/aliyun-oss-go-sdk v2.2.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
+github.com/anacrolix/envpprof v0.0.0-20180404065416-323002cec2fa/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.0.0/go.mod h1:KgHhUaQMc8cC0+cEflSgCFNFbKwi5h54gqtVn8yhP7c=
+github.com/anacrolix/envpprof v1.0.1/go.mod h1:My7T5oSqVfEn4MD4Meczkw/f5lSIndGAKu/0SM/rkf4=
+github.com/anacrolix/log v0.3.0/go.mod h1:lWvLTqzAnCWPJA08T2HCstZi0L1y2Wyvm3FJgwU9jwU=
+github.com/anacrolix/missinggo v0.0.0-20180725070939-60ef2fbf63df/go.mod h1:kwGiTUTZ0+p4vAz3VbAI5a30t2YbvemcmspjKwrAz5s=
+github.com/anacrolix/missinggo v1.1.2-0.20190815015349-b888af804467/go.mod h1:MBJu3Sk/k3ZfGYcS7z18gwfu72Ey/xopPFJJbTi5yIo=
+github.com/anacrolix/missinggo v1.2.1/go.mod h1:J5cMhif8jPmFoC3+Uvob3OXXNIhOUikzMt+uUjeM21Y=
+github.com/anacrolix/missinggo/perf v1.0.0/go.mod h1:ljAFWkBuzkO12MQclXzZrosP5urunoLS0Cbvb4V0uMQ=
+github.com/anacrolix/sync v0.0.0-20180808010631-44578de4e778/go.mod h1:s735Etp3joe/voe2sdaXLcqDdJSay1O0OPnM0ystjqk=
+github.com/anacrolix/tagflag v0.0.0-20180109131632-2146c8d41bf0/go.mod h1:1m2U/K6ZT+JZG0+bdMK6qauP49QT4wE5pmhJXOKKCHw=
+github.com/anacrolix/utp v0.0.0-20180219060659-9e0e1d1d0572/go.mod h1:MDwc+vsGEq7RMw6lr2GKOEqjWny5hO5OZXRVNaBJ2Dk=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/apache/thrift v0.12.0 h1:pODnxUFNcjP9UTLZGTdeh+j16A8lJbRvD3rOtrk/7bs=
+github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
+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-metrics v0.0.0-20190430140413-ec5e00d3c878/go.mod h1:3AMJUQhVx52RsWOnlkpikZr01T/yAVN2gn0861vByNg=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
+github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
+github.com/aws/aws-sdk-go v1.16.23/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
+github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
+github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
+github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
+github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
+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/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/bradfitz/iter v0.0.0-20140124041915-454541ec3da2/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
+github.com/cenk/backoff v2.2.1+incompatible/go.mod h1:7FtoeaSnHoZnmZzz47cM35Y9nSW7tNyaidugnHTaFDE=
+github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cheekybits/genny v1.0.0/go.mod h1:+tQajlRqAUrPI7DOSpB0XAqZYtQakVtB7wXkRAgjxjQ=
+github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
+github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/coreos/bbolt v1.3.3/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+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-20190719114852-fd7a80b32e1f h1:JOrtw2xFKzlg+cbHpyrpLDmnN1HqhBfnX7WDiW7eG2c=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
+github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
+github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
+github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
+github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
+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/deckarep/golang-set v1.7.1 h1:SCQV0S6gTtp6itiFrTqI+pfmJ4LN85S1YzhDf9rTHJQ=
+github.com/deckarep/golang-set v1.7.1/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-jump v0.0.0-20170409065014-e1f439676b57/go.mod h1:4hKCXuwrJoYvHZxJ86+bRVTOMyJ0Ej+RqfSm8mHi6KA=
+github.com/docker/libkv v0.2.1/go.mod h1:r5hEwHwW8dr0TFBYGCarMNbrQOiwL1xoqDYZ/JqoTK0=
+github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
+github.com/dustin/go-humanize v0.0.0-20180421182945-02af3965c54e/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/edwingeng/doublejump v0.0.0-20190102103700-461a0155c7be/go.mod h1:sqbHCF7b7eMiCtiwNY5+2bqhT+Zx6Duj2VU5WigITOQ=
+github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE=
+github.com/glycerine/goconvey v0.0.0-20180728074245-46e3a41ad493/go.mod h1:Ogl1Tioa0aV7gstGFO7KhffUsb9M4ydbEbbxpcEDc24=
+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/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis v6.15.2+incompatible h1:9SpNVG76gr6InJGxoZ6IuuxaCOQwDAhzyXg+Bs+0Sb4=
+github.com/go-redis/redis v6.15.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
+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/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.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+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/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/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/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.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.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/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+github.com/google/btree v0.0.0-20180124185431-e89373fe6b4a/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+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/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+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.5.0/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 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e h1:JKmoR8x90Iww1ks85zJ1lfDGgIiMDuIptTOhJq+zKyg=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/grandcat/zeroconf v0.0.0-20190424104450-85eadb44205c/go.mod h1:YjKB0WsLXlMkO9p+wGTCoPIDGRJH0mz7E526PxkQVxI=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+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.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+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-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
+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/golang-lru v0.5.3 h1:YPkqC67at8FYaadspW/6uE0COsBxS2656RLEr8Bppgk=
+github.com/hashicorp/golang-lru v0.5.3/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+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/memberlist v0.1.4/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
+github.com/influxdata/influxdb1-client v0.0.0-20190809212627-fc22c7df067e/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
+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.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+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.7/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/jtolds/gls v4.2.1+incompatible h1:fSuqC+Gmlu6l/ZYAoZzx2pyucC8Xza35fpRVWLVmUEE=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
+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/kavu/go_reuseport v1.4.0/go.mod h1:CG8Ee7ceMFSMnx/xr25Vm0qXaj2Z4i5PWoUx+JZ5/CU=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+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/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
+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/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lucas-clemente/quic-go v0.11.0/go.mod h1:PpMmPfPKO9nKJ/psF49ESTAGQSdfXxlg1otPbEB2nOw=
+github.com/marten-seemann/qtls v0.2.3/go.mod h1:xzjG7avBwGGbdZ8dTGxlBnLArsVKLvwmjgmPuiQEcYk=
+github.com/marten-seemann/quic-conn v0.0.0-20190827120552-a06e62da55b7/go.mod h1:BTUDloYEkSYt9Yf98rSVDnIFrSJc04H3/6FfE+lsjNg=
+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-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/mattn/go-sqlite3 v1.14.0 h1:mLyGNKR8+Vv9CAU7PphKa2hkEqxxhn8i32J6FPj1/QA=
+github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
+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/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+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/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
+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/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.10.0 h1:XaTQmdKecIbwNHpzOIy0XMoEG5bmv/n0OVyaF1NKUdo=
+github.com/onsi/ginkgo v1.10.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/onsi/gomega v1.5.0 h1:izbySO9zDPmjJ8rDjLvkA2zJHIo+HkYXHnf7eN7SSyo=
+github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
+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/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/peterbourgon/g2s v0.0.0-20170223122336-d4e7ad98afea/go.mod h1:1VcHEd3ro4QMoHfiNl/j7Jkln9+KQuorp0PItHMJYNg=
+github.com/philhofer/fwd v1.0.0/go.mod h1:gk3iGcWd9+svBvR0sR+KPcfE+RNWozjowpeBVG3ZVNU=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+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/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 v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+github.com/prometheus/client_golang v1.11.1/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/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+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.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
+github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/rpcx-ecosystem/quic-conn v0.0.0-20190830034555-28ef71d38a15/go.mod h1:jk/xizLAYKg5ft2h5IAqThQuxtMRUE6dL93liAh4WEk=
+github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
+github.com/rubyist/circuitbreaker v2.2.1+incompatible/go.mod h1:Ycs3JgJADPuzJDwffe12k6BZT8hxVi6lFK+gWYJLN4A=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8=
+github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/serialx/hashring v0.0.0-20180504054112-49a4782e9908/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc=
+github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
+github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
+github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
+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/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/smallnest/libkv-etcdv3-store v0.0.0-20190827025031-6b33215321f0/go.mod h1:SYR75Tf6jJoZ8wZca3fqyzXorUyOwnorUYIKv4T+u9Q=
+github.com/smallnest/rpcx v0.0.0-20190830042331-e50181fc75b4 h1:v3LYeX4+wpWeGqR23bsHKRpefhvwrgo4VD7zb9jhG+Y=
+github.com/smallnest/rpcx v0.0.0-20190830042331-e50181fc75b4/go.mod h1:mMw5Jh8gV9ongiVM90AGxXLUa+x4lPwHPjG/N//OsFE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c h1:Ho+uVpkel/udgjbwB5Lktg9BtvJSh2DT0Hi6LPSyI2w=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
+github.com/sony/sonyflake v1.0.0/go.mod h1:Jv3cfhf/UFtolOTTRd3q4Nl6ENqM+KfyZ5PseKfZGF4=
+github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
+github.com/streadway/amqp v1.0.0 h1:kuuDrUJFZL1QYL9hUNuCxNObNzB0bV/ZG5jV3RWAQgo=
+github.com/streadway/amqp v1.0.0/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
+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.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+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.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
+github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
+github.com/tealeg/xlsx v1.0.5/go.mod h1:btRS8dz54TDnvKNosuAqxrM1QgN1udgk9O34bDCnORM=
+github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
+github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
+github.com/tidwall/gjson v1.14.0 h1:6aeJ0bzojgWLa82gDQHcx3S0Lr/O51I9bJ5nv6JFx5w=
+github.com/tidwall/gjson v1.14.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/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
+github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
+github.com/valyala/fastrand v1.0.0/go.mod h1:HWqCzkrkg6QXT8V2EXWvXCoow7vLwOFN002oeRzjapQ=
+github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
+github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
+github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
+github.com/willf/bitset v1.1.9/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
+github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
+github.com/yuin/goldmark v1.1.27/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.1-etcd.8/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.etcd.io/etcd v3.3.13+incompatible h1:jCejD5EMnlGxFvcGRyEV4VGlENZc7oPQX6o0t7n3xbw=
+go.etcd.io/etcd v3.3.13+incompatible/go.mod h1:yaeTdrJi5lOmYerz05bd8+V7KubZs8YSFZfzsF9A6aI=
+go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc=
+go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg=
+go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
+go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4=
+go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
+go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
+go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+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/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U=
+go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
+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=
+golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/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/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/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.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/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-20181220203305-927f97764cc3/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-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-20200202094626-16171245cfb2/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-20200324143707-d3edc9973b7e/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-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+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-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+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-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-20180830151530-49385e6e1522/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-20181107165924-66b7b1311ac8/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-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-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-20210124154548-22da62e12c0c/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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q=
+golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.0/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.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
+golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/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-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.2/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/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c h1:wtujag7C+4D6KMoulW9YauvK2lgdvCMS260jsqqBXr0=
+google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
+google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0=
+google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+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.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=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+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 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
+gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
+gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
+gopkg.in/redis.v5 v5.2.9/go.mod h1:6gtv0/+A4iM08kdRfocWYB3bLX2tebpNtfKlFT6H4mY=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+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.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+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=
+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.3.3 h1:jXG9ANrwBc4+bMvBcSl8zCfPBaVoPyBEBshA8dA93X8=
+gorm.io/driver/mysql v1.3.3/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
+gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+gorm.io/gorm v1.23.4 h1:1BKWM67O6CflSLcwGQR7ccfmC4ebOxQrTfOQGRE9wjg=
+gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=

+ 111 - 0
imagebuild.sh

@@ -0,0 +1,111 @@
+#!/bin/bash
+
+set -e
+
+show_usage="args: [--runmode=,--serve-addr=,--serve-port=,--etcd-addr=,--etcd-port=,--encrypt-key=,--log-dir=,--image-name=]"
+
+#-o或--options选项后面是可接受的短选项,如ab:c::,表示可接受的短选项为-a -b -c,
+#其中-a选项不接参数,-b选项后必须接参数,-c选项的参数为可选的
+#-l或--long选项后面是可接受的长选项,用逗号分开,冒号的意义同短选项。
+#-n选项后接选项解析错误时提示的脚本名字
+ARGS=`getopt -o -h --long help,runmode:,serve-addr:,serve-port:,etcd-addr:,etcd-port:,encrypt-key:,log-dir:,image-name: -n "$0" -- "$@"`
+
+if [ $? != 0 ]; then
+    exit 1
+fi
+
+#将规范化后的命令行参数分配至位置参数($1,$2,...)
+eval set -- "${ARGS}"
+
+while [ -n "$1" ]
+do
+    case "$1" in
+        -h|--help)
+            echo "$show_usage"; exit 1 ;;
+        --runmode)
+            RUNMODE=$2; shift ;;
+        --serve-addr)
+            SERVE_ADDR=$2; shift ;;
+        --serve-port)
+            SERVE_PORT=$2; shift ;;
+        --etcd-addr)
+            ETCD_ADDR=$2; shift ;;
+        --etcd-port)
+            ETCD_PORT=$2; shift ;;
+        --encrypt-key)
+            ENCRYPT_KEY=$2; shift ;;
+        --image-name)
+            IMAGE_NAME=$2; shift ;;
+        --log-dir)
+            LOG_DIR=$2; shift ;;
+        *) shift ;;
+    esac
+done
+
+DISCOVERY_TYPE="k8s"
+
+if [ -z $SERVE_PORT ];then
+    SERVE_PORT=60001
+fi
+
+if [ -z $RUNMODE ];then
+    RUNMODE="dev"
+fi
+if [ -z $SERVE_ADDR ];then
+    SERVE_ADDR="0.0.0.0"
+fi
+if [ -z $ETCD_ADDR ];then
+    ETCD_ADDR="127.0.0.1"
+fi
+if [ -z $ETCD_PORT ];then
+    ETCD_PORT="2379"
+fi
+if [ -z $ENCRYPT_KEY ];then
+    ENCRYPT_KEY="a95cbb574bdc905f9bf457820f1fa602"
+fi
+if [ -z $LOG_DIR ];then
+    LOG_DIR="logs"
+fi
+
+SERVICE_NAME="dip-vehicle"
+
+if [ -z $IMAGE_NAME ];then
+    IMAGE_NAME=$SERVICE_NAME
+fi
+
+PROJECT_PATH=$(cd `dirname $0`; pwd)
+APP_NAME="${PROJECT_PATH##*/}"
+APP_CONF=conf/app.conf
+
+
+#生成配置文件
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${APP_CONF}.in > ${APP_CONF}
+sed -i "s/%APP_NAME%/$APP_NAME/g" ${APP_CONF}
+sed -i "s/%RUNMODE%/$RUNMODE/g" ${APP_CONF}
+sed -i "s/%SERVE_ADDR%/$SERVE_ADDR/g" ${APP_CONF}
+sed -i "s/%SERVE_PORT%/$SERVE_PORT/g" ${APP_CONF}
+sed -i "s/%ETCD_ADDR%/$ETCD_ADDR/g" ${APP_CONF}
+sed -i "s/%ETCD_PORT%/$ETCD_PORT/g" ${APP_CONF}
+sed -i "s/%ENCRYPT_KEY%/$ENCRYPT_KEY/g" ${APP_CONF}
+sed -i "s/%LOG_DIR%/$LOG_DIR/g" ${APP_CONF}
+sed -i "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${APP_CONF}
+sed -i "s/%DISCOVERY_TYPE%/$DISCOVERY_TYPE/g" ${APP_CONF}
+
+VERSION=`cat VERSION`
+make version=$VERSION
+
+DOCKERFILE=Dockerfile
+DOCKERCOMPOSE=docker/compose/docker-compose.yaml
+K8SFILE=docker/kubernetes/$SERVICE_NAME.yaml
+
+sed -e "s/%APP_NAME%/$APP_NAME/g" ${DOCKERFILE}.in > ${DOCKERFILE}
+sed -i "s/%SERVE_PORT%/$SERVE_PORT/g" ${DOCKERFILE}
+
+sed -e "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${DOCKERCOMPOSE}.in > ${DOCKERCOMPOSE}
+
+sed -e "s/%SERVICE_NAME%/$SERVICE_NAME/g" ${K8SFILE}.in > ${K8SFILE}
+sed -i "s/%SERVE_PORT%/$SERVE_PORT/g" ${K8SFILE}
+sed -i "s/%RUNMODE%/$RUNMODE/g" ${K8SFILE}
+
+docker build . -t $IMAGE_NAME
+

+ 130 - 0
impl/analysis/common.go

@@ -0,0 +1,130 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import "strconv"
+
+func checkDataMapListEqual(oldDataMapList, newDataMapList []map[string]string) bool {
+	if len(oldDataMapList) == len(newDataMapList) {
+		for index, v := range newDataMapList {
+			for key, value := range v {
+				if value1, ok := oldDataMapList[index][key]; ok {
+					if value != value1 {
+						return false
+					}
+				} else {
+					return false
+				}
+			}
+		}
+	} else {
+		return false
+	}
+
+	return true
+}
+
+func calcPlateType(longStr, approvedNumberStr, grossMassStr string) string {
+	if longStr == "" && approvedNumberStr == "" && grossMassStr == "" {
+		return ""
+	}
+	long, _ := strconv.Atoi(longStr)
+	approvedNumber, _ := strconv.Atoi(approvedNumberStr)
+	grossMass, _ := strconv.Atoi(grossMassStr)
+	if (long != 0) && (approvedNumber != 0 || grossMass != 0) {
+		if (long < 6000 && approvedNumber > 9) || long >= 6000 || approvedNumber >= 20 || grossMass >= 4500 {
+			return "01"
+		} else {
+			return "02"
+		}
+	}
+
+	return ""
+}
+
+func checkLastMapIsLastDate(lastMap, newMap map[string]string) bool {
+	if lastMap["last_compulsory_insurance_date"] == newMap["last_compulsory_insurance_date"] {
+		if lastMap["insurance_first_date"] < newMap["insurance_first_date"] {
+			return false
+		}
+	} else if lastMap["last_compulsory_insurance_date"] < newMap["last_compulsory_insurance_date"] {
+		return false
+	}
+
+	return true
+}
+func sortByDate(dataMapList []map[string]string) (newDataMapList []map[string]string) {
+	// TODO 准确判断出过期的数据
+	var lastMap map[string]string
+	var lastOtherMap map[string]string
+	for _, v := range dataMapList {
+		plateType := ""
+		longStr := ""
+		approvedNumberStr := ""
+		grossMassStr := ""
+
+		if value, ok := v["plate_type"]; ok {
+			plateType = value
+		}
+
+		if value, ok := v["long"]; ok {
+			longStr = value
+		}
+
+		if value, ok := v["approved_number"]; ok {
+			approvedNumberStr = value
+		}
+
+		if value, ok := v["gross_mass"]; ok {
+			grossMassStr = value
+		}
+
+		if plateType == "" {
+			plateType := calcPlateType(longStr, approvedNumberStr, grossMassStr)
+			v["plate_type"] = plateType
+		}
+
+		if lastMap == nil {
+			lastMap = v
+		} else {
+			// 号牌种类相同
+			if lastMap["plate_type"] == v["plate_type"] {
+				if checkLastMapIsLastDate(lastMap, v) {
+					delete(v, "plate_type")
+					newDataMapList = append(newDataMapList, v)
+				} else {
+					delete(lastMap, "plate_type")
+					newDataMapList = append(newDataMapList, lastMap)
+					lastMap = v
+				}
+			} else {
+				// 号牌种类不同
+				if lastOtherMap == nil {
+					lastOtherMap = v
+				} else {
+					if checkLastMapIsLastDate(lastOtherMap, v) {
+						delete(v, "plate_type")
+						newDataMapList = append(newDataMapList, v)
+					} else {
+						delete(lastOtherMap, "plate_type")
+						newDataMapList = append(newDataMapList, lastOtherMap)
+						lastOtherMap = v
+					}
+				}
+			}
+		}
+	}
+
+	if lastMap != nil {
+		delete(lastMap, "plate_type")
+		newDataMapList = append(newDataMapList, lastMap)
+	}
+
+	if lastOtherMap != nil {
+		delete(lastOtherMap, "plate_type")
+		newDataMapList = append(newDataMapList, lastOtherMap)
+	}
+
+	return newDataMapList
+}

+ 15 - 0
impl/analysis/logger.go

@@ -0,0 +1,15 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"go.uber.org/zap"
+)
+
+var l *zap.Logger
+func SetLogger(logger *zap.Logger) {
+	l = logger
+}
+
+

+ 97 - 0
impl/analysis/ods1.go

@@ -0,0 +1,97 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/consts"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+	"strings"
+)
+
+// CXY 违章查询
+func ParasOds1(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	rawCode := data.Get("raw_code").String()
+	dataMap := make(map[string]string)
+	if rawCode == "0" || rawCode == "-6" {
+		arrayStr := data.Get("request_params").String()
+		array := gjson.Parse(arrayStr).Array()
+		if len(array) < 2 {
+			return nil, fmt.Errorf("数据异常,没有数据")
+		}
+		requestParams := array[1]
+
+		dataMap["plate_no"] = requestParams.Get("carnumber").String()
+		dataMap["plate_type"] = requestParams.Get("cartype").String()
+		dataMap["vin"] = requestParams.Get("carcode").String()
+		dataMap["engine_no"] = requestParams.Get("cardrivenumber").String()
+		if rawCode == "0" {
+			dataMap["is_expire"] = consts.NORMALDATA
+		} else {
+			dataMap["is_expire"] = consts.EXPIREDATA
+		}
+
+		return append(ret, dataMap), nil
+	}
+
+	l.Error("func",
+		zap.String("call", "ParasOds2"),
+		zap.String("args", content),
+		zap.String("error", "数据异常,无法解析"))
+
+	return nil, fmt.Errorf("数据异常")
+}
+
+func HandleOnlineOds1(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds1(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 如本地库
+	ods1 := &model.Ods1{}
+	ods1.Vin = dataMapList[0]["vin"]
+	ods1.PlateType = dataMapList[0]["plate_type"]
+	ods1.PlateNo = dataMapList[0]["plate_no"]
+	ods1.EngineNo = dataMapList[0]["engine_no"]
+	ods1.Content = msg.Content
+	err = ods1.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			// 如果存在判断是否字段相等
+			where := map[string]interface{}{"vin": ods1.Vin, "plate_no": ods1.PlateNo, "plate_type": ods1.PlateType, "engine_no": ods1.EngineNo}
+			oldOds1 := &model.Ods1{}
+			err = oldOds1.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds1(oldOds1.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+
+			ods1.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	// 构建消息
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 92 - 0
impl/analysis/ods10.go

@@ -0,0 +1,92 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// SPY 二要素验证(新能源)(128-008)
+func ParasOds10(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	plateNo := gjson.Get(requestParams, "cp").String()
+	owner := gjson.Get(requestParams, "name").String()
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	if plateNo == "" || owner == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["plate_no"] = plateNo
+	dataMap["owner"] = owner
+	respData := gjson.Get(responseParams, "data").String()
+	isMatch := gjson.Get(respData, "isMatch").Bool()
+	if isMatch {
+		ret = append(ret, dataMap)
+		return ret, nil
+	} else {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+	/*if isMatch {
+		dataMap["is_expire"] = consts.NORMALDATA
+	}else{
+		dataMap["is_expire"] = consts.EXPIREDATA
+	}
+	ret = append(ret, dataMap)
+	return ret, nil*/
+}
+
+func HandleOnlineOds10(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds10(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods10 := &model.Ods10{}
+	ods10.PlateNo = dataMapList[0]["plate_no"]
+	ods10.Owner = dataMapList[0]["owner"]
+	ods10.Content = msg.Content
+	err = ods10.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods10.PlateNo, "owner": ods10.Owner}
+			oldOds10 := &model.Ods10{}
+			err = oldOds10.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds10(oldOds10.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods10.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 101 - 0
impl/analysis/ods11.go

@@ -0,0 +1,101 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// DRV 车牌姓名验证(138-002)
+func ParasOds11(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+
+	plateNo := requestParam.Get("plateNumber").String()
+	plateType := requestParam.Get("plateType").String()
+	owner := requestParam.Get("name").String()
+
+	responseParams := data.Get("response_params").String()
+	respData := gjson.Get(responseParams, "data").String()
+	if respData == "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	verifyData := gjson.Parse(respData).Get("data").String()
+	msg := gjson.Parse(respData).Get("msg").String()
+
+	if verifyData != "1" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	// 0为一致
+	if msg != "0" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	if plateNo == "" || plateType == "" || owner == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["plate_no"] = plateNo
+	dataMap["plate_type"] = plateType
+	dataMap["owner"] = owner
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+func HandleOnlineOds11(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds11(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods11 := &model.Ods11{}
+	ods11.PlateNo = dataMapList[0]["plate_no"]
+	ods11.PlateType = dataMapList[0]["plate_type"]
+	ods11.Owner = dataMapList[0]["owner"]
+	ods11.Content = msg.Content
+	err = ods11.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods11.PlateNo, "plate_type": ods11.PlateType, "owner": ods11.Owner}
+			oldOds11 := &model.Ods11{}
+			err = oldOds11.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds11(oldOds11.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods11.UpdateWhere(clinit.DB(), where)
+		}
+
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 117 - 0
impl/analysis/ods12.go

@@ -0,0 +1,117 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+func ConvertPlateType(s string) (plateColor string, exist bool) {
+	plateTypeMap := map[string]string{
+		"1": "01",
+		"0": "02",
+		"5": "51",
+		"4": "52",
+	}
+
+	if v, ok := plateTypeMap[s]; ok {
+		return v, true
+	}
+
+	return
+}
+
+//   ZCRK 姓名车牌号核查(河南)(141-001)
+func ParasOds12(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+
+	responseParams := data.Get("response_params").String()
+	code := gjson.Get(responseParams, "code").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	result := gjson.Get(responseParams, "result").String()
+	consistent := gjson.Get(result, "consistent").Bool()
+	if !consistent {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	plateNo := requestParam.Get("plateNo").String()
+	plateType := requestParam.Get("plateType").String()
+	if plateType == "" {
+		plateColor := requestParam.Get("plateColor").String()
+		if plateColor != "" {
+			plateType, _ = ConvertPlateType(plateColor)
+		}
+	}
+
+	owner := requestParam.Get("name").String()
+	if plateNo == "" || plateType == "" || owner == "" {
+		return nil, fmt.Errorf("数据异常,数据为空")
+	}
+	dataMap := make(map[string]string)
+	dataMap["plate_no"] = plateNo
+	dataMap["plate_type"] = plateType
+	dataMap["owner"] = owner
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+func HandleOnlineOds12(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds12(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods12 := &model.Ods12{}
+	ods12.PlateNo = dataMapList[0]["plate_no"]
+	ods12.PlateType = dataMapList[0]["plate_type"]
+	ods12.Owner = dataMapList[0]["owner"]
+	ods12.Content = msg.Content
+	err = ods12.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods12.PlateNo, "plate_type": ods12.PlateType, "owner": ods12.Owner}
+			oldOds12 := &model.Ods12{}
+			err = oldOds12.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds12(oldOds12.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods12.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 134 - 0
impl/analysis/ods13.go

@@ -0,0 +1,134 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/consts"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+func dumpOds13() {
+
+}
+
+//  ZJC 所有人验证(142-001)
+func ParasOds13(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+	plateNo := requestParam.Get("carno").String()
+	plateType := requestParam.Get("plateType").String()
+	owner := requestParam.Get("name").String()
+	if plateNo == "" || owner == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	responseParams := data.Get("response_params").String()
+	errorCode := gjson.Get(responseParams, "err_code").String()
+	if errorCode != "200" {
+		return nil, fmt.Errorf("数据异常,没有数据", errorCode)
+	}
+
+	respData := gjson.Get(responseParams, "data").String()
+	if respData == "" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	detail := gjson.Get(respData, "vehicle_drivers_permit_detailed").String()
+	detailCode := gjson.Get(detail, "code").String()
+	if detailCode != "9100" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["plate_no"] = plateNo
+	dataMap["plate_type"] = plateType
+
+	detailData := gjson.Get(detail, "data")
+	ownerVerifyResult := detailData.Get("owerState").String()
+	dataMap["owner"] = owner
+	if ownerVerifyResult == "1" {
+		dataMap["is_expire"] = consts.NORMALDATA
+	} else {
+		dataMap["is_expire"] = consts.EXPIREDATA
+	}
+
+	dataMap["brand_name"] = detailData.Get("brandName").String()
+	dataMap["use_property"] = detailData.Get("useType").String()
+	dataMap["vehicle_body_color"] = detailData.Get("bodyColor").String()
+	dataMap["engine_no"] = detailData.Get("engineNumber").String()
+	dataMap["vin"] = detailData.Get("frameVIN").String()
+	dataMap["compulsory_scrap_to"] = detailData.Get("retirementDate").String()
+	dataMap["engine_type"] = detailData.Get("engineType").String()
+	dataMap["initial_registration_date"] = detailData.Get("firstIssueDate").String()
+	dataMap["inspection_result_effective_to"] = detailData.Get("validityDayEnd").String()
+	dataMap["fuel_type"] = detailData.Get("fuelType").String()
+	dataMap["state"] = detailData.Get("vehicleStatus").String()
+	dataMap["vehicle_type"] = detailData.Get("vehicleType").String()
+	dataMap["approved_number"] = detailData.Get("passengers").String()
+	dataMap["displacement"] = detailData.Get("displacement").String()
+	dataMap["release_date"] = detailData.Get("productionDate").String()
+	dataMap["rated_power"] = detailData.Get("maxJourney").String()
+	dataMap["axle_number"] = detailData.Get("shaft").String()
+	dataMap["wheel_base"] = detailData.Get("wheelBase").String()
+	dataMap["front_wheel_distance"] = detailData.Get("frontTread").String()
+	dataMap["back_wheel_distance"] = detailData.Get("rearTread").String()
+	dataMap["gross_mass"] = detailData.Get("crossWeight").String()
+	dataMap["unladen_mass"] = detailData.Get("curbWeight").String()
+	dataMap["approved_load"] = detailData.Get("loadWeight").String()
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+func HandleOnlineOds13(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds13(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods13 := &model.Ods13{}
+	ods13.PlateNo = dataMapList[0]["plate_no"]
+	ods13.PlateType = dataMapList[0]["plate_type"]
+	ods13.Owner = dataMapList[0]["owner"]
+	ods13.Content = msg.Content
+	err = ods13.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods13.PlateNo, "plate_type": ods13.PlateType, "owner": ods13.Owner}
+			oldOds13 := &model.Ods13{}
+			err = oldOds13.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds13(oldOds13.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods13.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	for _, dataMap := range dataMapList {
+		if dataMap["is_expire"] == consts.EXPIREDATA {
+			delete(dataMap, "owner")
+		}
+	}
+	return dataMapList, nil
+}

+ 4 - 0
impl/analysis/ods14.go

@@ -0,0 +1,4 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis

+ 84 - 0
impl/analysis/ods15.go

@@ -0,0 +1,84 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+//  DY身份认证()
+func ParasOds15(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+	idCard := requestParam.Get("identityCard").String()
+	name := requestParam.Get("name").String()
+	if idCard == "" || name == "" {
+		return nil, fmt.Errorf("数据异常,身份证姓名任意为空")
+	}
+
+	responseParams := data.Get("response_params").String()
+	errorCode := gjson.Get(responseParams, "code").String()
+	if errorCode != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据", errorCode)
+	}
+
+	compStatus := gjson.Get(responseParams, "data.compStatus").String()
+	if compStatus != "1" {
+		return nil, fmt.Errorf("数据异常,不匹配")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["id_card"] = idCard
+	dataMap["name"] = name
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+func HandleOnlineOds15(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds15(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods15 := &model.Ods15{}
+	ods15.Name = dataMapList[0]["name"]
+	ods15.IdCard = dataMapList[0]["id_card"]
+	ods15.Content = msg.Content
+	err = ods15.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} /*else {
+			where := map[string]interface{}{"name": ods15.Name, "id_card": ods15.IdCard}
+			oldods15 := &model.Ods15{}
+			err = oldods15.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds15(oldods15.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods15.UpdateWhere(clinit.DB(), where)
+		}*/
+		return nil, nil
+	}
+	return dataMapList, nil
+}

+ 105 - 0
impl/analysis/ods16.go

@@ -0,0 +1,105 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// spy 	VIN查询车型信息128-011
+func ParasOds16(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	vin := gjson.Get(requestParams, "vin").String()
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常,vin为空")
+	}
+
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	stateCode := gjson.Get(responseParams, "data.stateCode").String()
+	if stateCode == "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	respData := gjson.Get(responseParams, "data").String()
+	dataMap := make(map[string]string)
+	dataMap["vin"] = vin
+	dataMap["model_no"] = gjson.Get(respData, "clxh").String()
+	dataMap["approved_number"] = gjson.Get(respData, "edzk").String()
+	dataMap["engine_type"] = gjson.Get(respData, "fdjxh").String()
+	dataMap["rated_power"] = gjson.Get(respData, "gl").String()
+	plL := gjson.Get(respData, "plL").String()
+	if plL != "" {
+		tmp := strings.Replace(plL, "L", "", -1)
+		tmp = strings.Replace(tmp, "T", "", -1)
+		dataMap["displacement_l"] = tmp
+		if strings.HasSuffix(plL, "L") {
+			dataMap["air_intak_form"] = "L"
+		} else if strings.HasSuffix(plL, "T") {
+			dataMap["air_intak_form"] = "T"
+		}
+	}
+	dataMap["third_style_ids"] = gjson.Get(respData, "qczjIds").String()
+	dataMap["fuel_type_detail"] = gjson.Get(respData, "rlzl").String()
+	dataMap["long"] = gjson.Get(respData, "wkc").String()
+	dataMap["high"] = gjson.Get(respData, "wkg").String()
+	dataMap["wide"] = gjson.Get(respData, "wkk").String()
+	dataMap["wheel_base"] = gjson.Get(respData, "zj").String()
+	//dataMap["brand_name"] = gjson.Get(respData,"zpp").String()
+	dataMap["brand_name"] = gjson.Get(respData,"brand").String()
+	dataMap["source"] = "2"
+	ret = append(ret, dataMap)
+	return ret, nil
+}
+
+func HandleOnlineOds16(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds16(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods16 := &model.Ods16{}
+	ods16.Vin = dataMapList[0]["vin"]
+	ods16.Content = msg.Content
+	err = ods16.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods16.Vin}
+			oldOds16 := &model.Ods16{}
+			err = oldOds16.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds16(oldOds16.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods16.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 49 - 0
impl/analysis/ods17.go

@@ -0,0 +1,49 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"strings"
+)
+
+//   ZCRK 姓名车牌号核查(全国)(141-002)
+func HandleOnlineOds17(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds12(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods17 := &model.Ods17{}
+	ods17.PlateNo = dataMapList[0]["plate_no"]
+	ods17.PlateType = dataMapList[0]["plate_type"]
+	ods17.Owner = dataMapList[0]["owner"]
+	ods17.Content = msg.Content
+	err = ods17.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods17.PlateNo, "plate_type": ods17.PlateType, "owner": ods17.Owner}
+			oldOds17 := &model.Ods17{}
+			err = oldOds17.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds12(oldOds17.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods17.UpdateWhere(clinit.DB(), where)
+		}
+	}
+	return dataMapList, nil
+}

+ 80 - 0
impl/analysis/ods18.go

@@ -0,0 +1,80 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+func ParasOds18(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+
+	responseParams := data.Get("response_params").String()
+	code := gjson.Get(responseParams, "code").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	result := gjson.Get(responseParams, "result").String()
+	engineNo := gjson.Get(result, "engine_number").String()
+	vin := requestParam.Get("vin").String()
+
+	if vin == "" || engineNo == "" {
+		return nil, fmt.Errorf("数据异常")
+	}
+	dataMap := make(map[string]string)
+	dataMap["vin"] = vin
+	dataMap["engine_no"] = engineNo
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+//   ZCRK vin 查发动机号(141-003)
+func HandleOnlineOds18(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds18(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods18 := &model.Ods18{}
+	ods18.Vin = dataMapList[0]["vin"]
+	ods18.Content = msg.Content
+	err = ods18.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods18.Vin}
+			oldOds18 := &model.Ods18{}
+			err = oldOds18.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds18(oldOds18.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods18.UpdateWhere(clinit.DB(), where)
+		}
+	}
+	return dataMapList, nil
+}

+ 96 - 0
impl/analysis/ods19.go

@@ -0,0 +1,96 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+func ParasOds19(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+
+	requestParams := data.Get("request_params").String()
+	requestParamsArray := gjson.Parse(requestParams).Array()
+	if len(requestParamsArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	requestParam := requestParamsArray[0]
+
+	responseParams := data.Get("response_params").String()
+	code := gjson.Get(responseParams, "code").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	result := gjson.Get(responseParams, "result").String()
+	vin := requestParam.Get("vin").String()
+
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常")
+	}
+	dataMap := make(map[string]string)
+	dataMap["release_date"] = gjson.Get(result,"manufacturerDate").String()
+	dataMap["vehicle_manufacturer"] = gjson.Get(result,"manufacturerName").String()
+	dataMap["model_no"] = gjson.Get(result,"vehicleCode").String()
+	dataMap["brand_name"] = gjson.Get(result,"brandName").String()
+	dataMap["engine_no"] = gjson.Get(result,"engineCode").String()
+	dataMap["wheel_base"]= gjson.Get(result,"wheelbase").String()
+	dataMap["long"] = gjson.Get(result,"vehicleLength").String()
+	dataMap["wide"]  = gjson.Get(result,"vehicleWidth").String()
+	dataMap["high"]  = gjson.Get(result,"vehicleHeight").String()
+	dataMap["fuel_type_detail"] = gjson.Get(result,"dynamicType").String()
+	dataMap["displacement"] =gjson.Get(result,"displacement").String()
+	dataMap["rated_power"] = gjson.Get(result,"power").String()
+	dataMap["gross_mass"] = gjson.Get(result,"totalWeight").String()
+	dataMap["vehicle_type_detail"] = gjson.Get(result,"autoCategory").String()
+	dataMap["vehicle_body_color_detail"] = gjson.Get(result,"color").String()
+	dataMap["unladen_mass"]  = gjson.Get(result,"curbWeight").String()
+	dataMap["approved_number"]  = gjson.Get(result,"ratedPassengers").String()
+	dataMap["engine_type"] = gjson.Get(result,"engineModel").String()
+	dataMap["vin"] = vin
+	ret = append(ret, dataMap)
+	return ret, nil
+
+}
+
+//   ZCRK vin 查发动机号(141-003)
+func HandleOnlineOds19(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds19(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods19 := &model.Ods19{}
+	ods19.Vin = dataMapList[0]["vin"]
+	ods19.Content = msg.Content
+	err = ods19.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods19.Vin}
+			oldOds19 := &model.Ods19{}
+			err = oldOds19.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds19(oldOds19.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods19.UpdateWhere(clinit.DB(), where)
+		}
+	}
+	return dataMapList, nil
+}

+ 139 - 0
impl/analysis/ods2.go

@@ -0,0 +1,139 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+	"strings"
+)
+
+// ZQY 车牌查车档119-001
+func ParasOds2(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	//rawCode := data.Get("raw_code").String()
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+
+	requestArray := gjson.Parse(requestParams).Array()
+	if len(requestArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	plateNo := requestArray[0].Get("cph").String()
+	if plateNo == "" {
+		return nil, fmt.Errorf("数据异常,车牌号为空")
+	}
+
+	state := gjson.Get(responseParams, "ok").String()
+	if state != "1" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	responseArray := gjson.Get(responseParams, "ds").Array()
+	fmt.Println("")
+	for _, v := range responseArray {
+		dataMap := make(map[string]string)
+		dataMap["plate_no"] = plateNo
+		dataMap["vin"] = v.Get("车辆识别代码").String()
+		dataMap["brand_name"] = v.Get("品牌").String()
+		//dataMap["vehicle_type_detail"] = v.Get("车辆名称").String()
+		dataMap["model_no"] = v.Get("车辆型号").String()
+		dataMap["use_property_detail"] = v.Get("使用性质").String()
+		dataMap["insurance_first_date"] = v.Get("初次登记时间").String()
+		dataMap["fuel_type_detail"] = v.Get("燃料种类").String()
+		dataMap["displacement"] = v.Get("排量").String()
+		dataMap["engine_type"] = v.Get("发动机型号").String()
+		dataMap["approved_number"] = v.Get("额定载客").String()
+		dataMap["vehicle_body_color_detail"] = v.Get("车身颜色").String()
+		dataMap["release_date"] = v.Get("出厂日期").String()
+		dataMap["axle_number"] = v.Get("轴数").String()
+		dataMap["wheel_base"] = v.Get("轴距").String()
+		dataMap["front_wheel_distance"] = v.Get("前轮距").String()
+		dataMap["back_wheel_distance"] = v.Get("后轮距").String()
+		dataMap["gross_mass"] = v.Get("总质量").String()
+		dataMap["unladen_mass"] = v.Get("整备质量").String()
+		dataMap["approved_load"] = v.Get("额定载质量").String()
+		dataMap["rated_power"] = v.Get("功率").String()
+		//dataMap["plate_no"] = v.Get("CPH").String()
+		dataMap["last_compulsory_insurance_date"] = v.Get("承保日期").String()
+		dataMap["engine_no"] = v.Get("发动机号").String()
+		dataMap["oil_wear"] = v.Get("油耗").String()
+		dataMap["long"] = v.Get("长").String()
+		dataMap["wide"] = v.Get("宽").String()
+		dataMap["high"] = v.Get("高").String()
+		dataMap["emission_standard"] = v.Get("排放标准").String()
+		dataMap["tyre_number"] = v.Get("轮胎数").String()
+		// TODO 拆分为前后轮胎规格
+		dataMap["tyre_size"] = v.Get("轮胎规格").String()
+		//dataMap["front_wheel_specification"] = dataMap["tyre_size"]
+		//dataMap["back_wheel_specification"] = dataMap["tyre_size"]
+		dataMap["axle_weight"] = v.Get("轴荷").String()
+		dataMap["style_name"] = v.Get("车型").String()
+
+		ret = append(ret, dataMap)
+	}
+
+	dataMapLen := len(ret)
+	if dataMapLen == 0 {
+		l.Error("func",
+			zap.String("call", "ParasOds2"),
+			zap.String("args", content),
+			zap.String("error", "数据异常,无法解析"))
+		return nil, fmt.Errorf("数据异常")
+	} else {
+		if dataMapLen > 1 {
+			return sortByDate(ret), nil
+		}
+
+		return ret, nil
+	}
+
+}
+
+func HandleOnlineOds2(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds2(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods2 := &model.Ods2{}
+	ods2.PlateNo = dataMapList[0]["plate_no"]
+	ods2.Content = msg.Content
+	err = ods2.Save(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"plate_no": ods2.PlateNo}
+			oldOds2 := &model.Ods2{}
+			err = oldOds2.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds2(oldOds2.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods2.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 132 - 0
impl/analysis/ods3.go

@@ -0,0 +1,132 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+	"strings"
+)
+
+// ZQY vin码查车档119-002
+func ParasOds3(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	//rawCode := data.Get("raw_code").String()
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+
+	requestArray := gjson.Parse(requestParams).Array()
+	if len(requestArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	vin := requestArray[0].Get("vin").String()
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+	state := gjson.Get(responseParams, "ok").String()
+	if state != "1" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	responseArray := gjson.Get(responseParams, "ds").Array()
+	for _, v := range responseArray {
+		dataMap := make(map[string]string)
+		dataMap["vin"] = vin
+		//dataMap["vin"] = v.Get("车辆识别代码").String()
+		dataMap["brand_name"] = v.Get("品牌").String()
+		//dataMap["vehicle_type_detail"] = v.Get("车辆名称").String()
+		dataMap["model_no"] = v.Get("车辆型号").String()
+		dataMap["use_property_detail"] = v.Get("使用性质").String()
+		dataMap["insurance_first_date"] = v.Get("初次登记时间").String()
+		dataMap["fuel_type_detail"] = v.Get("燃料种类").String()
+		dataMap["displacement"] = v.Get("排量").String()
+		dataMap["engine_type"] = v.Get("发动机型号").String()
+		dataMap["approved_number"] = v.Get("额定载客").String()
+		dataMap["vehicle_body_color_detail"] = v.Get("车身颜色").String()
+		dataMap["release_date"] = v.Get("出厂日期").String()
+		dataMap["axle_number"] = v.Get("轴数").String()
+		dataMap["wheel_base"] = v.Get("轴距").String()
+		dataMap["front_wheel_distance"] = v.Get("前轮距").String()
+		dataMap["back_wheel_distance"] = v.Get("后轮距").String()
+		dataMap["gross_mass"] = v.Get("总质量").String()
+		dataMap["unladen_mass"] = v.Get("整备质量").String()
+		dataMap["approved_load"] = v.Get("额定载质量").String()
+		dataMap["rated_power"] = v.Get("功率").String()
+		dataMap["plate_no"] = v.Get("CPH").String()
+		dataMap["last_compulsory_insurance_date"] = v.Get("承保日期").String()
+		dataMap["engine_no"] = v.Get("发动机号").String()
+		dataMap["oil_wear"] = v.Get("油耗").String()
+		dataMap["long"] = v.Get("长").String()
+		dataMap["wide"] = v.Get("宽").String()
+		dataMap["high"] = v.Get("高").String()
+		dataMap["emission_standard"] = v.Get("排放标准").String()
+		dataMap["tyre_number"] = v.Get("轮胎数").String()
+		// TODO 拆分前后轮胎规格
+		dataMap["tyre_size"] = v.Get("轮胎规格").String()
+		//dataMap["front_wheel_specification"] = dataMap["tyre_size"]
+		//dataMap["back_wheel_specification"] = dataMap["tyre_size"]
+		dataMap["axle_weight"] = v.Get("轴荷").String()
+		dataMap["style_name"] = v.Get("车型").String()
+		ret = append(ret, dataMap)
+	}
+
+	if len(ret) == 0 {
+		l.Error("func",
+			zap.String("call", "ParasOds3"),
+			zap.String("args", content),
+			zap.String("error", "数据异常,无法解析"))
+		return nil, fmt.Errorf("数据异常")
+	} else {
+		return ret, nil
+	}
+
+}
+
+func HandleOnlineOds3(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds3(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods3 := &model.Ods3{}
+	ods3.Vin = dataMapList[0]["vin"]
+
+	ods3.Content = msg.Content
+	err = ods3.Save(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods3.Vin}
+			oldOds3 := &model.Ods3{}
+			err = oldOds3.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds3(oldOds3.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods3.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 115 - 0
impl/analysis/ods4.go

@@ -0,0 +1,115 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	dutils "gadm-ods/utils"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+	"strings"
+)
+
+// ZQY 两日期查询119-005
+func ParasOds4(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	//rawCode := data.Get("raw_code").String()
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+
+	requestArray := gjson.Parse(requestParams).Array()
+	if len(requestArray) < 1 {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	code := requestArray[0].Get("code").String()
+	if code == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+	state := gjson.Get(responseParams, "ok").String()
+	if state != "1" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	responseArray := gjson.Get(responseParams, "ds").Array()
+	for _, v := range responseArray {
+		dataMap := make(map[string]string)
+		dataMap["code"] = code
+		if dutils.IsPlate(code) {
+			dataMap["plate_no"] = code
+		} else {
+			dataMap["vin"] = code
+		}
+
+		if dataMap["vin"] == "" {
+			dataMap["vin"] = v.Get("vin").String()
+		}
+		//dataMap["vin"] = v.Get("vin").String()
+		dataMap["insurance_first_date"] = v.Get("register_date").String()
+		dataMap["last_compulsory_insurance_date"] = v.Get("year_date").String()
+		ret = append(ret, dataMap)
+	}
+	dataMapLen := len(ret)
+	// TODO 排序
+	if dataMapLen == 0 {
+		l.Error("func",
+			zap.String("call", "ParasOds3"),
+			zap.String("args", content),
+			zap.String("error", "数据异常,无法解析"))
+		return nil, fmt.Errorf("数据异常")
+	} else {
+		if dataMapLen > 1 {
+			return sortByDate(ret), nil
+		}
+		return ret, nil
+	}
+
+}
+
+func HandleOnlineOds4(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds4(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods4 := &model.Ods4{}
+	ods4.Code = dataMapList[0]["code"]
+	ods4.Content = msg.Content
+	err = ods4.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			// 如果存在判断是否字段相等
+			where := map[string]interface{}{"code": ods4.Code}
+			oldOds4 := &model.Ods4{}
+			err = oldOds4.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds4(oldOds4.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods4.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	for _, dataMap := range dataMapList {
+		delete(dataMap, "code")
+		//dwsMsg := dutils.NewDwsMessage(msg)
+		//dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		//ret = append(ret, dwsMsg)
+	}
+
+	return dataMapList, nil
+}

+ 290 - 0
impl/analysis/ods5.go

@@ -0,0 +1,290 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+	"strings"
+	"time"
+)
+
+// spy 	VIN查询车型信息128-001
+func ParasOds5(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	vin := gjson.Get(requestParams, "vin").String()
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	stateCode := gjson.Get(responseParams, "data.stateCode").String()
+	if stateCode == "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	respData := gjson.Get(responseParams, "data").String()
+	if stateCode == "1" {
+		arr := gjson.Get(respData, "vecStdVOS").Array()
+		for _, v := range arr {
+			dataMap := make(map[string]string)
+			dataMap["vin"] = vin
+			dataMap["approved_load"] = v.Get("approvedLoad").String()
+			dataMap["release_date"] = time.Unix(v.Get("vinTime").Int()/1000, 0).Format("2006-01-02")
+			dataMap["long"] = v.Get("wkc").String()
+			dataMap["oil_wear"] = v.Get("yh").String()
+			dataMap["engine_type"] = v.Get("fdjxh").String()
+			dataMap["high"] = v.Get("wkg").String()
+			dataMap["wide"] = v.Get("wkk").String()
+			dataMap["vehicle_body_color_detail"] = v.Get("csys").String()
+			dataMap["gross_mass"] = v.Get("zzl").String()
+			dataMap["front_wheel_distance"] = v.Get("qlj").String()
+			dataMap["axle_weight"] = v.Get("zh").String()
+			dataMap["wheel_base"] = v.Get("zj").String()
+			dataMap["model_no"] = v.Get("clxh").String()
+			dataMap["axle_number"] = v.Get("zs").String()
+			dataMap["approved_number"] = v.Get("edzk").String()
+			plL := v.Get("plL").String()
+			engineMod := v.Get("engineMod").String()
+			plLMod := ""
+			tmp := strings.Split(engineMod, " ")
+			if len(tmp) > 0 {
+				plLMod = tmp[0]
+			}
+			if plL != "" {
+				tmp := strings.Replace(plL, "L", "", -1)
+				tmp = strings.Replace(tmp, "T", "", -1)
+				dataMap["displacement_l"] = tmp
+				if strings.HasSuffix(plL, "L") {
+					dataMap["air_intak_form"] = "L"
+				} else if strings.HasSuffix(plL, "T") {
+					dataMap["air_intak_form"] = "T"
+				}
+
+			} else if engineMod != "" {
+				if plLMod != "" {
+					pl := strings.Replace(plLMod, "L", "", -1)
+					pl = strings.Replace(pl, "T", "", -1)
+					dataMap["displacement_l"] = pl
+				}
+			}
+			if dataMap["air_intak_form"] == "" {
+				if plLMod != "" {
+					if strings.HasSuffix(plLMod, "L") {
+						dataMap["air_intak_form"] = "L"
+					} else if strings.HasSuffix(plLMod, "T") {
+						dataMap["air_intak_form"] = "T"
+					}
+				}
+			}
+
+			dataMap["engine_no"] = v.Get("fdjh").String()
+
+			// TODO 拆分为前后轮胎规格
+			dataMap["tyre_size"] = v.Get("ltgg").String()
+			//dataMap["front_wheel_specification"] = dataMap["tyre_size"]
+			//dataMap["back_wheel_specification"] = dataMap["tyre_size"]
+			dataMap["fuel_type_detail"] = v.Get("oilWay").String()
+			dataMap["use_property_detail"] = v.Get("syxz").String()
+			dataMap["unladen_mass"] = v.Get("zbzl").String()
+			dataMap["rated_power"] = v.Get("gl").String()
+			dataMap["back_wheel_distance"] = v.Get("hlj").String()
+			dataMap["vehicle_type_detail"] = v.Get("vecModType").String()
+			dataMap["tyre_number"] = v.Get("lts").String()
+			dataMap["traction_mass"] = v.Get("quasiTraction").String()
+			dataMap["emission_standard"] = v.Get("pfbz").String()
+			dataMap["brand_name"] = v.Get("automarkerInfo").String()
+
+			dataMap["price"] = v.Get("autoPrice").String()
+			dataMap["vehicle_structure"] = v.Get("bodyStruct").String()
+			dataMap["model_year"] = v.Get("modelYear").String()
+			//dataMap["axle_number"] = v.Get("wheelSum").String()
+			dataMap["drive_type"] = v.Get("driveWay").String()
+			dataMap["chassis_model"] = v.Get("chassisModel").String()
+			if v.Get("cxid").String() != ""{
+				dataMap["third_style_id"] = v.Get("cxid").String()
+				dataMap["source"] = "1"
+			}else{
+				qczjId := v.Get("qczjId").String()
+				if qczjId != "" {
+					dataMap["third_style_id"] = qczjId
+					dataMap["source"] = "2"
+				}
+			}
+
+			dataMap["series_source"] = "1"
+			/*if v.Get("catarcCode").Exists() {
+				dataMap["third_style_id"] = v.Get("catarcCode").String()
+			} else {
+				dataMap["third_style_id"] = v.Get("cxid").String()
+			}*/
+
+			dataMap["third_series_id"] = v.Get("ccs5").String()
+			dataMap["third_series_name"] = v.Get("vecMod").String()
+			dataMap["third_style_name"] = v.Get("vecName").String()
+			ret = append(ret, dataMap)
+		}
+	} else if stateCode == "2" {
+		arr := gjson.Get(respData, "vinSycVO").Array()
+		for _, v := range arr {
+			dataMap := make(map[string]string)
+			dataMap["vin"] = vin
+			dataMap["approved_load"] = v.Get("ratedLoadMass").String()
+			if v.Get("createTime").Int() != 0 {
+				dataMap["release_date"] = time.Unix(v.Get("createTime").Int()/1000, 0).Format("2006-01-02")
+			} else {
+				dataMap["release_date"] = ""
+			}
+
+			dataMap["long"] = v.Get("length").String()
+			dataMap["oil_wear"] = v.Get("oilLoss").String()
+			dataMap["engine_type"] = v.Get("engineType").String()
+			dataMap["high"] = v.Get("height").String()
+			dataMap["wide"] = v.Get("wide").String()
+			dataMap["gross_mass"] = v.Get("totalQuality").String()
+			dataMap["front_wheel_distance"] = v.Get("frontGauge").String()
+			dataMap["axle_weight"] = v.Get("axleLoad").String()
+			dataMap["wheel_base"] = v.Get("wheelBase").String()
+			dataMap["model_no"] = v.Get("vehicleNumber").String()
+			dataMap["axle_number"] = v.Get("numberOfShaft").String()
+			dataMap["approved_number"] = v.Get("ratedPassenger").String()
+			dataMap["displacement"] = v.Get("displacement").String()
+			dataMap["engine_no"] = v.Get("fdjh").String()
+			dataMap["tyre_size"] = v.Get("tireSize").String()
+			dataMap["fuel_type_detail"] = v.Get("fuelType").String()
+			dataMap["unladen_mass"] = v.Get("curbWeight").String()
+			dataMap["rated_power"] = v.Get("power").String()
+			dataMap["back_wheel_distance"] = v.Get("trackRear").String()
+			dataMap["vehicle_type_detail"] = v.Get("vehicleCategory").String() + v.Get("vehicleName").String()
+			dataMap["tyre_number"] = v.Get("numberOfTires").String()
+			dataMap["emission_standard"] = v.Get("emissionStandard").String()
+			dataMap["brand_name"] = v.Get("brand").String()
+			if v.Get("brand").String() == "" {
+				dataMap["brand_name"] = v.Get("vehicleBrand").String()
+			}
+			dataMap["traction_mass"] = v.Get("totalMassOfQuasiTraction").String()
+			dataMap["cargo_high"] = v.Get("cargoVanHeight").String()
+			dataMap["cargo_long"] = v.Get("cargoVanLong").String()
+			dataMap["cargo_wide"] = v.Get("cargoVanWide").String()
+			dataMap["chassis_model"] = v.Get("chassisModel").String()
+			//dataMap["drive_type"] = v.Get("drivingForm").String()
+			//dataMap["vehicle_type_detail"] = v.Get("vehicleType").String()
+			dataMap["number_of_leaf_springs"] = v.Get("numberOfLeafSprings").String()
+			dataMap["number_of_passengers"] = v.Get("numberOfPassengers").String()
+			ret = append(ret, dataMap)
+		}
+	} else if stateCode == "3" {
+		arr := gjson.Get(respData, "vinMotoVO").Array()
+		for _, v := range arr {
+			dataMap := make(map[string]string)
+			dataMap["vin"] = vin
+			if v.Get("createTime").Int() != 0 {
+				dataMap["release_date"] = time.Unix(v.Get("createTime").Int()/1000, 0).Format("2006-01-02")
+			} else {
+				dataMap["release_date"] = ""
+			}
+			dataMap["brand_name"] = v.Get("brand").String()
+			if v.Get("brand").String() == "" {
+				dataMap["brand_name"] = v.Get("vehicleBrand").String()
+			}
+			dataMap["front_wheel_distance"] = v.Get("frontGauge").String()
+			dataMap["back_wheel_distance"] = v.Get("trackRear").String()
+			dataMap["tyre_number"] = v.Get("numberOfTires").String()
+			dataMap["tyre_size"] = v.Get("tireSize").String()
+			dataMap["number_of_leaf_springs"] = v.Get("numberOfLeafSprings").String()
+			dataMap["wheel_base"] = v.Get("wheelBase").String()
+			dataMap["axle_weight"] = v.Get("axleLoad").String()
+			dataMap["axle_number"] = v.Get("numberOfShaft").String()
+			dataMap["long"] = v.Get("length").String()
+			dataMap["wide"] = v.Get("wide").String()
+			dataMap["high"] = v.Get("height").String()
+			dataMap["cargo_long"] = v.Get("cargoVanLong").String()
+			dataMap["cargo_wide"] = v.Get("cargoVanWide").String()
+			dataMap["cargo_high"] = v.Get("cargoVanHeight").String()
+			dataMap["gross_mass"] = v.Get("totalQuality").String()
+			dataMap["approved_load"] = v.Get("ratedLoadMass").String()
+			dataMap["unladen_mass"] = v.Get("curbWeight").String()
+			dataMap["traction_mass"] = v.Get("totalMassOfQuasiTraction").String()
+			dataMap["number_of_passengers"] = v.Get("numberOfPassengers").String()
+			dataMap["approved_number"] = v.Get("ratedPassenger").String()
+			dataMap["top_speed"] = v.Get("topSpeed").String()
+			dataMap["model_no"] = v.Get("vehicleNumber").String()
+			//vehicleCategory
+			dataMap["vehicle_type_detail"] = v.Get("vehicleName").String()
+			dataMap["emission_standard"] = v.Get("emissionStandard").String()
+			dataMap["displacement"] = v.Get("displacement").String()
+			dataMap["rated_power"] = v.Get("power").String()
+			dataMap["engine_type"] = v.Get("engineType").String()
+			dataMap["fuel_type_detail"] = v.Get("fuelType").String()
+			// enterpriseName
+			dataMap["engine_no"] = v.Get("fdjh").String()
+			dataMap["vehicle_body_color_detail"] = v.Get("bodyColor").String()
+			dataMap["steering_mode"] = v.Get("steeringMode").String()
+			dataMap["is_moto"] = "1"
+			ret = append(ret, dataMap)
+		}
+	}
+
+	if len(ret) == 0 {
+		l.Error("func",
+			zap.String("call", "ParasOds5"),
+			zap.String("args", content),
+			zap.String("error", "数据异常,无法解析"))
+		return nil, fmt.Errorf("数据异常")
+	} else {
+		return ret, nil
+	}
+
+}
+
+func HandleOnlineOds5(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds5(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods5 := &model.Ods5{}
+	ods5.Vin = dataMapList[0]["vin"]
+	ods5.Content = msg.Content
+	err = ods5.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods5.Vin}
+			oldOds5 := &model.Ods5{}
+			err = oldOds5.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds5(oldOds5.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods5.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 108 - 0
impl/analysis/ods6.go

@@ -0,0 +1,108 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/model"
+	"fmt"
+	"strings"
+
+	"gadm-ods/common.in/clinit"
+	"github.com/tidwall/gjson"
+)
+
+// spy 两日期查询128-004,128-006
+func ParasOds6(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	cp := gjson.Get(requestParams, "cp").String()
+	if cp == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	stateCode := gjson.Get(responseParams, "data.stateCode").String()
+	if stateCode == "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	dataArray := gjson.Get(responseParams, "data").Array()
+	if len(dataArray) == 0 {
+		dataMap := make(map[string]string)
+		dataMap["cp"] = cp
+		respData := gjson.Get(responseParams, "data").String()
+		dataMap["plate_no"] = gjson.Get(respData, "cp").String()
+		dataMap["vin"] = gjson.Get(respData, "vin").String()
+		dataMap["last_compulsory_insurance_date"] = gjson.Get(respData, "insuranceMonth").String()
+		dataMap["insurance_first_date"] = gjson.Get(respData, "registerDate").String()
+		dataMap["use_property_detail"] = gjson.Get(respData, "usage").String()
+		ret = append(ret, dataMap)
+	}else{
+		for _,v := range dataArray{
+			dataMap := make(map[string]string)
+			dataMap["cp"] = cp
+			dataMap["plate_no"] = v.Get("cp").String()
+			dataMap["vin"] = v.Get("vin").String()
+			dataMap["last_compulsory_insurance_date"] = v.Get("insuranceMonth").String()
+			dataMap["insurance_first_date"] = v.Get("registerDate").String()
+			dataMap["use_property_detail"] = v.Get("usage").String()
+			dataMap["city"] = v.Get( "city").String()
+			dataMap["province"] = v.Get( "province").String()
+			ret = append(ret, dataMap)
+		}
+	}
+	return ret, nil
+}
+
+func HandleOnlineOds6(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds6(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods6 := &model.Ods6{}
+	ods6.Code = dataMapList[0]["cp"]
+	if ods6.Code == "" {
+		fmt.Println("主键值为空")
+		return nil, nil
+	}
+	ods6.Content = msg.Content
+	err = ods6.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"code": ods6.Code}
+			oldOds6 := &model.Ods6{}
+			err = oldOds6.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds6(oldOds6.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods6.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	for _, dataMap := range dataMapList {
+		delete(dataMap, "cp")
+		//dwsMsg := dutils.NewDwsMessage(msg)
+		//dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		//ret = append(ret, dwsMsg)
+	}
+
+	return dataMapList, nil
+}

+ 110 - 0
impl/analysis/ods7.go

@@ -0,0 +1,110 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/consts"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// spy 过户车记录查询128-010
+func ParasOds7(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	vin := gjson.Get(requestParams, "vin").String()
+
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	lastTransTime := int64(0)
+	lastPlate := ""
+
+	respData := gjson.Get(responseParams, "data").Array()
+	for _, v := range respData {
+		dataMap := make(map[string]string)
+		//record.TransferNumber = v.Get("transTime").Int()
+		//record.TransferAge = v.Get("carYear").String()
+		//record.NewPlateNo = v.Get("newCp").String()
+		transTime := v.Get("transTime").Int()
+		dataMap["vin"] = vin
+		dataMap["plate_no"] = v.Get("oldCp").String()
+		if transTime > lastTransTime {
+			lastPlate = v.Get("newCp").String()
+			lastTransTime = transTime
+		}
+		insuranceDate := v.Get("changeMonth").String()
+		if len(insuranceDate) >= 6 {
+			insuranceDate = insuranceDate[0:4] + "-" + insuranceDate[4:]
+		}
+
+		dataMap["insurance_date"] = insuranceDate
+		dataMap["is_expire"] = consts.EXPIREDATA
+
+		//record.TransferDate = v.Get("changeMonth").String()
+		//record.TransferAgeMonth = v.Get("carMonth").String()
+		ret = append(ret, dataMap)
+	}
+
+	if lastPlate != "" {
+		dataMap := make(map[string]string)
+		dataMap["vin"] = vin
+		dataMap["plate_no"] = lastPlate
+		dataMap["is_expire"] = consts.NORMALDATA
+		ret = append(ret, dataMap)
+	}
+
+	return ret, nil
+}
+
+func HandleOnlineOds7(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds7(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+	// 入本地库
+	ods7 := &model.Ods7{}
+	ods7.Vin = dataMapList[0]["vin"]
+	ods7.Content = msg.Content
+	err = ods7.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods7.Vin}
+			oldOds7 := &model.Ods7{}
+			err = oldOds7.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds7(oldOds7.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods7.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 90 - 0
impl/analysis/ods8.go

@@ -0,0 +1,90 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	dutils "gadm-ods/utils"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// SPY 首保日期查询(128-009)
+func ParasOds8(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	cp := gjson.Get(requestParams, "queryByCp").String()
+	if cp == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["cp"] = cp
+	if dutils.IsPlate(cp) {
+		dataMap["plate_no"] = cp
+	} else {
+		dataMap["vin"] = cp
+	}
+
+	respData := gjson.Get(responseParams, "data").String()
+	dataMap["city"] = gjson.Get(respData, "city").String()
+	dataMap["district"] = gjson.Get(respData, "district").String()
+	dataMap["province"] = gjson.Get(respData, "province").String()
+	dataMap["last_compulsory_insurance_date"] = gjson.Get(respData, "insuranceMonth").String()
+	dataMap["insurance_first_date"] = gjson.Get(respData, "registerDate").String()
+	dataMap["use_property_detail"] = gjson.Get(respData, "usage").String()
+	ret = append(ret, dataMap)
+	return ret, nil
+}
+
+func HandleOnlineOds8(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds8(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods8 := &model.Ods8{}
+	ods8.Cp = dataMapList[0]["cp"]
+	ods8.Content = msg.Content
+	err = ods8.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"cp": ods8.Cp}
+			oldOds8 := &model.Ods8{}
+			err = oldOds8.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds8(oldOds8.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods8.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	for _, dataMap := range dataMapList {
+		delete(dataMap, "cp")
+		//dwsMsg := dutils.NewDwsMessage(msg)
+		//dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		//ret = append(ret, dwsMsg)
+	}
+
+	return dataMapList, nil
+}

+ 82 - 0
impl/analysis/ods9.go

@@ -0,0 +1,82 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package analysis
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/model"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"strings"
+)
+
+// SPY 过户车查询(VIN)(128-007)
+func ParasOds9(content string) (ret []map[string]string, err error) {
+	data := gjson.Parse(content)
+	requestParams := data.Get("request_params").String()
+	responseParams := data.Get("response_params").String()
+	vin := gjson.Get(requestParams, "vin").String()
+	code := gjson.Get(responseParams, "respCode").String()
+	if code != "0" {
+		return nil, fmt.Errorf("数据异常,没有数据")
+	}
+	if vin == "" {
+		return nil, fmt.Errorf("数据异常,车牌姓名不匹配")
+	}
+
+	dataMap := make(map[string]string)
+	dataMap["vin"] = vin
+	respData := gjson.Get(responseParams, "data").String()
+	dataMap["engine_no"] = gjson.Get(respData, "fdjh").String()
+	if vin == "" {
+		dataMap["vin"] = gjson.Get(respData, "vin").String()
+	}
+	//dataMap["insurance_first_date"] = gjson.Get(respData, "registerDate").String()
+	dataMap["insurance_first_date"] = gjson.Get(respData, "registerDate").String()
+	ret = append(ret, dataMap)
+	return ret, nil
+}
+
+func HandleOnlineOds9(msg *apis.OdsMessage) (dataMapList []map[string]string, err error) {
+	dataMapList, err = ParasOds9(msg.Content)
+	if err != nil {
+		// 解析不出来数据直接返回
+		return nil, nil
+	}
+
+	if len(dataMapList) == 0 {
+		return nil, nil
+	}
+
+	// 入本地库
+	ods9 := &model.Ods9{}
+	ods9.Vin = dataMapList[0]["vin"]
+	ods9.Content = msg.Content
+	err = ods9.Insert(clinit.DB())
+	if err != nil {
+		if !strings.Contains(err.Error(), "Duplicate") {
+			return nil, err
+		} else {
+			where := map[string]interface{}{"vin": ods9.Vin}
+			oldOds9 := &model.Ods9{}
+			err = oldOds9.Query(clinit.DB(), where)
+			if err == nil {
+				oldDataMapList, _ := ParasOds9(oldOds9.Content)
+				if checkDataMapListEqual(oldDataMapList, dataMapList) {
+					return nil, nil
+				}
+			}
+			ods9.UpdateWhere(clinit.DB(), where)
+		}
+	}
+
+	/*for _, dataMap := range dataMapList {
+		dwsMsg := dutils.NewDwsMessage(msg)
+		dwsMsg.Content = utils.MarshalJsonString(dataMap)
+		ret = append(ret, dwsMsg)
+	}*/
+
+	return dataMapList, nil
+}

+ 65 - 0
impl/handle/common.go

@@ -0,0 +1,65 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/consts"
+	"gadm-ods/impl/analysis"
+)
+
+// 处理函数的声明
+type Handler func(*apis.OdsMessage) ([]map[string]string, error)
+type ParasHandler func(string) ([]map[string]string, error)
+
+type ProviderLogTask struct {
+	OnlineFunction Handler
+	ParasFunction  ParasHandler
+}
+
+// 声明在线任务
+var OdsOnlineTaskMap = map[string]ProviderLogTask{
+	consts.SOURCEODS1:  {analysis.HandleOnlineOds1, analysis.ParasOds1},
+	consts.SOURCEODS2:  {analysis.HandleOnlineOds2, analysis.ParasOds2},
+	consts.SOURCEODS3:  {analysis.HandleOnlineOds3, analysis.ParasOds3},
+	consts.SOURCEODS4:  {analysis.HandleOnlineOds4, analysis.ParasOds4},
+	consts.SOURCEODS5:  {analysis.HandleOnlineOds5, analysis.ParasOds5},
+	consts.SOURCEODS6:  {analysis.HandleOnlineOds6, analysis.ParasOds6},
+	consts.SOURCEODS7:  {analysis.HandleOnlineOds7, analysis.ParasOds7},
+	consts.SOURCEODS8:  {analysis.HandleOnlineOds8, analysis.ParasOds8},
+	consts.SOURCEODS9:  {analysis.HandleOnlineOds9, analysis.ParasOds9},
+	consts.SOURCEODS10: {analysis.HandleOnlineOds10, analysis.ParasOds10},
+	consts.SOURCEODS11: {analysis.HandleOnlineOds11, analysis.ParasOds11},
+	consts.SOURCEODS12: {analysis.HandleOnlineOds12, analysis.ParasOds12},
+	consts.SOURCEODS13: {analysis.HandleOnlineOds13, analysis.ParasOds13},
+	consts.SOURCEODS15: {analysis.HandleOnlineOds15, analysis.ParasOds15},
+	consts.SOURCEODS16: {analysis.HandleOnlineOds16, analysis.ParasOds16},
+	consts.SOURCEODS17: {analysis.HandleOnlineOds17, analysis.ParasOds12},
+	consts.SOURCEODS18: {analysis.HandleOnlineOds18, analysis.ParasOds18},
+	consts.SOURCEODS19: {analysis.HandleOnlineOds19, analysis.ParasOds19},
+}
+
+// 声明数据源编码到源编码映射
+var ProviderApiCodeToSoucreCodeMap = map[string]string{
+	consts.CXYVIOLATION:             consts.SOURCEODS1,
+	consts.ZQYPLATEVEHICLE:          consts.SOURCEODS2,
+	consts.ZQYVINVEHICLE:            consts.SOURCEODS3,
+	consts.ZQYINSUREDATE:            consts.SOURCEODS4,
+	consts.SPYDEFINEVIN:             consts.SOURCEODS5,
+	consts.SPYVINNEW:                consts.SOURCEODS16,
+	consts.SPYTWODATE:               consts.SOURCEODS6,
+	consts.SPYTWODATE2:              consts.SOURCEODS6,
+	consts.SPYTRANSFERRECORD:        consts.SOURCEODS7,
+	consts.SPYINSUREDATE:            consts.SOURCEODS8,
+	consts.SPYTRANSFERBYVIN:         consts.SOURCEODS9,
+	consts.SPYTWOELEMENTVERIFY:      consts.SOURCEODS10,
+	consts.DRVVEHICLEOWNERVERIFY:    consts.SOURCEODS11,
+	consts.DRVVEHICLEOWNERVERIFYBAK: consts.SOURCEODS11,
+	consts.ZCRKTWOELEMENTVERIFY:     consts.SOURCEODS12,
+	consts.ZCRKTWOELEMENTVERIFY2:    consts.SOURCEODS17,
+	consts.ZJCVEHICLEOWNER:          consts.SOURCEODS13,
+	consts.DYIDCERTVERIFY:           consts.SOURCEODS15,
+	consts.ZCRKENGINENO:             consts.SOURCEODS18,
+	consts.ZCRKBYVIN: consts.SOURCEODS19,
+}

+ 32 - 0
impl/handle/logger.go

@@ -0,0 +1,32 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"fmt"
+	"go.uber.org/zap"
+	"time"
+)
+var l *zap.Logger
+var accessLog *zap.Logger
+func SetLogger(logger *zap.Logger) {
+	l = logger
+}
+
+func SetAccessLogger(logger *zap.Logger){
+	accessLog = logger
+}
+
+func printAccessLog(msgType string,startTime uint64, status string) {
+	// TODO 添加追踪
+	if accessLog != nil {
+		endTime := uint64(time.Now().UnixNano())
+		accessLog.Info("ods",
+			zap.String("msg_type", msgType),
+			zap.String("status", status),
+			zap.String("elapsed", fmt.Sprintf("%fms", float64(endTime-startTime)/1000000)))
+	}
+}
+
+

+ 57 - 0
impl/handle/manual_amendment_data.go

@@ -0,0 +1,57 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/common.in/utils"
+	"gadm-ods/model"
+	dutils "gadm-ods/utils"
+	"encoding/json"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+)
+
+// 处理手动修改
+func HandleManualAmendmentData(msg *apis.OdsMessage) error {
+	// 1 存储到本地
+	// 2 发送
+	if msg.SourceCode == "" && len(msg.TaskList) <=0  {
+		l.Error("func",
+			zap.String("call", "HandleManualAmendmentData"),
+			zap.String("args", msg.SourceCode),
+			zap.String("error", "source code is null"))
+		return nil
+	}
+
+	manual := &model.ManualAmendment{}
+
+	manual.NewContent = gjson.Get(msg.Content, "new_content").String()
+	if manual.NewContent == "" {
+		l.Error("func",
+			zap.String("call", "HandleManualAmendmentData"),
+			zap.String("args", msg.SourceCode),
+			zap.String("error", "new content is null "))
+		return nil
+	}
+	manual.Source = msg.SourceCode
+	manual.OldContent = gjson.Get(msg.Content, "old_content").String()
+	err := manual.Insert(clinit.DB())
+	if err != nil {
+		return err
+	}
+
+	dwsMsg := dutils.NewDwsMessage(msg)
+
+	var dataMapList []map[string]interface{}
+	dataMap := make(map[string]interface{})
+	json.Unmarshal([]byte(manual.NewContent) ,&dataMap)
+	dataMapList = append(dataMapList, dataMap)
+	dwsMsg.Content = utils.MarshalJsonString(dataMapList)
+
+	err = dutils.SendDwsMsg(dwsMsg)
+	
+	return err
+}

+ 326 - 0
impl/handle/offline_data.go

@@ -0,0 +1,326 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/common.in/utils"
+	"gadm-ods/consts"
+	"gadm-ods/errors"
+	"gadm-ods/pb"
+	v1 "gadm-ods/pb/v1"
+	dutils "gadm-ods/utils"
+
+	"github.com/tealeg/xlsx"
+	"go.uber.org/zap"
+	"gorm.io/gorm"
+)
+
+func updateTask(taskId, total, finishCount, isFinish int32, lastId int64) error {
+	req := &v1.UpdateOfflineTaskRequest{Total: total, TaskId: taskId, FinishCount: finishCount, IsFinish: isFinish}
+	_, err := pb.AdmTask.UpdateOfflineTask(context.Background(), req)
+	if err != nil {
+		l.Error("rpc",
+			zap.String("call", "pb.AdmTask.UpdateOfflineTask"),
+			zap.String("args", utils.MarshalJsonString(*req)),
+			zap.String("error", err.Error()))
+		return errors.UpdateTaskError
+	}
+
+	if finishCount != 0 {
+		err = dutils.SetLastId(int64(taskId), lastId)
+		if err != nil {
+			return errors.UpdateTaskError
+		}
+	}
+
+	if isFinish != 0 {
+		dutils.DeleteLastId(int64(taskId))
+	}
+
+	return err
+}
+
+func handleOfflineFromExecl(msg *apis.OdsMessage) (err error) {
+	if len(msg.TaskList) == 0 {
+		l.Error("func",
+			zap.String("call", "HandleOfflineData"),
+			zap.String("args", utils.MarshalJsonString(*msg)),
+			zap.String("error", "excel导入任务必须指定任务"))
+		return nil
+	}
+
+	nameMap := make(map[int]string)
+	downloadedFileName := msg.Content
+	if true {
+		err = utils.OssDownloadFile(msg.Content, downloadedFileName)
+		// 下载文件失败
+		if err != nil {
+			l.Error("func",
+				zap.String("call", "handleOfflineFromExecl.OssDownloadFile"),
+				zap.String("args", msg.Content),
+				zap.String("error", err.Error()))
+			return err
+		}
+	}
+
+	file, err := xlsx.OpenFile(downloadedFileName)
+	if err != nil {
+		l.Error("func",
+			zap.String("call", "handleOfflineFromExecl.OpenFile"),
+			zap.String("args", downloadedFileName),
+			zap.String("error", err.Error()))
+		return err
+	}
+
+	if len(file.Sheets) == 0 {
+		l.Error("func",
+			zap.String("call", "handleOfflineFromExecl.Sheets"),
+			zap.String("args", downloadedFileName),
+			zap.String("error", "not found sheets"))
+		return nil
+	}
+	sheet := file.Sheets[0]
+	if len(sheet.Rows) == 0 {
+		l.Error("func",
+			zap.String("call", "handleOfflineFromExecl.Rows"),
+			zap.String("args", downloadedFileName),
+			zap.String("error", "not found rows"))
+		return nil
+	}
+
+	// 获取最后一次发送id
+	lastId := dutils.GetLastId(msg.OfflineTaskId)
+	if lastId == 0 {
+		// 设置任务总数
+		err = updateTask(int32(msg.OfflineTaskId), int32(len(sheet.Rows)-1), 0, 0, 0)
+		if err != nil {
+			return errors.UpdateTaskError
+		}
+	}
+
+	for index, row := range sheet.Rows {
+		dataMap := make(map[string]interface{})
+		if index == 0 {
+			for cellIndex, cell := range row.Cells {
+				value := strings.TrimSpace(cell.Value)
+				if value != "" {
+					nameMap[cellIndex] = value
+				}
+			}
+			continue
+		}
+
+		if len(nameMap) == 0 {
+			l.Error("func",
+				zap.String("call", "handleOfflineFromExecl.Rows"),
+				zap.String("args", downloadedFileName),
+				zap.String("error", "name map len is 0"))
+			return fmt.Errorf("表头错误")
+		}
+
+		if int64(index) <= lastId {
+			fmt.Println("already send:", index)
+			// 小于最后一次发送,表示已经发送过,继续后面的内容
+			continue
+		}
+
+		for cellIndex, cell := range row.Cells {
+			if v, ok := nameMap[cellIndex]; ok {
+				dataMap[v] = cell.Value
+			}
+		}
+
+		if len(dataMap) > 0 {
+			dwsMsg := dutils.NewDwsMessage(msg)
+			var dataMapList []map[string]interface{}
+			dataMapList = append(dataMapList, dataMap)
+			dwsMsg.Content = utils.MarshalJsonString(dataMapList)
+			err = dutils.SendDwsMsg(dwsMsg)
+			if err != nil {
+				return errors.SenMqError
+			} else {
+				err = updateTask(int32(msg.OfflineTaskId), 0, 1, 0, int64(index))
+				if err != nil {
+					return errors.UpdateTaskError
+				}
+			}
+		}
+	}
+
+	/*err = updateTask(int32(msg.OfflineTaskId), 0, 0, 1, 0)
+	if err != nil {
+		return err
+	}*/
+	// 已处理完返回正常,结束消费
+	return nil
+}
+
+func handleOfflineFromDb(msg *apis.OdsMessage) (err error) {
+	pageSize := consts.DEFAULTPAGESIZE
+	pageNum := 0
+	total := int64(0)
+
+	countSql := strings.Replace(msg.Content, "*", "count(*)", -1)
+	err = clinit.DB().Raw(countSql).Count(&total).Error
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return nil
+		}
+		return err
+	}
+
+	if total == 0 {
+		return nil
+	}
+
+	offset := dutils.GetLastId(msg.OfflineTaskId)
+	if offset == 0 {
+		err = updateTask(int32(msg.OfflineTaskId), int32(total), 0, 0, 0)
+		if err != nil {
+			return errors.UpdateTaskError
+		}
+	}
+
+	task, ok := OdsOnlineTaskMap[msg.SourceCode]
+	isFirst := true
+	id := int64(0)
+	for {
+		// 判断是否结束任务
+		if dutils.IsStop(msg.OfflineTaskId) {
+			err = updateTask(int32(msg.OfflineTaskId), int32(total), 0, 2, 0)
+			if err != nil {
+				return errors.UpdateTaskError
+			}
+			return nil
+		}
+
+		// 循环查询数据
+		var results []map[string]interface{}
+		sql := msg.Content
+		if isFirst {
+			// 第一次判断
+			sql = fmt.Sprintf("%s limit %d offset %d", sql, pageSize, offset)
+			isFirst = false
+		} else {
+			if !strings.Contains(sql, "where") && id > 0 {
+				sql = fmt.Sprintf("%s where id>%d limit %d", sql, id, pageSize)
+			} else {
+				sql = fmt.Sprintf("%s limit %d offset %d", sql, pageSize, offset)
+			}
+		}
+
+		fmt.Println("sql11111111111111111111111111111111111:", sql, offset, id)
+		err = clinit.DB().Raw(sql).Find(&results).Error
+		if err != nil {
+			// 查无返回正常,没有数据处理
+			if err == gorm.ErrRecordNotFound {
+				return nil
+			}
+			return err
+		}
+
+		resultLen := int64(len(results))
+		if resultLen == 0 {
+			return nil
+		}
+
+		offset = offset + resultLen
+
+		for _, v := range results {
+			if v, ok := v["id"]; ok {
+				switch v.(type) {
+				case int:
+					id = int64(v.(int))
+				case int32:
+					id = int64(v.(int32))
+				case int64:
+					id = v.(int64)
+				}
+			}
+
+			dwsMsg := dutils.NewDwsMessage(msg)
+			if ok {
+				dataMapList, err := task.ParasFunction(v["content"].(string))
+				if err != nil {
+					// 更新完成进度数
+					err = updateTask(int32(msg.OfflineTaskId), 0, 1, 0, offset)
+					if err != nil {
+						return errors.UpdateTaskError
+					}
+					continue
+				}
+
+				if len(dataMapList) > 0 {
+					dwsMsg.Content = utils.MarshalJsonString(dataMapList)
+					err = dutils.SendDwsMsg(dwsMsg)
+					if err != nil {
+						return errors.SenMqError
+					}
+				}
+			} else {
+				delete(v, "id")
+				delete(v, "created_at")
+				delete(v, "updated_at")
+				var dataMapList []map[string]interface{}
+				dataMapList = append(dataMapList, v)
+				dwsMsg.Content = utils.MarshalJsonString(dataMapList)
+				err = dutils.SendDwsMsg(dwsMsg)
+				if err != nil {
+					return errors.UpdateTaskError
+				}
+			}
+
+			// 更新完成进度数
+			err = updateTask(int32(msg.OfflineTaskId), 0, 1, 0, offset)
+			if err != nil {
+				return errors.UpdateTaskError
+			}
+		}
+
+		if len(results) < pageSize {
+			return nil
+		}
+		pageNum++
+		time.Sleep(2 * time.Second)
+	}
+
+	return nil
+}
+
+// 处理离线任务
+func HandleOfflineData(msg *apis.OdsMessage) (err error) {
+	defer func() {
+		if err != nil {
+			//if err != errors.UpdateTaskError && err != errors.SenMqError {
+			err = updateTask(int32(msg.OfflineTaskId), 0, 0, 2, 0)
+			//}
+		} else {
+			err = updateTask(int32(msg.OfflineTaskId), 0, 0, 1, 0)
+		}
+	}()
+
+	if msg.OfflineTaskId == 0 {
+		l.Error("func",
+			zap.String("call", "HandleOfflineData"),
+			zap.String("args", utils.MarshalJsonString(*msg)),
+			zap.String("error", "离线任务id为空"))
+		return errors.ArgsError
+	}
+
+	if msg.From == consts.FromDb {
+		err = handleOfflineFromDb(msg)
+	} else if msg.From == consts.FromExcel {
+		// excel 必须指定任务
+		err = handleOfflineFromExecl(msg)
+	}
+
+	return err
+}

+ 70 - 0
impl/handle/online_data.go

@@ -0,0 +1,70 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/utils"
+	dutils "gadm-ods/utils"
+	"fmt"
+	"github.com/tidwall/gjson"
+	"go.uber.org/zap"
+)
+
+// 实时处理数据源日志
+func HandleOnlineProviderLog(msg *apis.OdsMessage) (err error) {
+	fmt.Println(utils.MarshalJsonString(*msg))
+	//thirdpartyLog := gjson.Parse(msg.Content)
+	providerApiCode := msg.SourceCode
+	//providerApiCode := thirdpartyLog.Get("provider_api_code").String()
+	if v, ok := ProviderApiCodeToSoucreCodeMap[providerApiCode]; ok {
+		if task, ok1 := OdsOnlineTaskMap[v]; ok1 {
+			// 赋值源
+			msg.SourceCode = v
+			// 获取数据源时间
+			data := gjson.Parse(msg.Content)
+			msg.Timestamp = data.Get("timestamp").Int()
+			dataMapList, err := task.OnlineFunction(msg)
+			if err == nil {
+
+				if len(dataMapList) > 0 {
+					dwsMsg := dutils.NewDwsMessage(msg)
+					dwsMsg.Content = utils.MarshalJsonString(dataMapList)
+					err = dutils.SendDwsMsg(dwsMsg)
+					if err != nil {
+						return err
+					}
+				}
+				/*
+					dwsMsgListLen := len(dataMapList)
+					if dwsMsgListLen > 0 {
+						for _, dwsMsg := range dwsMsgList {
+
+							err = dutils.SendDwsMsg(dwsMsg)
+							if err != nil {
+								return err
+							}
+						}
+					}*/
+			}
+			return err
+		} else {
+			l.Error("func",
+				zap.String("call", "HandleOnlineProviderLog.ProviderApiCodeToSoucreCodeMap"),
+				zap.String("args", v),
+				zap.String("error", "provider msg no handle"))
+
+		}
+	} else {
+		l.Error("func",
+			zap.String("call", "HandleOnlineProviderLog.OdsOnlineTaskMap"),
+			zap.String("args", providerApiCode),
+			zap.String("error", "provider msg no mapping"))
+	}
+	return nil
+}
+
+func HandleOnlineDipLog(msg *apis.OdsMessage) error {
+	return nil
+}

+ 73 - 0
impl/handle/run.go

@@ -0,0 +1,73 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package handle
+
+import (
+	"gadm-ods/apis"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/consts"
+	"gadm-ods/model"
+	"encoding/json"
+	"fmt"
+	"time"
+
+	"go.uber.org/zap"
+)
+
+// 处理ods数据任务
+func Run(data []byte) (err error) {
+	odsMessage := &apis.OdsMessage{}
+	err = json.Unmarshal(data, odsMessage)
+	if err != nil {
+		l.Error("func",
+			zap.String("call", "Run"),
+			zap.String("args", string(data)),
+			zap.String("error", err.Error()))
+		return nil
+	}
+
+	// 开始时间
+	startTime := uint64(time.Now().UnixNano())
+	// 捕获各个task中的异常并返回给调用者
+	defer func() {
+		status := "SUCCESS"
+		if r := recover(); r != nil {
+			err = fmt.Errorf("%+v", r)
+			l.Error("err",
+				zap.String("run_task", err.Error()),
+				zap.Stack("stacktrace"))
+		}
+
+		if err != nil {
+			//if err != nil && err != errors.UpdateTaskError && err != errors.SenMqError {
+			consumeFail := &model.ConsumeFail{}
+			consumeFail.Content = string(data)
+			err = consumeFail.Insert(clinit.DB())
+			if err != nil {
+				l.Error("mysql",
+					zap.String("sql", "insert into t_adm_ods_consume_fail"),
+					zap.String("fields", string(data)),
+					zap.String("error", err.Error()))
+			}
+			status = "FAIL"
+		}
+
+		printAccessLog(odsMessage.MsgType, startTime, status)
+	}()
+
+	if odsMessage.MsgType == consts.ODSPROVIDERLOG {
+		// 处理三方日志
+		err = HandleOnlineProviderLog(odsMessage)
+	} else if odsMessage.MsgType == consts.ODSDIPLOG {
+		// 处理dip调用日志
+		err = HandleOnlineDipLog(odsMessage)
+	} else if odsMessage.MsgType == consts.ODSOFFLINEIMPORT {
+		// 处理离线任务
+		go HandleOfflineData(odsMessage)
+	} else if odsMessage.MsgType == consts.ODSMANUALAMENDMENT {
+		err = HandleManualAmendmentData(odsMessage)
+	}
+
+	return err
+}

+ 13 - 0
impl/rcvr.go

@@ -0,0 +1,13 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package impl
+
+import (
+	"gadm-ods/impl/handle"
+)
+
+// 处理ods数据任务
+func HandleOdsData(data []byte) (err error) {
+	return handle.Run(data)
+}

+ 0 - 0
log/ads-access.log


+ 0 - 0
log/ads.log


+ 216 - 0
main.go

@@ -0,0 +1,216 @@
+// Copyright 2019 getensh.com. All rights reserved.
+// Use of this source code is governed by getensh.com.
+
+package main
+
+import (
+	"gadm-ods/common.in/cache"
+	"gadm-ods/common.in/mq"
+	"gadm-ods/impl"
+	"gadm-ods/impl/analysis"
+	"gadm-ods/impl/handle"
+	"gadm-ods/model"
+	"gadm-ods/pb"
+	dutils "gadm-ods/utils"
+	"context"
+	"flag"
+	"fmt"
+	"log"
+	"os"
+	"os/signal"
+	"strings"
+	"syscall"
+	"time"
+	//"gadm-ods/common.in/cache"
+	"gadm-ods/common.in/clinit"
+	"gadm-ods/common.in/config"
+	"gadm-ods/common.in/logger"
+	"gadm-ods/common.in/utils"
+
+	_ "github.com/go-sql-driver/mysql"
+	"gopkg.in/ini.v1"
+)
+
+var (
+	// 这里可以改默认值
+	appConfigFile = flag.String("appconfig", "/etc/gadm-ods/app.conf", "app config file location")
+	configFile    = flag.String("config", "/etc/adm/common.json", "config file location")
+	version       = flag.Bool("version", false, "config file location")
+	GitCommit     = "library-import"
+	Version       = "library-import"
+)
+
+func showVersion() {
+	fmt.Println("Version:  ", Version)
+	fmt.Println("GitCommit:", GitCommit)
+}
+
+func prepare(filename string, etcdAddrs []string, discoveryType string) {
+	var conf *config.Configure
+
+	if discoveryType == "k8s" {
+		config.SetConfigFile(filename)
+		conf = config.GetConfigForK8s()
+		if conf == nil {
+			fmt.Printf("get conf failed\n\n")
+			os.Exit(1)
+		}
+		//clinit.EctdHandler(conf)
+	} else {
+		config.SetConfigFile(filename)
+		conf = config.GetConfigForK8s()
+		if conf == nil {
+			fmt.Printf("get conf failed\n\n")
+			os.Exit(1)
+		}
+		clinit.EctdHandler(etcdAddrs)
+
+		// 先行于读配置
+		/*clinit.InitEtcd(etcdAddrs)
+		conf = config.GetConfig(projectName+"/"+runmode, key, clinit.GetEtcdClient())
+		if conf == nil {
+			fmt.Printf("get conf failed\n\n")
+			os.Exit(1)
+		}*/
+		//rpc_apis.Init(etcdAddrs, conf)
+	}
+
+	// 指定mysql数据库,若无则使用默认数据库
+	mysqldb := conf.Rpc.AdmOds.MysqlDb
+	if mysqldb == "" {
+		mysqldb = conf.Mysql.Db
+	}
+	// 连接数据库服务器
+	clinit.InitMysqlGorm(
+		conf.Mysql.User,
+		conf.Mysql.Password,
+		conf.Mysql.Addr,
+		mysqldb,
+		conf.Mysql.Charset,
+		conf.Mysql.MaxIdle,
+		conf.Mysql.MaxConn,
+		conf.RunMode != "prod",
+	)
+	fmt.Println("mysql init finish")
+	// 指定redis数据库,若无则使用默认数据库
+	redisdb := conf.Rpc.AdmOds.RedisDb
+	if redisdb == "" {
+		redisdb = conf.Redis.Db
+	}
+	// 连接redis服务器
+	fmt.Println("redis init start")
+	cache.InitRedis(&cache.RedisConfig{
+		Addrs:        strings.Split(conf.Redis.Addrs, ","),
+		Password:     conf.Redis.Password,
+		DB:           redisdb,
+		PoolSize:     conf.Redis.PoolSize,
+		MinIdleConns: conf.Redis.MinIdleConns,
+		MaxRetries:   conf.Redis.MaxRetries,
+		IsCluster:    conf.Redis.IsCluster,
+	})
+
+	fmt.Println("redis init finish")
+
+	// 建立rpc客户端
+	conns := pb.SetupClients()
+	for _, conn := range conns {
+		defer conn.Close()
+	}
+	fmt.Println("setup client finish")
+	// 初始化logger
+	ms, _ := conf.Log.MaxSize.Int64()
+	mb, _ := conf.Log.MaxBackups.Int64()
+	ma, _ := conf.Log.MaxAge.Int64()
+	maxSize := int(ms)
+	maxBackups := int(mb)
+	maxAge := int(ma)
+
+	disableStacktrace := (conf.Log.Stacktrace == "true")
+	// 通用logger
+	commonLogger := logger.InitLogger(conf.RunMode, fmt.Sprintf("%s/%s.log", conf.Log.Path, conf.Rpc.AdmOds.ServiceName), conf.Rpc.AdmOds.ServiceName, conf.Log.Level,
+		maxSize, maxBackups, maxAge, disableStacktrace)
+	// 单独设置
+	accessLogger := logger.NewInfoLogger(conf.RunMode, fmt.Sprintf("%s/%s-access.log", conf.Log.Path, conf.Rpc.AdmOds.ServiceName), conf.Rpc.AdmOds.ServiceName, conf.Log.Level,
+		maxSize, maxBackups, maxAge)
+
+	// 设置需要使用logger的地方
+	dutils.SetLogger(commonLogger)
+	analysis.SetLogger(commonLogger)
+	handle.SetLogger(commonLogger)
+	handle.SetAccessLogger(accessLogger)
+	model.SetLogger(commonLogger)
+	// access日志
+
+	fmt.Println("init rabbitmq")
+	// 初始化rabbitmq
+
+	consumerCount, _ := conf.OdsRabbitmq.ConsumerCount.Int64()
+	fmt.Println("consumerCount:", consumerCount)
+	if consumerCount == 0 {
+		consumerCount = 2
+	}
+
+	odsMq := mq.InitRabbitmq(
+		conf.OdsRabbitmq.Addr,
+		conf.OdsRabbitmq.Username,
+		conf.OdsRabbitmq.Passwrod,
+		conf.OdsRabbitmq.Vhost,
+		conf.OdsRabbitmq.ExchangeName,
+		conf.OdsRabbitmq.QueueName,
+		conf.OdsRabbitmq.RouteBindKey,
+		impl.HandleOdsData,
+		false,
+		int(consumerCount),
+	)
+	mq.SetOdsMq(odsMq)
+
+	dwsMq := mq.InitRabbitmq(
+		conf.DwsRabbitmq.Addr,
+		conf.DwsRabbitmq.Username,
+		conf.DwsRabbitmq.Passwrod,
+		conf.DwsRabbitmq.Vhost,
+		conf.DwsRabbitmq.ExchangeName,
+		conf.DwsRabbitmq.QueueName,
+		conf.DwsRabbitmq.RouteBindKey,
+		nil,
+		true,
+		0,
+	)
+	mq.SetDwsMq(dwsMq)
+
+	odsMq.StartConsumer()
+}
+
+func start() {
+	// 优雅关闭服务器
+	sigChan := make(chan os.Signal, 1)
+	// 捕获信号
+	signal.Notify(sigChan, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL)
+	sigValue := <-sigChan
+	log.Printf("Got a signal:%v", sigValue)
+	// 不管什么行为,都等待5秒退出
+	log.Println("Start to shutdown server...")
+	_, cancel := context.WithTimeout(context.Background(), 5*time.Second)
+	defer cancel()
+	log.Println("Shutdown server finished.")
+}
+
+func main() {
+	flag.Parse()
+	if *version {
+		showVersion()
+	}
+	cfg, err := ini.Load(*appConfigFile)
+	if err != nil {
+		fmt.Printf("Fail to read file: %v\n\n", err)
+		os.Exit(1)
+	}
+
+	etcdAddrs := strings.Split(cfg.Section("").Key("etcd_addrs").String(), ",")
+	discoveryType := cfg.Section("").Key("discovery_type").String()
+	//utils.SetRunmode(runmode)
+	prepare(*configFile, etcdAddrs, discoveryType)
+	go utils.Free()
+	start()
+	return
+}

+ 148 - 0
model/consume_fail.go

@@ -0,0 +1,148 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package model
+
+import (
+	"time"
+
+	"gadm-ods/common.in/utils"
+	"go.uber.org/zap"
+	"gorm.io/gorm"
+)
+
+type ConsumeFail struct {
+	ID        int64  `gorm:"primary_key" json:"-"`
+	Content   string `content`
+	CreatedAt int64  `json:"-"`
+	UpdatedAt int64  `json:"-"`
+}
+
+func (ConsumeFail) TableName() string {
+	return "t_adm_ods_consume_fail"
+}
+
+// Insert 插入一条记录
+func (p *ConsumeFail) Insert(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.UpdatedAt = timeNow
+	p.CreatedAt = timeNow
+	err := db.Create(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "insert into "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) InsertByMap(db *gorm.DB, data map[string]interface{}) error {
+	data["updated_at"] = time.Now().Unix()
+	err := db.Model(p).Create(data).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "insert into "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(data)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) Delete(db *gorm.DB, filter map[string]interface{}) error {
+	err := db.Where(filter).Delete(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "delete from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) Save(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.UpdatedAt = timeNow
+	p.CreatedAt = timeNow
+	//db.Model().Update()
+	err := db.Save(p).Error
+	if err != nil {
+
+		l.Error("mysql",
+			zap.String("sql", "save "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) Update(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.CreatedAt = 0
+	p.UpdatedAt = timeNow
+	err := db.Model(p).Updates(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) UpdateByMap(db *gorm.DB, data map[string]interface{}) error {
+	data["updated_at"] = time.Now().Unix()
+	err := db.Model(p).Updates(data).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(data)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+// 通过结构体变量更新字段值, gorm库会忽略零值字段。就是字段值等于0, nil, "", false这些值会被忽略掉,不会更新。如果想更新零值,可以使用map类型替代结构体。
+func (p *ConsumeFail) UpdateSome(db *gorm.DB, filed map[string]interface{}) error {
+	/*if filed == nil {
+		return errors.ParamsError
+	}*/
+	timeNow := time.Now().Unix()
+	filed["updated_at"] = timeNow
+	err := db.Model(p).Updates(filed).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filed)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *ConsumeFail) Query(db *gorm.DB, filter map[string]interface{}) error {
+	err := db.Where(filter).Find(p).Error
+	if err != nil {
+
+		l.Error("mysql",
+			zap.String("sql", "select from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+
+	if p.ID == 0 {
+		return gorm.ErrRecordNotFound
+	}
+
+	return err
+}
+
+func (p *ConsumeFail) QueryAll(db *gorm.DB, filter map[string]interface{}, out interface{}) error {
+	err := db.Where(filter).Find(out).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "select from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}

+ 144 - 0
model/fail_msg.go

@@ -0,0 +1,144 @@
+// Copyright 2019 autocareai.com. All rights reserved.
+// Use of this source code is governed by autocareai.com.
+
+package model
+
+import (
+	"gadm-ods/common.in/utils"
+	"go.uber.org/zap"
+	"gorm.io/gorm"
+	"time"
+)
+
+type FailMsg struct {
+	ID        int64  `gorm:"primary_key"`
+	Msg       string `json:"msg"`
+	CreatedAt int64  `json:"created_at"`
+	UpdatedAt int64  `json:"updated_at"`
+}
+
+func (FailMsg) TableName() string {
+	return "t_adm_ods_fail_msg"
+}
+
+// Insert 插入一条记录
+func (p *FailMsg) Insert(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.CreatedAt = timeNow
+	p.UpdatedAt = timeNow
+	err := db.Create(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "insert into "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) InsertByMap(db *gorm.DB, data map[string]interface{}) error {
+	data["updated_at"] = time.Now().Unix()
+	err := db.Model(p).Create(data).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "insert into "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(data)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) Delete(db *gorm.DB, filter map[string]interface{}) error {
+	err := db.Where(filter).Delete(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "delete from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) Save(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.UpdatedAt = timeNow
+	//db.Model().Update()
+	err := db.Save(p).Error
+	if err != nil {
+
+		l.Error("mysql",
+			zap.String("sql", "save "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) Update(db *gorm.DB) error {
+	timeNow := time.Now().Unix()
+	p.CreatedAt = 0
+	p.UpdatedAt = timeNow
+	err := db.Model(p).Updates(p).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(*p)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) UpdateByMap(db *gorm.DB, data map[string]interface{}) error {
+	data["updated_at"] = time.Now().Unix()
+	err := db.Model(p).Updates(data).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(data)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+// 通过结构体变量更新字段值, gorm库会忽略零值字段。就是字段值等于0, nil, "", false这些值会被忽略掉,不会更新。如果想更新零值,可以使用map类型替代结构体。
+func (p *FailMsg) UpdateSome(db *gorm.DB, filed map[string]interface{}) error {
+	/*if filed == nil {
+		return errors.ParamsError
+	}*/
+	timeNow := time.Now().Unix()
+	filed["updated_at"] = timeNow
+	err := db.Model(p).Updates(filed).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "update "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filed)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}
+
+func (p *FailMsg) Query(db *gorm.DB, filter map[string]interface{}) error {
+	err := db.Where(filter).Find(p).Error
+	if err != nil {
+
+		l.Error("mysql",
+			zap.String("sql", "select from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+	if p.ID == 0 {
+		return gorm.ErrRecordNotFound
+	}
+	return err
+}
+
+func (p *FailMsg) QueryAll(db *gorm.DB, filter map[string]interface{}, out interface{}) error {
+	err := db.Where(filter).Find(out).Error
+	if err != nil {
+		l.Error("mysql",
+			zap.String("sql", "select from "+p.TableName()),
+			zap.String("fields", utils.MarshalJsonString(filter)),
+			zap.String("error", err.Error()))
+	}
+	return err
+}

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott