DESKTOP-HN5QP3V\Administrator 7 mesi fa
commit
5f2f17f8f7
64 ha cambiato i file con 4565 aggiunte e 0 eliminazioni
  1. 4 0
      .gitignore
  2. 8 0
      .idea/.gitignore
  3. 9 0
      .idea/dsbqj_admin.iml
  4. 8 0
      .idea/modules.xml
  5. 28 0
      app/api/v1/upload.go
  6. 105 0
      app/api/v1/version.go
  7. 51 0
      app/router/router.go
  8. 6 0
      app/service/common.go
  9. 68 0
      app/service/upload.go
  10. 100 0
      app/service/version.go
  11. 66 0
      app/task/task.go
  12. 13 0
      build.bat
  13. 13 0
      config/.env.local
  14. 82 0
      go.mod
  15. 474 0
      go.sum
  16. 73 0
      main.go
  17. 164 0
      middleware/auth.go
  18. 17 0
      middleware/cors.go
  19. 39 0
      middleware/jwt/jwt.go
  20. 81 0
      middleware/logger/logger.go
  21. 15 0
      middleware/session.go
  22. 21 0
      model/mongo/init.go
  23. 14 0
      model/mongo/version/version.go
  24. 75 0
      model/mysql/init.go
  25. 15 0
      model/mysql/migration.go
  26. 27 0
      model/mysql/report.go
  27. 7 0
      pkg/app/gin.go
  28. 47 0
      pkg/app/request.go
  29. 36 0
      pkg/app/response.go
  30. 18 0
      pkg/cache/keys.go
  31. 59 0
      pkg/cache/main.go
  32. 9 0
      pkg/conf/conf.go
  33. 66 0
      pkg/conf/i18n.go
  34. 11 0
      pkg/conf/locales/zh-cn.yaml
  35. 101 0
      pkg/e/code.go
  36. 75 0
      pkg/e/msg.go
  37. 92 0
      pkg/file/file.go
  38. 67 0
      pkg/hashid/hashid.go
  39. 21 0
      pkg/logger/file.go
  40. 184 0
      pkg/logger/log.go
  41. 207 0
      pkg/oss/main.go
  42. 31 0
      pkg/serializer/common.go
  43. 41 0
      pkg/serializer/version.go
  44. 57 0
      pkg/sms/main.go
  45. 23 0
      pkg/static/enum.go
  46. 75 0
      pkg/util/aes_128.go
  47. 92 0
      pkg/util/authcode.go
  48. 283 0
      pkg/util/common.go
  49. 74 0
      pkg/util/des_ecb.go
  50. 135 0
      pkg/util/gomgr.go
  51. 76 0
      pkg/util/log_linux_amd64.go
  52. 14 0
      pkg/util/log_windows_x86.go
  53. 140 0
      pkg/util/logger.go
  54. 14 0
      pkg/util/md5.go
  55. 312 0
      pkg/util/proto_decode.go
  56. 265 0
      pkg/util/proto_encode.go
  57. 33 0
      pkg/util/redis.go
  58. 72 0
      pkg/util/safe_array.go
  59. 45 0
      pkg/util/safe_map.go
  60. 42 0
      pkg/util/times.go
  61. 95 0
      pkg/util/token.go
  62. 36 0
      pkg/util/verfiy.go
  63. 20 0
      pkg/validator/main.go
  64. 14 0
      test/mongo_test.go

+ 4 - 0
.gitignore

@@ -0,0 +1,4 @@
+log
+log/*
+bin/
+.env

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 9 - 0
.idea/dsbqj_admin.iml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+  <component name="Go" enabled="true" />
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/dsbqj_admin.iml" filepath="$PROJECT_DIR$/.idea/dsbqj_admin.iml" />
+    </modules>
+  </component>
+</project>

+ 28 - 0
app/api/v1/upload.go

@@ -0,0 +1,28 @@
+package v1
+
+import (
+	"dsbqj-admin/app/service"
+	"dsbqj-admin/pkg/app"
+	"dsbqj-admin/pkg/e"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+// UploadToken 上传授权
+// @Summary Upload Token
+// @Tags    用户授权
+// @Produce json
+// @Param filename query string true "上传的文件名"
+// @Success 200 {object} app.Response
+// @Failure 500 {object} app.Response
+// @Router /upload/token [post]
+func UploadReport(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.UploadReportService{}
+	if err := c.ShouldBind(&service); err == nil {
+		res := service.Create()
+		appG.Response(http.StatusOK, e.SUCCESS, res)
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}

+ 105 - 0
app/api/v1/version.go

@@ -0,0 +1,105 @@
+package v1
+
+import (
+	"dsbqj-admin/app/service"
+	"dsbqj-admin/pkg/app"
+	"dsbqj-admin/pkg/e"
+	"dsbqj-admin/pkg/serializer"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+func CheckVersion(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionCheckService{}
+	if err := c.ShouldBind(&service); err == nil {
+		if res, err := service.Check(); err != nil {
+			appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, serializer.BuildVersion(res))
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func ServerVersion(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionServerService{}
+	if err := c.ShouldBind(&service); err == nil {
+		if res, err := service.Servers(); err != nil {
+			appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, serializer.BuildVersions(res))
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+
+}
+
+func ReloadVersion(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionReloadService{}
+	if err := c.ShouldBind(&service); err == nil {
+		if err := service.Reload(); err != nil {
+			appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, nil)
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func ShowVersions(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionShowService{}
+
+	if err := c.ShouldBind(&service); err == nil {
+		if res, err := service.List(); err != nil {
+			appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+		} else {
+			appG.Response(http.StatusOK, e.SUCCESS, res)
+		}
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func VersionCreate(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionCreateService{}
+	if err := c.ShouldBind(&service); err == nil {
+		res := service.Create()
+		appG.Response(http.StatusOK, e.SUCCESS, res)
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func VersionEdit(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionEditService{}
+	if err := c.ShouldBind(&service); err == nil {
+		res := service.Edit()
+		appG.Response(http.StatusOK, e.SUCCESS, res)
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+}
+
+func VersionDelete(c *gin.Context) {
+	var appG = app.Gin{C: c}
+	service := service.VersionDeleteService{}
+	key := appG.ValidKey()
+	service.Version = key
+
+	if err := service.Delete(); err == nil {
+		appG.Response(http.StatusOK, e.SUCCESS, nil)
+
+	} else {
+		appG.Response(http.StatusOK, e.INVALID_PARAMS, err.Error())
+	}
+
+}

+ 51 - 0
app/router/router.go

@@ -0,0 +1,51 @@
+package router
+
+import (
+	"dsbqj-admin/middleware"
+	//"dsbqj-admin/middleware/logger"
+	v1 "dsbqj-admin/app/api/v1"
+	"os"
+
+	"github.com/gin-gonic/gin"
+)
+
+// NewRouter 路由配置
+func NewRouter() *gin.Engine {
+	r := gin.New()
+
+	// 中间件, 顺序不能改
+	r.Use(middleware.Session(os.Getenv("SESSION_SECRET")))
+	r.Use(middleware.Cors())
+	r.Use(gin.Recovery())
+
+	// 路由
+	apiv1 := r.Group("/api/v1")
+	{
+		//apiv1.POST("upload/data", v1.UploadReport)
+
+		apiv1.POST("version/check", v1.CheckVersion)
+		apiv1.POST("version/server", v1.ServerVersion)
+	}
+
+	webv1 := r.Group("/web/v1")
+	{
+		webv1.GET("versions", v1.ShowVersions)
+		webv1.POST("version", v1.VersionCreate)
+		webv1.PUT("version", v1.VersionEdit)
+		webv1.DELETE("version/:key", v1.VersionDelete)
+		webv1.POST("version/reload", v1.ReloadVersion)
+	}
+
+	//webv1.Use(middleware.BodyHandler())
+	//{
+	//
+	//	// 需要登录保护的
+	//	authed := webv1.Group("/")
+	//authed.Use(middleware.AdminRequired())
+	//	{
+	//
+	//	}
+	//
+
+	return r
+}

+ 6 - 0
app/service/common.go

@@ -0,0 +1,6 @@
+package service
+
+type CondPage struct {
+	PageIndex int `form:"pageIndex" json:"pageIndex" binding:"required"`
+	PageSize  int `form:"pageSize" json:"pageSize" binding:"required"`
+}

+ 68 - 0
app/service/upload.go

@@ -0,0 +1,68 @@
+package service
+
+import (
+	"dsbqj-admin/model/mysql"
+	"dsbqj-admin/pkg/cache"
+	"errors"
+	"fmt"
+	"github.com/aliyun/credentials-go/credentials/utils"
+	"github.com/goccy/go-json"
+	"time"
+)
+
+/*
+oss 层级规范
+
+|- 主目录  [models]|[maps]|[musics]|[effects] ....
+	|- 子目录 uuid为目录名
+		|- 子目录 [source] 源数据
+			|- 源数据结构
+		|- 文件1 perviewImage.jpg // 源数据预览图
+		|- 文件2 renderImage.jpg // 源数据渲染图
+		|- 文件
+*/
+
+// UploadTokenService 获得上传oss token的服务
+type UploadReportService struct {
+	Message string `form:"message" json:"message"`
+	Source  string `form:"source" json:"source"`
+	Line    string `form:"line" json:"line"`
+	Colno   string `form:"colno" json:"colno"`
+	Err     string `form:"err" json:"err"`
+	Aid     string `form:"aid" json:"aid"`
+	Sid     string `form:"sid" json:"sid"`
+	Uid     string `form:"uid" json:"uid"`
+	Channel string `form:"channel" json:"channel"`
+	Path    string `form:"path" json:"path"`
+}
+
+func (service *UploadReportService) Create() error {
+	buf, _ := json.Marshal(service)
+	str := utils.GetMD5Base64(buf)
+	key := fmt.Sprintf("%s:%s", "ClientErrReport", service.Aid)
+
+	// 检查数据是否冗余
+	success, err := cache.RedisReport.SetNX(key, str, 60*time.Second).Result()
+	if !success {
+		return errors.New("请勿重复提交")
+	}
+	fmt.Println(str, success)
+	var report = new(mysql.Report)
+	report.Message = service.Message
+	report.Err = service.Err
+	report.Source = service.Source
+	report.Line = service.Line
+	report.Colno = service.Colno
+	report.Aid = service.Aid
+	report.Sid = service.Sid
+	report.Uid = service.Uid
+	report.Channel = service.Channel
+	report.Path = service.Path
+
+	err = report.Create()
+	if err != nil {
+		return err
+	}
+
+	return nil
+}

+ 100 - 0
app/service/version.go

@@ -0,0 +1,100 @@
+package service
+
+import (
+	"context"
+	"dsbqj-admin/app/task"
+	"dsbqj-admin/model/mongo/version"
+	"github.com/kamva/mgm/v3"
+	"go.mongodb.org/mongo-driver/bson"
+)
+
+type VersionReloadService struct {
+}
+
+func (service *VersionReloadService) Reload() error {
+	task.VersionTask.Reload()
+	return nil
+}
+
+type VersionServerService struct {
+}
+
+func (service *VersionServerService) Servers() ([]*version.Version, error) {
+	return task.VersionTask.Servers(), nil
+}
+
+type VersionCheckService struct {
+	Version string `json:"version" binding:"required"`
+}
+
+func (service *VersionCheckService) Check() (*version.Version, error) {
+	return task.VersionTask.Check(service.Version), nil
+}
+
+type VersionShowService struct {
+	CondPage
+}
+
+func (service *VersionShowService) List() ([]version.Version, error) {
+	versions := make([]version.Version, 0)
+	err := mgm.Coll(&version.Version{}).SimpleFind(&versions, bson.M{})
+	return versions, err
+}
+
+type VersionCreateService struct {
+	Version string `json:"version" binding:"required"`
+	Name    string `json:"name" binding:"required"`
+	CDN     string `json:"cdn" binding:"required"`
+	Url     string `json:"url" binding:"required"`
+}
+
+func (service *VersionCreateService) Create() error {
+	version := new(version.Version)
+	version.Name = service.Name
+	version.Version = service.Version
+	version.CDN = service.CDN
+	version.Url = service.Url
+
+	return mgm.Coll(version).Create(version)
+}
+
+type VersionEditService struct {
+	Version string  `json:"version" binding:"required"`
+	Name    *string `json:"name"`
+	CDN     *string `json:"cdn"`
+	Url     *string `json:"url"`
+	Default *bool   `json:"default"`
+}
+
+func (service *VersionEditService) Edit() error {
+	version := new(version.Version)
+
+	filterDoc := bson.M{"version": service.Version}
+	updateDoc := bson.M{}
+	version.Version = service.Version
+	if service.CDN != nil {
+		updateDoc["cdn"] = service.CDN
+	}
+	if service.Url != nil {
+		updateDoc["url"] = service.Url
+	}
+	if service.Default != nil {
+		updateDoc["default"] = service.Default
+	}
+
+	_, err := mgm.Coll(version).UpdateOne(context.Background(), filterDoc, bson.M{"$set": updateDoc})
+
+	return err
+}
+
+type VersionDeleteService struct {
+	Version string `json:"version" binding:"required"`
+}
+
+func (service *VersionDeleteService) Delete() error {
+	version := new(version.Version)
+	filterDoc := bson.M{"version": service.Version}
+
+	_, err := mgm.Coll(version).DeleteOne(context.Background(), filterDoc)
+	return err
+}

+ 66 - 0
app/task/task.go

@@ -0,0 +1,66 @@
+package task
+
+import (
+	"dsbqj-admin/model/mongo/version"
+	"dsbqj-admin/pkg/util"
+	"github.com/kamva/mgm/v3"
+	"go.mongodb.org/mongo-driver/bson"
+	"sync"
+	"time"
+)
+
+var VersionTask *Versions
+
+type Versions struct {
+	sync.Mutex
+	VList util.SafeArray[*version.Version]
+	VMap  util.SafeMap[string, *version.Version]
+}
+
+func Init() *Versions {
+	VersionTask = new(Versions)
+	return VersionTask
+}
+
+func (this *Versions) Run() {
+	// 每5分钟执行一次
+	go func() {
+		for {
+			this.Reload()
+			time.Sleep(5 * time.Minute)
+		}
+	}()
+
+}
+
+func (this *Versions) Reload() {
+	var versions = make([]*version.Version, 0)
+	err := mgm.Coll(&version.Version{}).SimpleFind(&versions, bson.M{})
+	if err == nil {
+		// 重置
+		this.VList.Flush()
+		this.VList.PushMany(versions)
+		this.VMap.Clear()
+		for _, v := range versions {
+			this.VMap.Set(v.Version, v)
+		}
+	}
+}
+
+func (this *Versions) Check(version string) *version.Version {
+	data, ok := this.VMap.Get(version)
+	if !ok { // 遍历列表
+		for _, v := range this.VList.View() {
+			if v.Default {
+				data = v
+				break
+			}
+		}
+	}
+
+	return data
+}
+
+func (this *Versions) Servers() []*version.Version {
+	return this.VList.View()
+}

+ 13 - 0
build.bat

@@ -0,0 +1,13 @@
+SET CGO_ENABLED=0
+SET GOOS=linux
+SET GOARCH=amd64
+echo now the CGO_ENABLED:
+ go env CGO_ENABLED
+
+echo now the GOOS:
+ go env GOOS
+
+echo now the GOARCH:
+ go env GOARCH
+
+go build -o ./bin/admin-server main.go

+ 13 - 0
config/.env.local

@@ -0,0 +1,13 @@
+MYSQL_DSN="root:123456@tcp(127.0.0.1:3306)/dataReport?charset=utf8&parseTime=True&loc=Local"
+MONGO_DSN="mongodb://127.0.0.1:27017/dsbqj-admin?authSource=admin"
+SESSION_SECRET="youneedtoset"
+GIN_MODE="debug"
+RUNTIME_ROOT_PATH="log/"
+LOG_SAVE_PATH="logs/"
+LOG_SAVE_NAME="debug"
+TIME_FORMAT="2006010215"
+LOG_FILE_EXT="log"
+LISTEN_PORT=8765
+REDIS_DB=0
+REDIS_ADDR="127.0.0.1:6379"
+REDIS_PW=""

+ 82 - 0
go.mod

@@ -0,0 +1,82 @@
+module dsbqj-admin
+
+require (
+	github.com/alibabacloud-go/darabonba-openapi v0.2.1
+	github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18
+	github.com/alibabacloud-go/tea v1.3.9
+	github.com/alibabacloud-go/tea-utils v1.4.5
+	github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible
+	github.com/aliyun/credentials-go v1.4.6
+	github.com/astaxie/beego v1.12.3
+	github.com/bytedance/sonic v1.13.2
+	github.com/fatih/color v1.18.0
+	github.com/gin-contrib/cors v1.7.5
+	github.com/gin-contrib/sessions v1.0.3
+	github.com/gin-gonic/gin v1.10.0
+	github.com/go-playground/validator/v10 v10.26.0
+	github.com/go-redis/redis v6.15.9+incompatible
+	github.com/goccy/go-json v0.10.5
+	github.com/jinzhu/gorm v1.9.16
+	github.com/joho/godotenv v1.5.1
+	github.com/kamva/mgm/v3 v3.5.0
+	github.com/speps/go-hashids v2.0.0+incompatible
+	github.com/unknwon/com v1.0.1
+	go.mongodb.org/mongo-driver v1.17.3
+	gopkg.in/yaml.v2 v2.4.0
+)
+
+require (
+	github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
+	github.com/alibabacloud-go/debug v1.0.1 // indirect
+	github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
+	github.com/alibabacloud-go/openapi-util v0.0.11 // indirect
+	github.com/alibabacloud-go/tea-xml v1.1.2 // indirect
+	github.com/bytedance/sonic/loader v0.2.4 // indirect
+	github.com/clbanning/mxj/v2 v2.7.0 // indirect
+	github.com/cloudwego/base64x v0.1.5 // indirect
+	github.com/davecgh/go-spew v1.1.1 // indirect
+	github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+	github.com/gin-contrib/sse v1.0.0 // indirect
+	github.com/go-playground/locales v0.14.1 // indirect
+	github.com/go-playground/universal-translator v0.18.1 // indirect
+	github.com/go-sql-driver/mysql v1.5.0 // indirect
+	github.com/golang/snappy v1.0.0 // indirect
+	github.com/gorilla/context v1.1.2 // indirect
+	github.com/gorilla/securecookie v1.1.2 // indirect
+	github.com/gorilla/sessions v1.4.0 // indirect
+	github.com/jinzhu/inflection v1.0.0 // indirect
+	github.com/json-iterator/go v1.1.12 // indirect
+	github.com/klauspost/compress v1.18.0 // indirect
+	github.com/klauspost/cpuid/v2 v2.2.10 // indirect
+	github.com/leodido/go-urn v1.4.0 // indirect
+	github.com/mattn/go-colorable v0.1.13 // indirect
+	github.com/mattn/go-isatty v0.0.20 // indirect
+	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
+	github.com/modern-go/reflect2 v1.0.2 // indirect
+	github.com/montanaflynn/stats v0.7.1 // indirect
+	github.com/pelletier/go-toml/v2 v2.2.3 // indirect
+	github.com/pmezard/go-difflib v1.0.0 // indirect
+	github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
+	github.com/stretchr/testify v1.10.0 // indirect
+	github.com/tjfoc/gmsm v1.3.2 // indirect
+	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+	github.com/ugorji/go/codec v1.2.12 // indirect
+	github.com/xdg-go/pbkdf2 v1.0.0 // indirect
+	github.com/xdg-go/scram v1.1.2 // indirect
+	github.com/xdg-go/stringprep v1.0.4 // indirect
+	github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
+	golang.org/x/arch v0.16.0 // indirect
+	golang.org/x/crypto v0.37.0 // indirect
+	golang.org/x/net v0.38.0 // indirect
+	golang.org/x/sync v0.13.0 // indirect
+	golang.org/x/sys v0.32.0 // indirect
+	golang.org/x/text v0.24.0 // indirect
+	golang.org/x/time v0.11.0 // indirect
+	google.golang.org/protobuf v1.36.6 // indirect
+	gopkg.in/ini.v1 v1.67.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
+
+replace github.com/ugorji/go/codec => github.com/ugorji/go/codec v1.2.12
+
+go 1.24

+ 474 - 0
go.sum

@@ -0,0 +1,474 @@
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
+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/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
+github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
+github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc=
+github.com/alibabacloud-go/darabonba-openapi v0.2.1 h1:WyzxxKvhdVDlwpAMOHgAiCJ+NXa6g5ZWPFEzaK/ewwY=
+github.com/alibabacloud-go/darabonba-openapi v0.2.1/go.mod h1:zXOqLbpIqq543oioL9IuuZYOQgHQ5B8/n5OPrnko8aY=
+github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
+github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
+github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
+github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
+github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
+github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18 h1:hfZA4cgIl6frNdsRmAyj8sn9J1bihQpYbzIVv2T/+Cs=
+github.com/alibabacloud-go/dysmsapi-20170525/v2 v2.0.18/go.mod h1:di54xjBFHvKiQQo7st3TUmiMy0ywne5TOHup786Rhes=
+github.com/alibabacloud-go/endpoint-util v1.1.0 h1:r/4D3VSw888XGaeNpP994zDUaxdgTSHBbVfZlzf6b5Q=
+github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
+github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA=
+github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
+github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
+github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
+github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
+github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
+github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
+github.com/alibabacloud-go/tea v1.1.19/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A=
+github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
+github.com/alibabacloud-go/tea v1.3.9 h1:bjgt1bvdY780vz/17iWNNtbXl4A77HWntWMeaUF3So0=
+github.com/alibabacloud-go/tea v1.3.9/go.mod h1:A560v/JTQ1n5zklt2BEpurJzZTI8TUT+Psg2drWlxRg=
+github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
+github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
+github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
+github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
+github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M=
+github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
+github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
+github.com/alicebob/miniredis v2.5.0+incompatible/go.mod h1:8HZjEj4yU0dwhYHky+DxYx+6BMjkBbe5ONFIF1MXffk=
+github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible h1:8psS8a+wKfiLt1iVDX79F7Y6wUM49Lcha2FMXt4UM8g=
+github.com/aliyun/aliyun-oss-go-sdk v3.0.2+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
+github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
+github.com/aliyun/credentials-go v1.4.6 h1:CG8rc/nxCNKfXbZWpWDzI9GjF4Tuu3Es14qT8Y0ClOk=
+github.com/aliyun/credentials-go v1.4.6/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
+github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
+github.com/astaxie/beego v1.12.3 h1:SAQkdD2ePye+v8Gn1r4X6IKZM1wd28EyUOVQ3PDSOOQ=
+github.com/astaxie/beego v1.12.3/go.mod h1:p3qIm0Ryx7zeBHLljmd7omloyca1s4yu1a8kM1FkpIA=
+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/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
+github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
+github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
+github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY=
+github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI=
+github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
+github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
+github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
+github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4=
+github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
+github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
+github.com/couchbase/go-couchbase v0.0.0-20200519150804-63f3cdb75e0d/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
+github.com/couchbase/gomemcached v0.0.0-20200526233749-ec430f949808/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/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd h1:83Wprp6ROGeiHFAP8WJdI2RoxALQYgdllERc3N5N2DM=
+github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
+github.com/elastic/go-elasticsearch/v6 v6.8.5/go.mod h1:UwaDJsD3rWLM5rKNFzv9hgox93HoX8utj1kxD9aFUcI=
+github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
+github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
+github.com/gin-contrib/cors v1.7.5 h1:cXC9SmofOrRg0w9PigwGlHG3ztswH6bqq4vJVXnvYMk=
+github.com/gin-contrib/cors v1.7.5/go.mod h1:4q3yi7xBEDDWKapjT2o1V7mScKDDr8k+jZ0fSquGoy0=
+github.com/gin-contrib/sessions v1.0.3 h1:AZ4j0AalLsGqdrKNbbrKcXx9OJZqViirvNGsJTxcQps=
+github.com/gin-contrib/sessions v1.0.3/go.mod h1:5i4XMx4KPtQihnzxEqG9u1K446lO3G19jAi2GtbfsAI=
+github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E=
+github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0=
+github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
+github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
+github.com/glendc/gopher-json v0.0.0-20170414221815-dc4743023d0c/go.mod h1:Gja1A+xZ9BoviGJNA2E9vFkPjjsl+CoJxSXiQM1UXtw=
+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-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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
+github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
+github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
+github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
+github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
+github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
+github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
+github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
+github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
+github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
+github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
+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.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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
+github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
+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.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0=
+github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/context v1.1.2 h1:WRkNAv2uoa03QNIc1A6u4O7DAGMUVoopZhkiXWA2V1o=
+github.com/gorilla/context v1.1.2/go.mod h1:KDPwT9i/MeWHiLl90fuTgrt4/wPcv75vFAZLaOOcbxM=
+github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
+github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
+github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
+github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
+github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
+github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/jinzhu/gorm v1.9.16 h1:+IyIjPEABKRpsu/F8OvDPy9fyQlgsg2luMV2ZIH5i5o=
+github.com/jinzhu/gorm v1.9.16/go.mod h1:G3LB3wezTOWM2ITLzPxEXgSkOXAntiLHS7UdBefADcs=
+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.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
+github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
+github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kamva/mgm/v3 v3.5.0 h1:/2mNshpqwAC9spdzJZ0VR/UZ/SY/PsNTrMjT111KQjM=
+github.com/kamva/mgm/v3 v3.5.0/go.mod h1:F4J1hZnXQMkqL3DZgR7Z7BOuiTqQG/JTic3YzliG4jk=
+github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
+github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
+github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/ledisdb/ledisdb v0.0.0-20200510135210-d35789ec47e6/go.mod h1:n931TsDuKuq+uX4v1fulaMbA/7ZLLhjc85h7chZGBCQ=
+github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
+github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
+github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
+github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mattn/go-sqlite3 v1.14.0/go.mod h1:JIl7NbARA7phWnGvh0LKTyg7S9BA+6gx71ShQilpsus=
+github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
+github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
+github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
+github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
+github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
+github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
+github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/pelletier/go-toml v1.0.1/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
+github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
+github.com/peterh/liner v1.0.1-0.20171122030339-3681c2a91233/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc=
+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/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/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.7.0/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
+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.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
+github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8=
+github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
+github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
+github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
+github.com/siddontang/go v0.0.0-20170517070808-cb568a3e5cc0/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
+github.com/siddontang/goredis v0.0.0-20150324035039-760763f78400/go.mod h1:DDcKzU3qCuvj/tPnimWSsZZzvk9qvkvrIL5naVBPh5s=
+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/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/assertions v1.1.0 h1:MkTeG1DMwsrdH7QtLXy5W+fUxWq+vmb6cLmyJ7aRtF0=
+github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
+github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/speps/go-hashids v2.0.0+incompatible h1:kSfxGfESueJKTx0mpER9Y/1XHl+FVQjtCqRyYcviFbw=
+github.com/speps/go-hashids v2.0.0+incompatible/go.mod h1:P7hqPzMdnZOfyIk+xrlG1QaSMw+gCBdHKsBDnhpaZvc=
+github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
+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/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/syndtr/goleveldb v0.0.0-20160425020131-cfa635847112/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
+github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
+github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM=
+github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
+github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
+github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
+github.com/ugorji/go v0.0.0-20171122102828-84cb69a8af83/go.mod h1:hnLbHMwcvSihnDhEfx2/BzKp2xb0Y+ErdfYcrs9tkJQ=
+github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
+github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
+github.com/unknwon/com v1.0.1 h1:3d1LTxD+Lnf3soQiD4Cp/0BRB+Rsa/+RTvz8GMMzIXs=
+github.com/unknwon/com v1.0.1/go.mod h1:tOOxU81rwgoCLoOVVPHb6T/wt8HZygqH5id+GNnlCXM=
+github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
+github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
+github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
+github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs=
+github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
+github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
+github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM=
+github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
+github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
+github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
+github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yuin/gopher-lua v0.0.0-20171031051903-609c9cd26973/go.mod h1:aEV29XrmTYFr3CiRxZeGHpkvbwq+prZduBqMaascyCU=
+go.mongodb.org/mongo-driver v1.8.3/go.mod h1:0sQWfOeY63QTntERDJJ/0SuKK0T1uVSgKCuAROlKEPY=
+go.mongodb.org/mongo-driver v1.17.3 h1:TQyXhnsWfWtgAhMtOgtYHMTkZIfBTpMTsMnd9ZBeHxQ=
+go.mongodb.org/mongo-driver v1.17.3/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
+golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U=
+golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201216223049-8b5274cf687f/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
+golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
+golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
+golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/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-20181114220301-adae6a3d119a/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-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-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
+golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
+golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
+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-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
+golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
+golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
+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-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-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-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
+golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
+golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
+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/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
+golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
+golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
+golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
+golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
+golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
+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=
+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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
+google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
+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/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/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.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
+gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.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.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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

+ 73 - 0
main.go

@@ -0,0 +1,73 @@
+package main
+
+import (
+	"dsbqj-admin/app/router"
+	"dsbqj-admin/app/task"
+	"dsbqj-admin/model/mongo"
+	"dsbqj-admin/pkg/logger"
+	"dsbqj-admin/pkg/validator"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"github.com/joho/godotenv"
+	"log"
+	"net/http"
+	"os"
+	"time"
+)
+
+// @title           后端接口API文档
+// @version         1.0
+// @description     这里展示所有当前web端API接口信息
+
+// @host      localhost:30101
+// @BasePath  /web/v1
+
+// @securityDefinitions.apikey cxy_token
+// @name __CXY_TOKEN_
+// @in header
+// @securityDefinitions.apikey cxy_uid
+// @name __CXY_UID_
+// @in header
+
+func main() {
+	err := godotenv.Load()
+	if err != nil {
+		log.Fatal("Error loading .env file")
+		return
+	}
+	validator.Init()
+	logger.Init("")
+	//cache.Redis()
+	//oss.Init()
+	//sms.Init()
+
+	// 连接数据库
+	//mysql.Database(os.Getenv("MYSQL_DSN"))
+	mongo.Database(os.Getenv("MONGO_DSN"))
+
+	// 启动任务
+	task.Init().Run()
+
+	if os.Getenv("GIN_MODE") == "release" {
+		gin.SetMode(gin.ReleaseMode)
+	}
+
+	// 装载路由
+	routersInit := router.NewRouter()
+	readTimeout := 60 * time.Second
+	writeTimeout := 200 * time.Second
+	endPoint := fmt.Sprintf(":%s", os.Getenv("LISTEN_PORT"))
+	maxHeaderBytes := 1 << 20
+
+	server := &http.Server{
+		Addr:           endPoint,
+		Handler:        routersInit,
+		ReadTimeout:    readTimeout,
+		WriteTimeout:   writeTimeout,
+		MaxHeaderBytes: maxHeaderBytes,
+	}
+
+	logger.Info("start http version listening %s", endPoint)
+	err = server.ListenAndServe()
+	fmt.Println(err)
+}

+ 164 - 0
middleware/auth.go

@@ -0,0 +1,164 @@
+package middleware
+
+import (
+	"bytes"
+	"dsbqj-admin/pkg/serializer"
+	"dsbqj-admin/pkg/util"
+	"github.com/gin-gonic/gin"
+	"github.com/goccy/go-json"
+	"io/ioutil"
+	"os"
+)
+
+// CurrentUser 获取登录用户
+//func CurrentUser() gin.HandlerFunc {
+//	return func(c *gin.Context) {
+//		session := sessions.Default(c)
+//		uid := session.Get("user_id")
+//		if uid != nil {
+//			user, err := model.GetUser(uid)
+//			if err == nil {
+//				c.Set("user", &user)
+//			}
+//		}
+//		c.Next()
+//	}
+//}
+
+// CurrentAdmin 获取登录用户
+//func CurrentAdmin() gin.HandlerFunc {
+//	return func(c *gin.Context) {
+//		session := sessions.Default(c)
+//		uid := session.Get("admin_id")
+//		if uid != nil {
+//			admin, err := model.GetAdmin(uid)
+//			if err == nil {
+//				c.Set("admin", &admin)
+//			}
+//		}
+//		c.Next()
+//	}
+//}
+
+// AuthRequired 需要登录
+func AuthRequired() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		var loginToken util.Token
+		token := c.GetHeader("Token")
+		info := util.ParseToken(token, os.Getenv("TOKEN_SECRET"))
+		if len(info) == 0 {
+			c.JSON(200, serializer.Response{
+				Code: 403,
+				Msg:  "token解析失败",
+			})
+			c.Abort()
+			return
+		}
+		json.Unmarshal(info, &loginToken)
+
+		if loginToken.Status == "active" || loginToken.Status == "host" {
+			if util.GetNowSecond()-loginToken.ExpiresIn < 24*60*60*1000 {
+				c.Set("user", &loginToken)
+				c.Next()
+				return
+			}
+		} else {
+			c.JSON(200, serializer.Response{
+				Code: 402,
+				Msg:  "需要完善数据",
+			})
+			c.Abort()
+			return
+		}
+
+		c.JSON(200, serializer.Response{
+			Code: 401,
+			Msg:  "需要登录",
+		})
+		c.Abort()
+	}
+}
+
+func AdminRequired() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		var loginToken util.Token
+		token := c.GetHeader("Token")
+		info := util.ParseToken(token, os.Getenv("TOKEN_SECRET"))
+		if len(info) == 0 {
+			c.JSON(200, serializer.Response{
+				Code: 403,
+				Msg:  "token解析失败",
+			})
+			c.Abort()
+			return
+		}
+		json.Unmarshal(info, &loginToken)
+
+		if util.GetNowSecond()-loginToken.ExpiresIn < 24*60*60*1000 {
+			c.Set("user", &loginToken)
+			c.Next()
+			return
+		}
+
+		c.JSON(200, serializer.Response{
+			Code: 401,
+			Msg:  "需要登录",
+		})
+		c.Abort()
+	}
+}
+
+func WebRequired() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		var loginToken util.Token
+		token := c.GetHeader("Token")
+		if token == "DCciDkTCPwFxTYxFzdgxYoOQVXJesKwQccNddefRMmYUfXnXcgQdPaAHcVJWENJHBh" { // 管理平台发送的请求 特殊处理
+			loginToken.Role = "web"
+			c.Set("user", &loginToken)
+			c.Next()
+			return
+		}
+
+		c.JSON(200, serializer.Response{
+			Code: 401,
+			Msg:  "没有请求权限",
+		})
+		c.Abort()
+	}
+}
+
+func BodyHandler() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		payload, err := c.GetRawData()
+		if err != nil {
+			c.JSON(200, serializer.Response{
+				Code: 501,
+				Msg:  "数据读取错误",
+			})
+			c.Abort()
+			return
+		}
+
+		//todo test use it, need delete it after test
+		// payloadstr := util.EncryptDES_ECB(payload, os.Getenv("CRYPRO_SECRET"))
+		// payload = []byte(payloadstr)
+
+		if len(payload) != 0 {
+			res, err := util.DecryptDES_ECB(string(payload), os.Getenv("CRYPRO_SECRET"))
+			if err != nil {
+				c.JSON(200, serializer.Response{
+					Code: 501,
+					Msg:  "数据解析错误",
+				})
+				c.Abort()
+				return
+			}
+			j := map[string]interface{}{}
+			json.Unmarshal([]byte(res), &j)
+			buf, _ := json.Marshal(j)
+			c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(buf))
+		}
+
+		c.Next()
+	}
+}

+ 17 - 0
middleware/cors.go

@@ -0,0 +1,17 @@
+package middleware
+
+import (
+	"github.com/gin-contrib/cors"
+	"github.com/gin-gonic/gin"
+)
+
+// Cors 跨域配置
+func Cors() gin.HandlerFunc {
+	config := cors.DefaultConfig()
+	config.AllowMethods = []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
+	config.AllowHeaders = []string{"Origin", "Content-Length", "Content-Type", "Cookie", "Token", "Authorization"}
+	config.ExposeHeaders = []string{"Origin", "Content-Length", "Content-Type", "Cookie", "Token"}
+	config.AllowOrigins = []string{"*"}
+	config.AllowCredentials = true
+	return cors.New(config)
+}

+ 39 - 0
middleware/jwt/jwt.go

@@ -0,0 +1,39 @@
+package jwt
+
+// JWT is jwt middleware
+//func JWT() gin.HandlerFunc {
+//	return func(c *gin.Context) {
+//		var code int
+//		var data interface{}
+//
+//		code = e.SUCCESS
+//		token := c.GetHeader("Token")
+//		if token == "" {
+//			code = e.INVALID_PARAMS
+//		} else {
+//			claims, err := util.ParseToken(token, os.Getenv("TOKEN_SECRET"))
+//			if err != nil {
+//				switch err.(*jwt.ValidationError).Errors {
+//				case jwt.ValidationErrorExpired:
+//					code = e.ERROR_AUTH_CHECK_TOKEN_TIMEOUT
+//				default:
+//					code = e.ERROR_AUTH_CHECK_TOKEN_FAIL
+//				}
+//			} else {
+//				c.Set("user", claims)
+//			}
+//		}
+//
+//		if code != e.SUCCESS {
+//			c.JSON(http.StatusUnauthorized, gin.H{
+//				"code": code,
+//				"msg":  e.GetMsg(code),
+//				"data": data,
+//			})
+//
+//			c.Abort()
+//			return
+//		}
+//		c.Next()
+//	}
+//}

+ 81 - 0
middleware/logger/logger.go

@@ -0,0 +1,81 @@
+package logger
+
+import (
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"idou-server/pkg/file"
+	"log"
+	"os"
+	"time"
+)
+
+var formatter = func(param gin.LogFormatterParams) string {
+	var statusColor, methodColor, resetColor string
+	if param.IsOutputColor() {
+		statusColor = param.StatusCodeColor()
+		methodColor = param.MethodColor()
+		resetColor = param.ResetColor()
+	}
+
+	if param.Latency > time.Minute {
+		param.Latency = param.Latency.Truncate(time.Second)
+	}
+	return fmt.Sprintf("[GIN] %v |%s %3d %s| %13v | %15s |%s %-7s %s %#v\n%s",
+		param.TimeStamp.Format("2006/01/02 - 15:04:05"),
+		statusColor, param.StatusCode, resetColor,
+		param.Latency,
+		param.ClientIP,
+		methodColor, param.Method, resetColor,
+		param.Path,
+		param.ErrorMessage,
+	)
+}
+
+func LoggerHandler() gin.HandlerFunc {
+	out, err := file.MustOpen("gin-request.log", os.Getenv("LOG_SAVE_PATH"))
+	if err != nil {
+		log.Fatalf("logging.gin err: %v", err)
+	}
+
+	return func(c *gin.Context) {
+		// Start timer
+		start := time.Now()
+		path := c.Request.URL.Path
+		raw := c.Request.URL.RawQuery
+
+		// Process request
+		c.Next()
+
+		if out != nil {
+
+			// Log only when path is not being skipped
+			param := gin.LogFormatterParams{
+				Request: c.Request,
+				Keys:    c.Keys,
+			}
+
+			// Stop timer
+			param.TimeStamp = time.Now()
+			param.Latency = param.TimeStamp.Sub(start)
+
+			param.ClientIP = c.ClientIP()
+			param.Method = c.Request.Method
+			param.StatusCode = c.Writer.Status()
+			param.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
+
+			param.BodySize = c.Writer.Size()
+
+			if raw != "" {
+				path = path + "?" + raw
+			}
+
+			param.Path = path
+
+			fmt.Fprint(out, formatter(param))
+
+			if gin.Mode() == gin.DebugMode {
+				fmt.Fprint(os.Stdout, formatter(param))
+			}
+		}
+	}
+}

+ 15 - 0
middleware/session.go

@@ -0,0 +1,15 @@
+package middleware
+
+import (
+	"github.com/gin-contrib/sessions"
+	"github.com/gin-contrib/sessions/cookie"
+	"github.com/gin-gonic/gin"
+)
+
+// Session 初始化session
+func Session(secret string) gin.HandlerFunc {
+	store := cookie.NewStore([]byte(secret))
+	//Also set Secure: true if using SSL, you should though
+	store.Options(sessions.Options{HttpOnly: true, MaxAge: 7 * 86400, Path: "/"})
+	return sessions.Sessions("gin-session", store)
+}

+ 21 - 0
model/mongo/init.go

@@ -0,0 +1,21 @@
+package mongo
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"github.com/kamva/mgm/v3"
+	"go.mongodb.org/mongo-driver/mongo"
+	"go.mongodb.org/mongo-driver/mongo/options"
+)
+
+var database *mongo.Database
+var client *mongo.Client
+
+func Database(connString string) {
+	logger.Info("mongo connect starting")
+	err := mgm.SetDefaultConfig(nil, "dsbqj_admin", options.Client().ApplyURI(connString))
+	if err != nil {
+		panic(err)
+	}
+	logger.Info("mongo connect success")
+	return
+}

+ 14 - 0
model/mongo/version/version.go

@@ -0,0 +1,14 @@
+package version
+
+import (
+	"github.com/kamva/mgm/v3"
+)
+
+type Version struct {
+	mgm.DefaultModel `bson:",inline" json:"-"`
+	Version          string `bson:"version" json:"version"`
+	Name             string `bson:"name" json:"name"`
+	CDN              string `bson:"cdn" json:"cdn"`
+	Url              string `bson:"url" json:"url"`
+	Default          bool   `bson:"default" json:"default"`
+}

+ 75 - 0
model/mysql/init.go

@@ -0,0 +1,75 @@
+package mysql
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"fmt"
+	"os"
+	"time"
+
+	"github.com/gin-gonic/gin"
+	"github.com/jinzhu/gorm"
+
+	//
+	_ "github.com/jinzhu/gorm/dialects/mysql"
+)
+
+// DB 数据库链接单例
+var DB *gorm.DB
+
+// Database 在中间件中初始化mysql链接
+func Database(connString string) {
+	logger.Info("开始连接mysql....%s", connString)
+	db, err := gorm.Open("mysql", connString)
+	db.LogMode(true)
+	// Error
+	if err != nil {
+		fmt.Println(err)
+		panic(err)
+	}
+	if gin.Mode() == "release" {
+		db.LogMode(false)
+	}
+	//设置连接池
+	//空闲
+	db.DB().SetMaxIdleConns(20)
+	//打开
+	db.DB().SetMaxOpenConns(100)
+	//超时
+	db.DB().SetConnMaxLifetime(time.Second * 30)
+
+	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
+		return os.Getenv("MYSQL_PREFIX") + defaultTableName
+	}
+
+	DB = db
+
+	logger.Info("连接mysql成功...")
+	migration()
+}
+
+func Connect(connString string, prefix string) *gorm.DB {
+	fmt.Println("开始连接mysql....")
+	db, err := gorm.Open("mysql", connString)
+	db.LogMode(true)
+	// Error
+	if err != nil {
+		panic(err)
+	}
+	if gin.Mode() == "release" {
+		db.LogMode(false)
+	}
+	//设置连接池
+	//空闲
+	db.DB().SetMaxIdleConns(20)
+	//打开
+	db.DB().SetMaxOpenConns(100)
+	//超时
+	db.DB().SetConnMaxLifetime(time.Second * 30)
+
+	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
+		return prefix + defaultTableName
+	}
+
+	fmt.Println("连接mysql成功...")
+	return db
+}

+ 15 - 0
model/mysql/migration.go

@@ -0,0 +1,15 @@
+package mysql
+
+import (
+	"dsbqj-admin/pkg/logger"
+)
+
+//执行数据迁移
+
+func migration() {
+	logger.Info("开始自动迁移模式....")
+	// 自动迁移模式
+	DB.Set("gorm:table_options", "charset=utf8mb4").
+		AutoMigrate(&Report{})
+	logger.Info("执行完成....")
+}

+ 27 - 0
model/mysql/report.go

@@ -0,0 +1,27 @@
+package mysql
+
+import "github.com/jinzhu/gorm"
+
+type Report struct {
+	gorm.Model
+
+	Message string `gorm:"type:longtext"`
+	Source  string
+	Line    string
+	Colno   string
+	Err     string `gorm:"type:longtext"`
+	Aid     string
+	Sid     string
+	Uid     string
+	Channel string
+	Path    string
+}
+
+func (report *Report) Create() error {
+	res := DB.Create(report)
+	if res.Error != nil {
+		return res.Error
+	}
+
+	return nil
+}

+ 7 - 0
pkg/app/gin.go

@@ -0,0 +1,7 @@
+package app
+
+import "github.com/gin-gonic/gin"
+
+type Gin struct {
+	C *gin.Context
+}

+ 47 - 0
pkg/app/request.go

@@ -0,0 +1,47 @@
+package app
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"errors"
+	"github.com/astaxie/beego/validation"
+	"github.com/unknwon/com"
+)
+
+// MarkErrors logs error logs
+func MarkErrors(errors []*validation.Error) {
+	for _, err := range errors {
+		logger.Info(err.Key, err.Message)
+	}
+
+	return
+}
+
+func (g *Gin) ValidKey() string {
+	return g.C.Param("key")
+}
+
+func (g *Gin) ValidId() (int, error) {
+	id := com.StrTo(g.C.Param("id")).MustInt()
+	valid := validation.Validation{}
+	valid.Min(id, 1, "id")
+
+	if valid.HasErrors() {
+		MarkErrors(valid.Errors)
+		return id, errors.New("id illegal")
+	}
+
+	return id, nil
+}
+
+func (g *Gin) ValidCode() (int, error) {
+	id := com.StrTo(g.C.Param("code")).MustInt()
+	valid := validation.Validation{}
+	valid.Min(id, 1, "code")
+
+	if valid.HasErrors() {
+		MarkErrors(valid.Errors)
+		return id, errors.New("code illegal")
+	}
+
+	return id, nil
+}

+ 36 - 0
pkg/app/response.go

@@ -0,0 +1,36 @@
+package app
+
+import (
+	"dsbqj-admin/pkg/e"
+	"dsbqj-admin/pkg/util"
+	"os"
+
+	"github.com/goccy/go-json"
+)
+
+type Response struct {
+	Code int         `json:"code"`
+	Msg  string      `json:"msg"`
+	Data interface{} `json:"data"`
+}
+
+func encode(data interface{}) interface{} {
+	if os.Getenv("NAME") == "admin" {
+		buf, _ := json.Marshal(data)
+		return util.EncryptDES_ECB(buf, os.Getenv("CRYPRO_SECRET"))
+	} else {
+		return data
+	}
+}
+
+// Response setting gin.JSON
+func (g *Gin) Response(httpCode, errCode int, data interface{}) {
+
+	g.C.JSON(httpCode, Response{
+		Code: errCode,
+		Msg:  e.GetMsg(errCode),
+		Data: encode(data),
+		// Data: data,
+	})
+	return
+}

+ 18 - 0
pkg/cache/keys.go

@@ -0,0 +1,18 @@
+package cache
+
+import (
+	"fmt"
+	"strconv"
+)
+
+const (
+	// DailyRankKey 每日排行
+	DailyRankKey = "rank:daily"
+)
+
+// AssetsViewKey 视频点击数的key
+// view:assets:1 -> 100
+// view:assets:2 -> 150
+func AssetsViewKey(id uint) string {
+	return fmt.Sprintf("view:assets:%s", strconv.Itoa(int(id)))
+}

+ 59 - 0
pkg/cache/main.go

@@ -0,0 +1,59 @@
+package cache
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"dsbqj-admin/pkg/util"
+	"github.com/go-redis/redis"
+	"os"
+	"strconv"
+	"sync/atomic"
+	"time"
+)
+
+// RedisClient Redis缓存客户端单例
+var RedisReport *redis.Client
+var num int32 = 0
+
+// Redis 在中间件中初始化redis链接
+func Redis() {
+	RedisReportInit()
+
+	startListen()
+}
+
+func startListen() {
+	ListenRedis("redis_report", RedisReport, RedisReportInit)
+}
+
+func RedisReportInit() {
+	db, _ := strconv.ParseUint(os.Getenv("REDIS_DB"), 10, 64)
+	client := redis.NewClient(&redis.Options{
+		Addr:     os.Getenv("REDIS_ADDR"),
+		Password: os.Getenv("REDIS_PW"),
+		DB:       int(db),
+	})
+
+	_, err := client.Ping().Result()
+	if err != nil {
+		if num == 0 {
+			panic("redis connect err")
+		}
+		return
+	}
+	logger.Info("redis 数据库连接成功 第%d次连接", num)
+	RedisReport = client
+}
+
+func ListenRedis(mark string, rd *redis.Client, connect func()) {
+	util.StartMinorGO("redis connect", func() {
+		for {
+			time.Sleep(5 * time.Second)
+			_, err := rd.Ping().Result()
+			if err != nil {
+				num = atomic.AddInt32(&num, 1)
+				logger.Info("%s---> redis client reconnect num: %d", mark, num)
+				connect()
+			}
+		}
+	}, func(isdebug bool) {})
+}

+ 9 - 0
pkg/conf/conf.go

@@ -0,0 +1,9 @@
+package conf
+
+// Init 初始化配置项
+func Init() {
+	// 从本地读取环境变量
+
+	//// 启动定时任务
+	//tasks.CronJob()
+}

+ 66 - 0
pkg/conf/i18n.go

@@ -0,0 +1,66 @@
+package conf
+
+import (
+	"io/ioutil"
+	"strings"
+
+	yaml "gopkg.in/yaml.v2"
+)
+
+// Dictinary 字典
+var Dictinary *map[interface{}]interface{}
+
+// LoadLocales 读取国际化文件
+func LoadLocales(path string) error {
+	//dir, _ := os.Getwd()
+	//fmt.Println(dir + path)
+	data, err := ioutil.ReadFile(path)
+	if err != nil {
+		return err
+	}
+
+	m := make(map[interface{}]interface{})
+	err = yaml.Unmarshal([]byte(data), &m)
+	if err != nil {
+		return err
+	}
+
+	Dictinary = &m
+
+	return nil
+}
+
+// T 翻译
+func T(key string) string {
+	dic := *Dictinary
+	keys := strings.Split(key, ".")
+	for index, path := range keys {
+		// 如果到达了最后一层,寻找目标翻译
+		if len(keys) == (index + 1) {
+			for k, v := range dic {
+				if k, ok := k.(string); ok {
+					if k == path {
+						if value, ok := v.(string); ok {
+							return value
+						}
+					}
+				}
+			}
+			return path
+		}
+		// 如果还有下一层,继续寻找
+		for k, v := range dic {
+			if ks, ok := k.(string); ok {
+				if ks == path {
+					if dic, ok = v.(map[interface{}]interface{}); ok == false {
+						return path
+					}
+				}
+			} else {
+				return ""
+			}
+		}
+	}
+
+	return ""
+}

+ 11 - 0
pkg/conf/locales/zh-cn.yaml

@@ -0,0 +1,11 @@
+Tag:
+  required: "必须存在,而且不能为空"  
+  min: "不够长"
+  max: "太长"
+Field:
+  Name: "名称"
+  Nickname: "用户昵称"
+  AdminName: "用户名"
+  Password: "密码"
+  PasswordConfirm: "密码校验"
+  

+ 101 - 0
pkg/e/code.go

@@ -0,0 +1,101 @@
+package e
+
+const (
+	OK                = 0
+	SUCCESS           = 200
+	NOT_OPEN          = 300
+	INVALID_PARAMS    = 400
+	INVALID_SYSTEM    = 401
+	ERROR             = 500
+	DB_ERROR          = 600
+	NO_RECORD         = 601
+	NO_PERMISSION     = 700
+	CALL_REMOTE_ERROR = 800
+	PONG              = 999
+
+	// 用户登录 注册相关
+	ERROR_USER_LOGIN_FAIL    = 10001
+	ERROR_USER_PHONE_FAIL    = 10002
+	ERROR_USER_CHPWD_FAIL    = 10003
+	ERROR_USER_PWD_FAIL      = 10004
+	ERROR_USER_CREATE_FAIL   = 10005
+	ERROR_USER_NOT_EXIST     = 10006
+	ERROR_USER_FIXED_FAIL    = 10007
+	ERROR_USER_REGISTED      = 10008
+	ERROR_USER_FIXPASSWORD   = 10009
+	ERROR_USER_PHONE_NUM     = 10010
+	ERROR_USER_NAME_ILLEGAL  = 10011
+	ERROR_USER_FRIEND_EXIST  = 10012
+	ERROR_USER_FRIEND_MAX    = 10013
+	ERROR_USER_PHONE_EXIST   = 10014
+	ERROR_USER_ASSETS_MARKED = 10015
+	ERROR_USER_PROJECT_MAX   = 10016
+	ERROR_USER_MISS_IMAGE    = 10017
+	ERROR_VISITOR_MISS_NAME  = 10018
+
+	ERROR_SMS_SEND_FAIL    = 10101
+	ERROR_SMS_CODE_FAIL    = 10102
+	ERROR_SMS_CODE_COOLING = 10103
+
+	ERROR_PROJECT_NOT_EXIST    = 20001
+	ERROR_PROJECT_DEL_FAIL     = 20002
+	ERROR_PROJECT_UPDATE_FAIL  = 20003
+	ERROR_PROJECT_NOT_ACTIVITY = 20004
+	ERROR_PROJECT_TIME_ERROR   = 20005
+	ERROR_PROJECT_NO_PUBLISH   = 20006
+	ERROR_PUBLISH_NO_MAPID     = 20007
+
+	ERROR_CREATE_REPORT_FAIL = 30001
+
+	ERROR_ACTIVITY_NOT_OPEN  = 40001
+	ERROR_ACTIVITY_IS_FINISH = 40002
+	ERROR_ACTIVITY_NO_REWARD = 40003
+	ERROR_ACTIVITY_NOT_EXIST = 40004
+	ERROR_ACTIVITY_DEL_FAIL  = 40005
+
+	ERROR_DRAW_NO_BONUS    = 50001
+	ERROR_DRAW_NO_TIMES    = 50002
+	ERROR_DRAW_FAIL        = 50003
+	ERROR_DRAW_DONE        = 50004
+	ERROR_DRAW_NO_MORE     = 50005
+	ERROR_DRAW_NEED_UPLOAD = 50006
+	ERROR_DRAW_CONFIG      = 50007
+
+	ERROR_AIUNKOWN_CREATE = 60001
+	ERROR_AIUNKOWN_DELETE = 60002
+
+	ERROR_APPEARANCE_NOT_EXIST = 70001
+
+	ERROR_AI_NO_TIMES = 80001
+
+	ERROR_AUTH_CHECK_TOKEN_FAIL    = 120001
+	ERROR_AUTH_CHECK_TOKEN_TIMEOUT = 120002
+	ERROR_AUTH_TOKEN               = 120003
+	ERROR_AUTH                     = 120004
+
+	ERROR_UPLOAD_SAVE_IMAGE_FAIL    = 130001
+	ERROR_UPLOAD_CHECK_IMAGE_FAIL   = 130002
+	ERROR_UPLOAD_CHECK_IMAGE_FORMAT = 130003
+	ERROR_UPLOAD_CHECK_SIZE_OVER    = 130004
+	ERROR_UPLOAD_SIGNURL_FAIL       = 130005
+)
+
+const (
+	USER_ID = 8
+)
+
+var GenderType = map[string]int8{
+	"":       -1,
+	"male":   0,
+	"female": 1,
+}
+
+var GenderStr = map[int8]string{
+	0: "male",
+	1: "female",
+}
+
+var LocalMimeType = map[string]string{
+	"":     "application/octet-stream",
+	".wav": "audio/wav",
+}

+ 75 - 0
pkg/e/msg.go

@@ -0,0 +1,75 @@
+package e
+
+var MsgFlags = map[int]string{
+	OK:                "ok",
+	SUCCESS:           "ok",
+	NOT_OPEN:          "未开启",
+	ERROR:             "fail",
+	PONG:              "pong",
+	INVALID_PARAMS:    "请求参数错误",
+	INVALID_SYSTEM:    "系统环境异常",
+	NO_PERMISSION:     "没有权限",
+	CALL_REMOTE_ERROR: "rpc调用失败",
+	NO_RECORD:         "数据不存在",
+
+	ERROR_USER_LOGIN_FAIL:    "登录失败:用户名或密码错误",
+	ERROR_USER_PHONE_FAIL:    "用户暂未注册 请前往注册",
+	ERROR_USER_CHPWD_FAIL:    "修改密码失败",
+	ERROR_USER_PWD_FAIL:      "账号或密码错误",
+	ERROR_USER_CREATE_FAIL:   "用户创建失败",
+	ERROR_USER_NOT_EXIST:     "用户不存在",
+	ERROR_USER_FIXED_FAIL:    "完善信息失败",
+	ERROR_USER_REGISTED:      "用户已注册 请登录",
+	ERROR_USER_FIXPASSWORD:   "非游客用户 请前往忘记密码修改",
+	ERROR_USER_PHONE_NUM:     "手机号码异常",
+	ERROR_USER_NAME_ILLEGAL:  "用户名不规范",
+	ERROR_USER_FRIEND_EXIST:  "名片已存在",
+	ERROR_USER_FRIEND_MAX:    "名片已达上限",
+	ERROR_USER_PHONE_EXIST:   "手机号码已注册",
+	ERROR_USER_ASSETS_MARKED: "已完成评价",
+	ERROR_USER_PROJECT_MAX:   "无法创建更多项目",
+	ERROR_USER_MISS_IMAGE:    "用户缺少形象",
+	ERROR_VISITOR_MISS_NAME:  "游客缺少名称",
+
+	ERROR_PROJECT_NOT_EXIST:  "查询项目不存在",
+	ERROR_PROJECT_NO_PUBLISH: "当前项目未发布",
+	ERROR_PUBLISH_NO_MAPID:   "项目发布缺少场景ID",
+
+	ERROR_SMS_SEND_FAIL:    "验证码发送失败",
+	ERROR_SMS_CODE_FAIL:    "验证码不匹配",
+	ERROR_SMS_CODE_COOLING: "验证码冷却中 稍后再试",
+
+	ERROR_ACTIVITY_NO_REWARD: "活动奖励已发完",
+
+	ERROR_DRAW_NO_BONUS:    "未抽取到奖品 再接再厉",
+	ERROR_DRAW_NO_TIMES:    "抽奖次数已用完",
+	ERROR_DRAW_FAIL:        "抽奖操作失败",
+	ERROR_DRAW_DONE:        "已完成抽奖",
+	ERROR_DRAW_NO_MORE:     "本轮抽奖已结束 没有更多奖励了",
+	ERROR_DRAW_NEED_UPLOAD: "需要完成上传",
+	ERROR_DRAW_CONFIG:      "抽奖参数配置错误",
+
+	ERROR_AIUNKOWN_CREATE: "问题创建失败",
+	ERROR_AIUNKOWN_DELETE: "问题删除失败",
+
+	ERROR_AI_NO_TIMES: "ai问答无多余次数",
+
+	ERROR_AUTH_CHECK_TOKEN_FAIL:     "Token鉴权失败",
+	ERROR_AUTH_CHECK_TOKEN_TIMEOUT:  "Token已超时",
+	ERROR_AUTH_TOKEN:                "Token生成失败",
+	ERROR_AUTH:                      "Token错误",
+	ERROR_UPLOAD_SAVE_IMAGE_FAIL:    "保存图片失败",
+	ERROR_UPLOAD_CHECK_IMAGE_FAIL:   "检查图片失败",
+	ERROR_UPLOAD_CHECK_IMAGE_FORMAT: "校验图片错误,图片格式或大小有问题",
+	ERROR_UPLOAD_CHECK_SIZE_OVER:    "使用空间超出,请升级用户等级",
+}
+
+// GetMsg get error information based on Code
+func GetMsg(code int) string {
+	msg, ok := MsgFlags[code]
+	if ok {
+		return msg
+	}
+
+	return MsgFlags[ERROR]
+}

+ 92 - 0
pkg/file/file.go

@@ -0,0 +1,92 @@
+package file
+
+import (
+	"fmt"
+	"io/ioutil"
+	"mime/multipart"
+	"os"
+	"path"
+)
+
+// GetSize get the file size
+func GetSize(f multipart.File) (int, error) {
+	content, err := ioutil.ReadAll(f)
+
+	return len(content), err
+}
+
+// GetExt get the file ext
+func GetExt(fileName string) string {
+	return path.Ext(fileName)
+}
+
+// CheckNotExist check if the file exists
+func CheckNotExist(src string) bool {
+	_, err := os.Stat(src)
+
+	return os.IsNotExist(err)
+}
+
+// CheckPermission check if the file has permission
+func CheckPermission(src string) bool {
+	_, err := os.Stat(src)
+
+	return os.IsPermission(err)
+}
+
+// IsNotExistMkDir create a directory if it does not exist
+func IsNotExistMkDir(src string) error {
+	if notExist := CheckNotExist(src); notExist == true {
+		if err := MkDir(src); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// MkDir create a directory
+func MkDir(src string) error {
+	err := os.MkdirAll(src, os.ModePerm)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Open a file according to a specific mode
+func Open(name string, flag int, perm os.FileMode) (*os.File, error) {
+	f, err := os.OpenFile(name, flag, perm)
+	if err != nil {
+		return nil, err
+	}
+
+	return f, nil
+}
+
+// MustOpen maximize trying to open the file
+func MustOpen(fileName, filePath string) (*os.File, error) {
+	dir, err := os.Getwd()
+	if err != nil {
+		return nil, fmt.Errorf("os.Getwd err: %v", err)
+	}
+
+	src := dir + "/" + filePath
+	perm := CheckPermission(src)
+	if perm == true {
+		return nil, fmt.Errorf("file.CheckPermission Permission denied src: %s", src)
+	}
+
+	err = IsNotExistMkDir(src)
+	if err != nil {
+		return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err)
+	}
+	
+	f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0644)
+	if err != nil {
+		return nil, fmt.Errorf("Fail to OpenFile :%v", err)
+	}
+
+	return f, nil
+}

+ 67 - 0
pkg/hashid/hashid.go

@@ -0,0 +1,67 @@
+package hashid
+
+import (
+	"errors"
+	"os"
+
+	"github.com/speps/go-hashids"
+)
+
+// ID类型
+const (
+	UserID = iota // 用户
+	ProjectID
+	RenderTaskID
+	CustomID
+)
+
+var (
+	// ErrTypeNotMatch ID类型不匹配
+	ErrTypeNotMatch = errors.New("ID类型不匹配")
+)
+
+// HashEncode 对给定数据计算HashID
+func HashEncode(v []int) (string, error) {
+	hd := hashids.NewData()
+	hd.Salt = os.Getenv("HASH_ID_SALT") //conf.SystemConfig.HashIDSalt
+
+	h, err := hashids.NewWithData(hd)
+	if err != nil {
+		return "", err
+	}
+
+	id, err := h.Encode(v)
+	if err != nil {
+		return "", err
+	}
+	return id, nil
+}
+
+// HashDecode 对给定数据计算原始数据
+func HashDecode(raw string) ([]int, error) {
+	hd := hashids.NewData()
+	hd.Salt = os.Getenv("HASH_ID_SALT") // conf.SystemConfig.HashIDSalt
+
+	h, err := hashids.NewWithData(hd)
+	if err != nil {
+		return []int{}, err
+	}
+
+	return h.DecodeWithError(raw)
+
+}
+
+// HashID 计算数据库内主键对应的HashID
+func HashID(id uint, t int) string {
+	v, _ := HashEncode([]int{int(id), t})
+	return v
+}
+
+// DecodeHashID 计算HashID对应的数据库ID
+func DecodeHashID(id string, t int) (uint, error) {
+	v, _ := HashDecode(id)
+	if len(v) != 2 || v[1] != t {
+		return 0, ErrTypeNotMatch
+	}
+	return uint(v[0]), nil
+}

+ 21 - 0
pkg/logger/file.go

@@ -0,0 +1,21 @@
+package logger
+
+import (
+	"fmt"
+	"os"
+	"time"
+)
+
+// getLogFilePath get the log file save path
+func getLogFilePath() string {
+	return fmt.Sprintf("%s%s", os.Getenv("RUNTIME_ROOT_PATH"), os.Getenv("LOG_SAVE_PATH"))
+}
+
+// getLogFileName get the save name of the log file
+func getLogFileName() string {
+	return fmt.Sprintf("%s%s.%s",
+		os.Getenv("LOG_SAVE_NAME"),
+		time.Now().Format(os.Getenv("TIME_FORMAT")),
+		os.Getenv("LOG_FILE_EXT"),
+	)
+}

+ 184 - 0
pkg/logger/log.go

@@ -0,0 +1,184 @@
+package logger
+
+import (
+	"dsbqj-admin/pkg/file"
+	"fmt"
+	"github.com/fatih/color"
+	"log"
+	"os"
+	"path/filepath"
+	"runtime"
+	"time"
+)
+
+type Level int
+
+var (
+	F  *os.File
+	F2 *os.File
+
+	DefaultPrefix      = ""
+	DefaultCallerDepth = 2
+
+	logger     *log.Logger
+	debug      *log.Logger
+	logPrefix  = ""
+	levelFlags = []string{"DEBUG", "INFO", "WARN", "ERROR", "FATAL"}
+
+	logMod = ""
+)
+
+var colors = map[string]func(a ...interface{}) string{
+	"Warning": color.New(color.FgYellow).Add(color.Bold).SprintFunc(),
+	"Panic":   color.New(color.BgRed).Add(color.Bold).SprintFunc(),
+	"Error":   color.New(color.FgRed).Add(color.Bold).SprintFunc(),
+	"Info":    color.New(color.FgCyan).Add(color.Bold).SprintFunc(),
+	"Debug":   color.New(color.FgWhite).Add(color.Bold).SprintFunc(),
+}
+
+var spaces = map[string]string{
+	"Warning": "",
+	"Panic":   "  ",
+	"Error":   "  ",
+	"Info":    "   ",
+	"Debug":   "  ",
+}
+
+const (
+	DEBUG Level = iota
+	INFO
+	WARNING
+	ERROR
+	FATAL
+)
+
+// Setup initialize the log instance
+func Init(tag string) {
+	var err error
+	filePath := getLogFilePath()
+	fileName := tag + getLogFileName()
+	logMod = os.Getenv("GIN_MODE")
+	F, err = file.MustOpen(fileName, filePath)
+	if err != nil {
+		log.Fatalf("logging.Setup err: %v", err)
+	}
+
+	logger = log.New(F, DefaultPrefix, log.LstdFlags)
+	F2, err = file.MustOpen("debug", filePath)
+	if err != nil {
+		log.Fatalf("logging.Setup err: %v", err)
+	}
+
+	debug = log.New(F2, DefaultPrefix, log.LstdFlags)
+	// 这里启动一个携程去定时检测当前时间 是否是同一天
+	go func() {
+		for {
+			last := time.Now().Format(os.Getenv("TIME_FORMAT"))
+			time.Sleep(1 * time.Second)
+			now := time.Now().Format(os.Getenv("TIME_FORMAT"))
+
+			if last != now {
+				filePath := getLogFilePath()
+				fileName := tag + getLogFileName()
+				F, err = file.MustOpen(fileName, filePath)
+				if err != nil {
+					log.Fatalf("logging.Setup err: %v", err)
+				}
+				logger = log.New(F, DefaultPrefix, log.LstdFlags)
+			}
+		}
+	}()
+}
+
+//func checkDate() {
+//	curDate := time.Now().Format(os.Getenv("TIME_FORMAT"))
+//	if lastDate != curDate {
+//		Init()
+//	}
+//}
+
+func File(fileName, msg string) {
+	filePath := getLogFilePath()
+	logMod = os.Getenv("GIN_MODE")
+	f, err := file.MustOpen(fileName, filePath)
+	if err != nil {
+		log.Fatalf("logging.Setup err: %v", err)
+	}
+
+	logger := log.New(f, DefaultPrefix, log.LstdFlags)
+
+	logger.Println(msg)
+}
+
+// Debug output logs at debug level
+func Debug(format string, v ...interface{}) {
+	setPrefix(DEBUG)
+	msg := fmt.Sprintf(format, v...)
+	if logMod == "debug" {
+		Println("Debug", msg)
+
+	}
+	debug.Println(msg)
+}
+
+// Info output logs at info level
+func Info(format string, v ...interface{}) {
+	setPrefix(INFO)
+	msg := fmt.Sprintf(format, v...)
+	Println("Info", msg)
+	logger.Println(msg)
+}
+
+// Warn output logs at warn level
+func Warn(format string, v ...interface{}) {
+	setPrefix(WARNING)
+	msg := fmt.Sprintf(format, v...)
+	if logMod == "debug" {
+		Println("Warning", msg)
+	}
+	logger.Println(msg)
+}
+
+// Error output logs at error level
+func Error(format string, v ...interface{}) {
+	setPrefix(ERROR)
+	msg := fmt.Sprintf(format, v...)
+	if logMod == "debug" {
+		Println("Error", msg)
+	}
+	logger.Println(msg)
+}
+
+// Fatal output logs at fatal level
+func Fatal(format string, v ...interface{}) {
+	setPrefix(FATAL)
+	logger.Fatalln(v)
+}
+
+func Println(prefix string, msg string) {
+	// TODO Release时去掉
+
+	c := color.New()
+
+	_, _ = c.Printf(
+		"%s%s %s %s\n",
+		colors[prefix]("["+prefix+"]"),
+		spaces[prefix],
+		time.Now().Format("2006-01-02 15:04:05"),
+		msg,
+	)
+}
+
+// setPrefix set the prefix of the log output
+func setPrefix(level Level) {
+	_, file, line, ok := runtime.Caller(DefaultCallerDepth)
+	if ok {
+		logPrefix = fmt.Sprintf("[%s][%s:%d]", levelFlags[level], filepath.Base(file), line)
+	} else {
+		logPrefix = fmt.Sprintf("[%s]", levelFlags[level])
+	}
+
+	logger.SetPrefix(logPrefix)
+
+	//checkDate()
+}

+ 207 - 0
pkg/oss/main.go

@@ -0,0 +1,207 @@
+package oss
+
+import (
+	"bytes"
+	"io"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/aliyun/aliyun-oss-go-sdk/oss"
+)
+
+var bucket *oss.Bucket
+
+var ProjectRoot = "webserver"
+
+// InitOSSClient 初始化OSS鉴权客户端
+func Init() error {
+	client, err := oss.New(os.Getenv("OSS_END_POINT"), os.Getenv("OSS_ACCESS_KEY_ID"), os.Getenv("OSS_ACCESS_KEY_SECRET"))
+	if err != nil {
+		panic("创建oss.Client失败")
+	}
+
+	// 获取存储空间。
+	obucket, err := client.Bucket(os.Getenv("OSS_BUCKET"))
+	if err != nil {
+		panic("创建oss.Bucket失败")
+	}
+	bucket = obucket
+
+	if os.Getenv("ENV_VALUE") == "test" {
+		ProjectRoot = "testserver"
+	}
+
+	if os.Getenv("ENV_VALUE") == "dev" {
+		ProjectRoot = "devserver"
+	}
+
+	return nil
+}
+
+func GetBucket() *oss.Bucket {
+	return bucket
+}
+
+// Put 将文件流保存到指定目录
+func Put(file io.Reader, dst string) error {
+	// 凭证有效期
+	credentialTTL := 3600
+
+	// 是否允许覆盖
+	overwrite := true
+
+	options := []oss.Option{
+		oss.Expires(time.Now().Add(time.Duration(credentialTTL) * time.Second)),
+		oss.ForbidOverWrite(!overwrite),
+	}
+	// 上传文件
+	err := bucket.PutObject(dst, file, options...)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// Get 读取文件
+func Get(dst string) (*bytes.Buffer, error) {
+	// 下载文件到缓存。
+	body, err := bucket.GetObject(dst)
+	if err != nil {
+		return nil, err
+	}
+	defer body.Close()
+
+	buf := new(bytes.Buffer)
+	io.Copy(buf, body)
+	return buf, nil
+}
+
+func Delete(dst []string, options ...oss.Option) (oss.DeleteObjectsResult, error) {
+	// 将oss.DeleteObjectsQuiet设置为true,表示不返回删除结果。
+	res, err := bucket.DeleteObjects(dst, options...)
+	if err != nil {
+		return res, err
+	}
+	return res, nil
+}
+
+func DeleteBatch(path string) error {
+	marker := oss.Marker("")
+	// 如果您需要删除所有前缀为src的文件,则prefix设置为src。设置为src后,所有前缀为src的非目录文件、src目录以及目录下的所有文件均会被删除。
+	//prefix := oss.Prefix("src")
+	// 如果您仅需要删除src目录及目录下的所有文件,则prefix设置为src/。
+	dst := path
+	if path[len(path)-1] != '/' {
+		dst = dst + "/"
+	}
+	prefix := oss.Prefix(dst)
+	lor, err := bucket.ListObjects(marker, prefix)
+	if err != nil {
+		return err
+	}
+
+	objects := []string{}
+	for _, object := range lor.Objects {
+		objects = append(objects, object.Key)
+	}
+	// 将oss.DeleteObjectsQuiet设置为true,表示不返回删除结果。
+	_, err = Delete(objects, oss.DeleteObjectsQuiet(true))
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+func SignVideoImageURL(path string, style string) string {
+	path = strings.Replace(path, "http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/", "", 1)
+	options := []oss.Option{}
+	if style != "" {
+		options = append(options, oss.Process(style))
+	}
+	signedGetURL, _ := bucket.SignURL(path, oss.HTTPGet, 600, options...)
+	return signedGetURL
+}
+
+func SignVideoImageURLExpire(path string, style string, expire int64) string {
+	path = strings.Replace(path, "http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/", "", 1)
+	options := []oss.Option{}
+	if style != "" {
+		options = append(options, oss.Process(style))
+	}
+	signedGetURL, _ := bucket.SignURL(path, oss.HTTPGet, expire, options...)
+	return signedGetURL
+}
+
+// AssetsPreviewImageURL 资产缩略图地址
+func SignSourceUrl(path string) string {
+	return path
+	// 移除 http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/ 前缀
+	path = strings.Replace(path, "http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/", "", 1)
+	signedGetURL, _ := bucket.SignURL(path, oss.HTTPGet, 600)
+	return signedGetURL
+}
+
+// SignSourceUrlBatch 资产效果图地址
+func SignSourceUrlBatch(path string) []string {
+	if path == "" {
+		return []string{}
+	}
+	pathList := strings.Split(path, ",")
+	return pathList
+	signedGetURL := make([]string, 0, len(pathList))
+	if !(len(pathList) == 0 || pathList[0] == "") {
+		for _, v := range pathList {
+			// 移除 http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/ 前缀
+			v = strings.Replace(v, "http://idouwebmodels.oss-cn-hangzhou.aliyuncs.com/", "", 1)
+			url, _ := bucket.SignURL(v, oss.HTTPGet, 600)
+			signedGetURL = append(signedGetURL, url)
+		}
+	}
+
+	return signedGetURL
+}
+
+func GetFolderSize(folderPath string) (int64, error) {
+	dst := folderPath
+	if folderPath[len(folderPath)-1] != '/' {
+		dst = dst + "/"
+	}
+
+	var totalSize int64
+	// 获取指定目录下的所有文件
+	marker := oss.Marker("")
+
+	for {
+		listObjectsResult, err := bucket.ListObjects(marker, oss.Prefix(dst))
+		if err != nil {
+			return 0, err
+		}
+
+		// 累加文件大小
+		for _, object := range listObjectsResult.Objects {
+			totalSize += object.Size
+		}
+
+		// 遍历子目录
+		for _, commonPrefix := range listObjectsResult.CommonPrefixes {
+			subFolderName := commonPrefix[len(folderPath):]
+			subFolderSize, err := GetFolderSize(subFolderName)
+			if err != nil {
+				return 0, err
+			}
+			totalSize += subFolderSize
+		}
+
+		// 判断是否还有更多文件需要获取
+		if !listObjectsResult.IsTruncated {
+			break
+		}
+
+		marker = oss.Marker(listObjectsResult.NextMarker)
+	}
+
+	return totalSize, nil
+}

+ 31 - 0
pkg/serializer/common.go

@@ -0,0 +1,31 @@
+package serializer
+
+// Response 基础序列化器
+type Response struct {
+	Code  int         `json:"code"`
+	Data  interface{} `json:"data"`
+	Msg   string      `json:"msg"`
+	Error string      `json:"error"`
+}
+
+// DataList 基础列表结构
+type DataList struct {
+	Items interface{} `json:"items"`
+	Total uint        `json:"total"`
+}
+
+// TrackedErrorResponse 有追踪信息的错误响应
+type TrackedErrorResponse struct {
+	Response
+	TrackID string `json:"track_id"`
+}
+
+// BuildListResponse 列表构建器
+func BuildListResponse(items interface{}, total uint) Response {
+	return Response{
+		Data: DataList{
+			Items: items,
+			Total: total,
+		},
+	}
+}

+ 41 - 0
pkg/serializer/version.go

@@ -0,0 +1,41 @@
+package serializer
+
+import (
+	"dsbqj-admin/model/mongo/version"
+)
+
+type Version struct {
+	Version string `json:"version"`
+	Name    string `json:"name"`
+	CDN     string `json:"cdn"`
+	Url     string `json:"url"`
+}
+
+func BuildVersion(version *version.Version) *Version {
+	var res *Version
+	if version != nil {
+		res = &Version{
+			Version: version.Version,
+			Name:    version.Name,
+			CDN:     version.CDN,
+			Url:     version.Url,
+		}
+	}
+	return res
+}
+
+type Versions map[string]interface{}
+
+func BuildVersions(versions []*version.Version) map[string]interface{} {
+	var res Versions = make(map[string]interface{})
+	var servers = make([]*Version, 0)
+	for _, v := range versions {
+		if v.Default {
+			res[v.Version] = BuildVersion(v)
+		}
+		servers = append(servers, BuildVersion(v))
+	}
+	res["servers"] = servers
+
+	return res
+}

+ 57 - 0
pkg/sms/main.go

@@ -0,0 +1,57 @@
+package sms
+
+import (
+	openapi "github.com/alibabacloud-go/darabonba-openapi/client"
+	dysmsapi20170525 "github.com/alibabacloud-go/dysmsapi-20170525/v2/client"
+	smsutil "github.com/alibabacloud-go/tea-utils/service"
+	"github.com/alibabacloud-go/tea/tea"
+	"github.com/goccy/go-json"
+	"os"
+)
+
+var client *dysmsapi20170525.Client
+
+// InitOSSClient 初始化OSS鉴权客户端
+func Init() error {
+	var _client *dysmsapi20170525.Client
+
+	var config = new(openapi.Config)
+	key := os.Getenv("SMS_ACCESS_KEY_ID")
+	config.AccessKeyId = &key
+	secret := os.Getenv("SMS_ACCESS_KEY_SECRET")
+	config.AccessKeySecret = &secret
+
+	// 访问的域名
+	config.Endpoint = tea.String("dysmsapi.aliyuncs.com")
+	_client = &dysmsapi20170525.Client{}
+	_client, err := dysmsapi20170525.NewClient(config)
+	if err != nil {
+		panic("阿里云sms初始化失败。。。。")
+	}
+	client = _client
+	return nil
+}
+
+// Put 将文件流保存到指定目录
+func Send(phoneNum string, code string, prefix string) (response *dysmsapi20170525.SendSmsResponse, err error) {
+	var params = make(map[string]interface{})
+	params["code"] = code
+	paramStr, _ := json.Marshal(params)
+
+	var sendSmsRequest = new(dysmsapi20170525.SendSmsRequest)
+	if prefix == "" || prefix == "86" {
+		sendSmsRequest.PhoneNumbers = tea.String(phoneNum)
+		sendSmsRequest.SignName = tea.String("八点八数字")
+		sendSmsRequest.TemplateCode = tea.String("SMS_243775044")
+		sendSmsRequest.TemplateParam = tea.String(string(paramStr))
+	} else {
+		sendSmsRequest.PhoneNumbers = tea.String(prefix + phoneNum)
+		sendSmsRequest.SignName = tea.String("XMEN")
+		sendSmsRequest.TemplateCode = tea.String("SMS_244595512")
+		sendSmsRequest.TemplateParam = tea.String(string(paramStr))
+	}
+
+	runtime := &smsutil.RuntimeOptions{}
+	return client.SendSmsWithOptions(sendSmsRequest, runtime)
+
+}

+ 23 - 0
pkg/static/enum.go

@@ -0,0 +1,23 @@
+package static
+
+import "errors"
+
+const (
+	ANIMS_DANCE  = "Dance"
+	ANIMS_SPEECH = "Speech"
+	ANIMS_POSE   = "Pose"
+)
+
+var AnimsTyps = map[string]int{
+	"Dance":  6,
+	"Speech": 7,
+}
+
+func GetType(code string) (int, error) {
+	msg, ok := AnimsTyps[code]
+	if ok {
+		return msg, nil
+	}
+
+	return 0, errors.New("Unkonw")
+}

+ 75 - 0
pkg/util/aes_128.go

@@ -0,0 +1,75 @@
+package util
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+)
+
+const (
+	BLOCK_SIZE_16 = 16
+)
+
+func NewAesCipher128(key, iv []byte) *AesCipher128 {
+	if len(key) == 0 || len(key) > BLOCK_SIZE_16 {
+		return nil
+	}
+	if len(iv) < BLOCK_SIZE_16 {
+		newIv := make([]byte, BLOCK_SIZE_16)
+		copy(newIv, iv)
+		iv = newIv
+	} else {
+		iv = iv[:BLOCK_SIZE_16]
+	}
+	newKey := make([]byte, BLOCK_SIZE_16)
+	copy(newKey, key)
+
+	block, err := aes.NewCipher(newKey)
+	if err != nil {
+		return nil
+	}
+	return &AesCipher128{
+		key:   newKey,
+		iv:    iv,
+		block: block,
+	}
+}
+
+type AesCipher128 struct {
+	key   []byte
+	iv    []byte
+	block cipher.Block
+}
+
+func (aesCipher *AesCipher128) BlockSize() int {
+	return BLOCK_SIZE_16
+}
+
+func (aesCipher *AesCipher128) Encrypt(origData []byte) []byte {
+	encodeBytes := []byte(origData)
+	blockSize := aesCipher.BlockSize()
+	encodeBytes = padding(encodeBytes, blockSize)
+
+	blockMode := cipher.NewCBCEncrypter(aesCipher.block, aesCipher.iv)
+	crypted := make([]byte, len(encodeBytes))
+	blockMode.CryptBlocks(crypted, encodeBytes)
+
+	return crypted
+}
+
+func (aesCipher *AesCipher128) Decrypt(encrData []byte) []byte {
+	blockMode := cipher.NewCBCDecrypter(aesCipher.block, aesCipher.iv)
+	result := make([]byte, len(encrData))
+	blockMode.CryptBlocks(result, encrData)
+	return bytes.Trim(result, "\x00")
+}
+
+func padding(ciphertext []byte, blockSize int) []byte {
+	dataSize := ((len(ciphertext)-1)/blockSize + 1) * blockSize
+	if dataSize == len(ciphertext) {
+		return ciphertext
+	}
+	newData := make([]byte, dataSize)
+	copy(newData, ciphertext)
+	return newData
+}

+ 92 - 0
pkg/util/authcode.go

@@ -0,0 +1,92 @@
+package util
+
+import (
+	"bytes"
+	"crypto/hmac"
+	"crypto/sha1"
+	"encoding/base32"
+	"encoding/binary"
+	"fmt"
+	"strings"
+	"time"
+)
+
+type GoogleAuth struct {
+}
+
+func NewGoogleAuth() *GoogleAuth {
+	return &GoogleAuth{}
+}
+
+func (this *GoogleAuth) Remain() int {
+	return 60 - time.Now().Second()
+}
+
+func (this *GoogleAuth) un() int64 {
+	return time.Now().Unix() / 60
+}
+
+func (this *GoogleAuth) hmacSha1(key, data []byte) []byte {
+	h := hmac.New(sha1.New, key)
+	if total := len(data); total > 0 {
+		h.Write(data)
+	}
+	return h.Sum(nil)
+}
+
+func (this *GoogleAuth) base32encode(src []byte) string {
+	return base32.StdEncoding.EncodeToString(src)
+}
+
+func (this *GoogleAuth) base32decode(s string) ([]byte, error) {
+	return base32.StdEncoding.DecodeString(s)
+}
+
+func (this *GoogleAuth) toBytes(value int64) []byte {
+	var result []byte
+	mask := int64(0xFF)
+	shifts := [8]uint16{56, 48, 40, 32, 24, 16, 8, 0}
+	for _, shift := range shifts {
+		result = append(result, byte((value>>shift)&mask))
+	}
+	return result
+}
+
+func (this *GoogleAuth) toUint32(bts []byte) uint32 {
+	return (uint32(bts[0]) << 24) + (uint32(bts[1]) << 16) +
+		(uint32(bts[2]) << 8) + uint32(bts[3])
+}
+
+func (this *GoogleAuth) oneTimePassword(key []byte, data []byte) uint32 {
+	hash := this.hmacSha1(key, data)
+	offset := hash[len(hash)-1] & 0x0F
+	hashParts := hash[offset : offset+4]
+	hashParts[0] = hashParts[0] & 0x7F
+	number := this.toUint32(hashParts)
+	return number % 1000000
+}
+
+func (this *GoogleAuth) GetSecret() string {
+	var buf bytes.Buffer
+	binary.Write(&buf, binary.BigEndian, this.un())
+	return strings.ToUpper(this.base32encode(this.hmacSha1(buf.Bytes(), nil)))
+}
+
+func (this *GoogleAuth) GetCode(secret string) (string, error) {
+	secretUpper := strings.ToUpper(secret)
+	secretKey, err := this.base32decode(secretUpper)
+	if err != nil {
+		return "", err
+	}
+	number := this.oneTimePassword(secretKey, this.toBytes(time.Now().Unix()/60))
+	return fmt.Sprintf("%06d", number), nil
+}
+
+func (this *GoogleAuth) VerifyCode(secret, code string) (bool, error) {
+	_code, err := this.GetCode(secret)
+	fmt.Println(_code, code)
+	if err != nil {
+		return false, err
+	}
+	return _code == code, nil
+}

+ 283 - 0
pkg/util/common.go

@@ -0,0 +1,283 @@
+package util
+
+import (
+	"dsbqj-admin/pkg/hashid"
+	"dsbqj-admin/pkg/logger"
+	"errors"
+	"io"
+	"math/rand"
+	"net"
+	"net/http"
+	"os"
+	"path/filepath"
+	"strconv"
+	"strings"
+	"sync/atomic"
+	"time"
+)
+
+var (
+	milliSec int64 = getMilliSec() //毫秒数
+)
+
+// RandStringRunes 返回随机字符串
+func RandStringRunes(n int) string {
+	var letterRunes = []rune("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
+
+	rand.Seed(time.Now().UnixNano())
+	b := make([]rune, n)
+	for i := range b {
+		b[i] = letterRunes[rand.Intn(len(letterRunes))]
+	}
+	return string(b)
+}
+
+func RandString(length int) string {
+	rand.Seed(time.Now().UnixNano())
+	rs := make([]string, length)
+	for start := 0; start < length; start++ {
+		t := rand.Intn(3)
+		if t == 0 {
+			rs = append(rs, strconv.Itoa(rand.Intn(10)))
+		} else if t == 1 {
+			rs = append(rs, string(rand.Intn(26)+65))
+		} else {
+			rs = append(rs, string(rand.Intn(26)+97))
+		}
+	}
+	return strings.Join(rs, "")
+}
+
+func RandomSmsCode(n int) string {
+	var letterRunes = []rune("1234567890")
+
+	rand.Seed(time.Now().UnixNano())
+	b := make([]rune, n)
+	for i := range b {
+		b[i] = letterRunes[rand.Intn(len(letterRunes))]
+	}
+	return string(b)
+}
+
+func RandomInt(min, max int) int {
+	rand.Seed(time.Now().UnixNano())
+	return rand.Intn(max-min+1) + min
+}
+
+func RandFloats(min, max float32, n int) []float32 {
+	res := make([]float32, n)
+	for i := range res {
+		res[i] = min + rand.Float32()*(max-min)
+	}
+	return res
+}
+
+func getMilliSec() int64 {
+	return time.Now().UnixNano() / int64(time.Millisecond)
+}
+
+func GetNowSecond() int64 {
+	return atomic.LoadInt64(&milliSec)
+}
+
+func IsSameDay(t1, t2 time.Time) bool {
+	y1, m1, d1 := t1.Date()
+	y2, m2, d2 := t2.Date()
+
+	return y1 == y2 && m1 == m2 && d1 == d2
+}
+
+func TimeStrToTick(t string) int64 {
+	loc, err := time.LoadLocation("Asia/Shanghai")
+	if err != nil {
+		logger.Error("load loc failed, err: %s", err.Error())
+		return 0
+	}
+
+	tobj, err := time.ParseInLocation("2006-01-02 15:04:05", t, loc)
+	if err != nil {
+		logger.Error("parse time failed, err: %s", err.Error())
+		return 0
+	}
+
+	return tobj.UnixNano() / 1e6
+}
+
+func subString(str string, start, end int) string {
+	rs := []rune(str)
+	length := len(rs)
+
+	if start < 0 || start > length {
+		panic("start is wrong")
+	}
+
+	if end < start || end > length {
+		panic("end is wrong")
+	}
+
+	return string(rs[start:end])
+}
+
+func GetFileDir(path string) string {
+	return subString(path, 0, strings.LastIndex(path, "/"))
+}
+
+type AarryT interface {
+	uint64 | int | uint | string
+}
+
+func ExistInArray[T AarryT](arr []T, val T) bool {
+	for _, v := range arr {
+		if v == val {
+			return true
+		}
+	}
+	return false
+}
+
+func IndexInArray[T AarryT](arr []T, val T) int {
+	for k, v := range arr {
+		if v == val {
+			return k
+		}
+	}
+	return -1
+}
+
+type MapT interface {
+	uint64 | int | uint | string
+}
+
+func GetMapKeys[T MapT, S MapT](m map[T]S) []T {
+	keys := make([]T, 0, len(m))
+	for k := range m {
+		keys = append(keys, k)
+	}
+	return keys
+}
+
+type Number interface {
+	uint64 | int64 | uint32 | int32 | uint | int | uint8 | int8
+}
+
+func StringToNumber[T Number](s string) T {
+	num, _ := strconv.Atoi(s)
+	return T(num)
+}
+
+func Exists(name string) bool {
+	if _, err := os.Stat(name); err != nil {
+		if os.IsNotExist(err) {
+			return false
+		}
+	}
+	return true
+}
+
+// CreatNestedFile 给定path创建文件,如果目录不存在就递归创建
+func CreatNestedFile(path string) (*os.File, error) {
+	basePath := filepath.Dir(path)
+	if !Exists(basePath) {
+		err := os.MkdirAll(basePath, 0700)
+		if err != nil {
+			Log().Warning("无法创建目录,%s", err)
+			return nil, err
+		}
+	}
+
+	return os.Create(path)
+}
+
+func DiffIntArr[T AarryT](arr1 []T, arr2 []T) []T {
+	res := make([]T, 0)
+	tempMap1 := make(map[T]int)
+	tempMap2 := make(map[T]int)
+
+	//已数组最长的赋值tempMap1
+	if len(arr1) > len(arr2) {
+		for _, v := range arr1 { //标记数组内重复数据
+			tempMap1[v]++
+		}
+
+		for _, v := range arr2 { //标记数组内重复数据
+			tempMap2[v]++
+		}
+	} else {
+		for _, v := range arr1 { //标记数组内重复数据
+			tempMap2[v]++
+		}
+
+		for _, v := range arr2 { //标记数组内重复数据
+			tempMap1[v]++
+		}
+	}
+
+	for k, v := range tempMap1 {
+		if tempMap2[k] != v {
+			res = append(res, k)
+		}
+	}
+
+	return res
+}
+
+func GetInternalIP() string {
+	// 思路来自于Python版本的内网IP获取,其他版本不准确
+	conn, err := net.Dial("udp", "8.8.8.8:80")
+	if err != nil {
+		return ""
+	}
+	defer conn.Close()
+
+	// udp 面向无连接,所以这些东西只在你本地捣鼓
+	res := conn.LocalAddr().String()
+	res = strings.Split(res, ":")[0]
+	return res
+}
+
+func GetExternalIP() (string, error) {
+	// 有很多类似网站提供这种服务,这是我知道且正在用的
+	// 备用:https://myexternalip.com/raw (cip.cc 应该是够快了,我连手机热点的时候不太稳,其他自己查)
+	response, err := http.Get("http://ip.cip.cc")
+	if err != nil {
+		return "", errors.New("external IP fetch failed, detail:" + err.Error())
+	}
+
+	defer response.Body.Close()
+	res := ""
+
+	// 类似的API应当返回一个纯净的IP地址
+	for {
+		tmp := make([]byte, 32)
+		n, err := response.Body.Read(tmp)
+		if err != nil {
+			if err != io.EOF {
+				return "", errors.New("external IP fetch failed, detail:" + err.Error())
+			}
+			res += string(tmp[:n])
+			break
+		}
+		res += string(tmp[:n])
+	}
+
+	return strings.TrimSpace(res), nil
+}
+
+func IsDir(path string) bool {
+	s, err := os.Stat(path)
+	if err != nil {
+
+		return false
+	}
+	return s.IsDir()
+
+}
+
+func ParseProjIdByStr(pid string) (uint, error) {
+	nProjID, err := hashid.DecodeHashID(pid, hashid.ProjectID)
+	if err != nil {
+		return 0, err
+	}
+
+	return nProjID, nil
+}

+ 74 - 0
pkg/util/des_ecb.go

@@ -0,0 +1,74 @@
+package util
+
+import (
+	"bytes"
+	"crypto/des"
+	"encoding/hex"
+	"fmt"
+)
+
+// Go DES ECB加密
+func EncryptDES_ECB(data []byte, key string) string {
+	keyByte := []byte(key)
+	block, err := des.NewCipher(keyByte)
+	if err != nil {
+		panic(err)
+	}
+	bs := block.BlockSize()
+	//对明文数据进行补码
+	data = PKCS5Padding(data, bs)
+	if len(data)%bs != 0 {
+		panic("Need a multiple of the blocksize")
+	}
+	out := make([]byte, len(data))
+	dst := out
+	for len(data) > 0 {
+		//对明文按照blocksize进行分块加密
+		//必要时可以使用go关键字进行并行加密
+		block.Encrypt(dst, data[:bs])
+		data = data[bs:]
+		dst = dst[bs:]
+	}
+	return fmt.Sprintf("%X", out)
+}
+
+func DecryptDES_ECB(src, key string) (string, error) {
+	data, err := hex.DecodeString(src)
+	if err != nil {
+		return "", err
+	}
+	keyByte := []byte(key)
+	block, err := des.NewCipher(keyByte)
+	if err != nil {
+		return "", err
+	}
+	bs := block.BlockSize()
+	if len(data)%bs != 0 {
+		panic("crypto/cipher: input not full blocks")
+	}
+	out := make([]byte, len(data))
+	dst := out
+	for len(data) > 0 {
+		block.Decrypt(dst, data[:bs])
+		data = data[bs:]
+		dst = dst[bs:]
+	}
+	out = PKCS5UnPadding(out)
+	return string(out), 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)]
+}

+ 135 - 0
pkg/util/gomgr.go

@@ -0,0 +1,135 @@
+package util
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"fmt"
+	"log"
+	"os"
+	"os/signal"
+	"runtime/debug"
+	"sync/atomic"
+	"syscall"
+	"time"
+)
+
+var (
+	gonum     int64 = 0    //主协程数管理
+	goState   int64 = 0    //游戏服运行状态 0运行中 1需要安全结束协程
+	needAlarm       = true //需要发送告警信息
+)
+
+// 所有协程是否安全运行
+func IsGoRuntime() bool {
+	return atomic.LoadInt64(&goState) == 0
+}
+
+// go协程安全结束
+func GoSecurityOver() {
+	atomic.StoreInt64(&goState, 1)
+}
+
+// 开启一个主要协程 mark协程标识
+func StartGo(mark string, f func(), overf func(isdebug bool)) {
+	startGo(mark, true, f, overf)
+}
+
+// 次要go
+// mark标识
+// f 主要逻辑方法
+// overf 结束方法
+func StartMinorGO(mark string, f func(), overf func(isdebug bool)) {
+	startGo(mark, false, f, overf)
+}
+
+// 开始go
+// mark 标识
+// ismain 是否主要协程(结束是否影响整体业务)
+// f 主要方法
+// overf 结束方法
+func startGo(mark string, ismain bool, f func(), overf func(isdebug bool)) {
+	if f == nil {
+		log.Panicln("start version fail:" + mark + ", f is nil")
+	}
+	if ismain {
+		atomic.AddInt64(&gonum, 1)
+	}
+	go func() {
+		logger.Info("start go: %s, ismain: %t", mark, ismain)
+		if ismain {
+			log.Println("start go:", mark)
+		}
+		defer func() {
+			isdebug := false
+			if err := recover(); err != nil {
+				logger.Debug(fmt.Sprint("[debug] ", mark, " error:", err, " stack:", string(debug.Stack())))
+				isdebug = true
+			}
+			if ismain {
+				log.Println("end go:", mark, ",isdebug:", isdebug)
+			}
+			logger.Info("version over mark: %v ,ismain: %t", mark, ismain)
+			if overf != nil {
+				func() { //防止结束任务debug
+					defer func() {
+						ListenDebug(mark + " overf bug")
+					}()
+					overf(isdebug)
+				}()
+			}
+			if ismain { //需要安全结束协程
+				atomic.AddInt64(&gonum, -1)
+				GoSecurityOver()
+			}
+		}()
+		f()
+	}()
+}
+
+// 监听debug(true为有bug)
+func ListenDebug(mark string) bool {
+	if err := recover(); err != nil {
+		logger.Debug("[debug] %s  error: %s stack: %s", mark, err, string(debug.Stack()))
+		return true
+	}
+	return false
+}
+
+// 监控所有主要协程,都结束后,才结束
+// stopall 程序被kill后执行
+func ListenAllGO(stopall func(), alarmGroup string, alarmContent string) {
+	ListenKill()
+	flag := false
+	for {
+		time.Sleep(2 * time.Second)
+		if !flag && !IsGoRuntime() { //监听有协程关闭,主动停止需要手动停止的协程
+			flag = true
+			if stopall != nil {
+				stopall()
+			}
+			log.Println("stopall goruntime")
+		}
+		v := atomic.LoadInt64(&gonum)
+		if v <= 0 {
+			if needAlarm {
+				// 发送报警
+				//SendAlarm(alarmGroup, alarmContent)
+			}
+			logger.Info("meeting over")
+			log.Println("all go over")
+			return
+		}
+	}
+}
+
+// 监听kill命令
+func ListenKill() {
+	StartMinorGO("listen kill", func() {
+		c := make(chan os.Signal, 1)
+		signal.Notify(c, os.Interrupt, os.Kill)
+		signal.Notify(c, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
+		s := <-c
+		needAlarm = false
+		logger.Info("Server Exit: %s", s.String())
+		atomic.StoreInt64(&goState, 1) //提示监听协程结束
+	}, nil)
+}

+ 76 - 0
pkg/util/log_linux_amd64.go

@@ -0,0 +1,76 @@
+//获取linux环境下崩溃栈(可以根据文件最后修改时间,知悉崩溃时间)
+//pprof memory(cpu) need graphviz
+//go tool pprof --pdf/web/svg http://10.105.57.151:8101/debug/pprof/heap(profile) >one.pdf
+
+package util
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"fmt"
+	"log"
+	"net/http"
+	_ "net/http/pprof"
+	"os"
+	"os/exec"
+	"syscall"
+)
+
+func init() {
+	os.MkdirAll("logs", 0660)
+	logFile, err := os.OpenFile("logs/sysdebug.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0660)
+	if err != nil {
+		log.Println("open sysdebug err:", err.Error())
+		return
+	}
+	// 将进程标准出错重定向至文件,进程崩溃时运行时将向该文件记录协程调用栈信息
+	syscall.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))
+	log.Println("program runtime...")
+	//监控pprof
+	ListenPprof()
+}
+
+// 端口号是否正在被使用
+func PortInUse(port int) bool {
+	checkStatement := fmt.Sprintf("lsof -i:%d ", port)
+	output, _ := exec.Command("sh", "-c", checkStatement).CombinedOutput()
+	if len(output) > 0 {
+		return true
+	}
+	return false
+}
+
+// 监控pprof
+func ListenPprof() {
+	go func() {
+		port := 6060
+		for i := 0; i < 1000; i++ { //从6060开始,找一个空闲的端口号
+			if PortInUse(port) {
+				port++
+			} else {
+				break
+			}
+		}
+		logger.Info("start listen pprof: %d", port)
+		err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
+		if err != nil {
+			logger.Error("pprof listen err: %s", err.Error())
+		}
+	}()
+}
+
+// 发送警告
+func SendAlarm(group string, content string) {
+	if group == "" {
+		return
+	}
+	command := fmt.Sprint("cagent_tools alarm  '《指挥官》", content, "' ", group)
+	cmd := exec.Command("/bin/bash", "-c", command)
+
+	output, err := cmd.Output()
+	if err != nil {
+		log.Printf("Execute Shell:%s failed with error:%s", command, err.Error())
+		return
+	}
+	log.Printf("Execute Shell:%s finished with output:\n%s", command, string(output))
+
+}

+ 14 - 0
pkg/util/log_windows_x86.go

@@ -0,0 +1,14 @@
+package util
+
+import (
+	"fmt"
+	"net/http"
+	_ "net/http/pprof"
+)
+
+func init() {
+	go func() {
+		fmt.Println("pprof running on %d", 6060)
+		http.ListenAndServe("0.0.0.0:6060", nil)
+	}()
+}

+ 140 - 0
pkg/util/logger.go

@@ -0,0 +1,140 @@
+package util
+
+import (
+	"fmt"
+	"github.com/fatih/color"
+	"sync"
+	"time"
+)
+
+const (
+	// LevelError 错误
+	LevelError = iota
+	// LevelWarning 警告
+	LevelWarning
+	// LevelInformational 提示
+	LevelInformational
+	// LevelDebug 除错
+	LevelDebug
+)
+
+var GloablLogger *Logger
+var Level = LevelDebug
+
+// Logger 日志
+type Logger struct {
+	level int
+	mu    sync.Mutex
+}
+
+// 日志颜色
+var colors = map[string]func(a ...interface{}) string{
+	"Warning": color.New(color.FgYellow).Add(color.Bold).SprintFunc(),
+	"Panic":   color.New(color.BgRed).Add(color.Bold).SprintFunc(),
+	"Error":   color.New(color.FgRed).Add(color.Bold).SprintFunc(),
+	"Info":    color.New(color.FgCyan).Add(color.Bold).SprintFunc(),
+	"Debug":   color.New(color.FgWhite).Add(color.Bold).SprintFunc(),
+}
+
+// 不同级别前缀与时间的间隔,保持宽度一致
+var spaces = map[string]string{
+	"Warning": "",
+	"Panic":   "  ",
+	"Error":   "  ",
+	"Info":    "   ",
+	"Debug":   "  ",
+}
+
+// Println 打印
+func (ll *Logger) Println(prefix string, msg string) {
+	// TODO Release时去掉
+
+	c := color.New()
+
+	ll.mu.Lock()
+	defer ll.mu.Unlock()
+
+	_, _ = c.Printf(
+		"%s%s %s %s\n",
+		colors[prefix]("["+prefix+"]"),
+		spaces[prefix],
+		time.Now().Format("2006-01-02 15:04:05"),
+		msg,
+	)
+}
+
+// Panic 极端错误
+func (ll *Logger) Panic(format string, v ...interface{}) {
+	if LevelError > ll.level {
+		return
+	}
+	msg := fmt.Sprintf(format, v...)
+	ll.Println("Panic", msg)
+	panic(msg)
+}
+
+// Error 错误
+func (ll *Logger) Error(format string, v ...interface{}) {
+	if LevelError > ll.level {
+		return
+	}
+	msg := fmt.Sprintf(format, v...)
+	ll.Println("Error", msg)
+}
+
+// Warning 警告
+func (ll *Logger) Warning(format string, v ...interface{}) {
+	if LevelWarning > ll.level {
+		return
+	}
+	msg := fmt.Sprintf(format, v...)
+	ll.Println("Warning", msg)
+}
+
+// Info 信息
+func (ll *Logger) Info(format string, v ...interface{}) {
+	if LevelInformational > ll.level {
+		return
+	}
+	msg := fmt.Sprintf(format, v...)
+	ll.Println("Info", msg)
+}
+
+// Debug 校验
+func (ll *Logger) Debug(format string, v ...interface{}) {
+	if LevelDebug > ll.level {
+		return
+	}
+	msg := fmt.Sprintf(format, v...)
+	ll.Println("Debug", msg)
+}
+
+// BuildLogger 构建logger
+func BuildLogger(level string) {
+	intLevel := LevelError
+	switch level {
+	case "error":
+		intLevel = LevelError
+	case "warning":
+		intLevel = LevelWarning
+	case "info":
+		intLevel = LevelInformational
+	case "debug":
+		intLevel = LevelDebug
+	}
+	l := Logger{
+		level: intLevel,
+	}
+	GloablLogger = &l
+}
+
+// Log 返回日志对象
+func Log() *Logger {
+	if GloablLogger == nil {
+		l := Logger{
+			level: Level,
+		}
+		GloablLogger = &l
+	}
+	return GloablLogger
+}

+ 14 - 0
pkg/util/md5.go

@@ -0,0 +1,14 @@
+package util
+
+import (
+	"crypto/md5"
+	"encoding/hex"
+)
+
+// EncodeMD5 md5 encryption
+func EncodeMD5(value string) string {
+	m := md5.New()
+	m.Write([]byte(value))
+
+	return hex.EncodeToString(m.Sum(nil))
+}

+ 312 - 0
pkg/util/proto_decode.go

@@ -0,0 +1,312 @@
+//协议反序列化
+package util
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"io"
+	"math"
+	"reflect"
+	"runtime/debug"
+	"sync"
+	"unsafe"
+)
+
+//将二进制协议转为结构体
+func UnPack(data []byte, v interface{}) error {
+	d := decodeState{*bytes.NewBuffer(data)}
+	return d.unmarshal(v)
+}
+
+func (d *decodeState) unmarshal(v interface{}) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			if je, ok := r.(error); ok {
+				err = je
+			} else {
+				err = errors.New("unpack error:" + fmt.Sprint(r) + " debug:" + string(debug.Stack()))
+			}
+		}
+	}()
+	rv := reflect.ValueOf(v)
+	if rv.Kind() != reflect.Ptr || rv.IsNil() {
+		return errors.New("unpack not support type:" + reflect.TypeOf(v).String())
+	}
+	d.reflectValue(rv.Elem())
+	return
+}
+
+//按长度读取字符串
+func (d *decodeState) ReadBytesLen(l uint32) []byte {
+	buf := make([]byte, l)
+	_, err := io.ReadFull(d, buf)
+	if err != nil {
+		panic(err)
+	}
+	return buf
+}
+
+//读取数字类型(需要明确v指针类型)
+func (d *decodeState) ReadInt(v interface{}) {
+	err := binary.Read(d, binary.BigEndian, v)
+	if err != nil {
+		panic(err)
+	}
+}
+
+type decodeState struct {
+	bytes.Buffer
+}
+
+func (d *decodeState) reflectValue(v reflect.Value) {
+	typeDecoder(v.Type())(d, v)
+}
+
+type decoderFunc func(e *decodeState, v reflect.Value)
+
+var decoderCache sync.Map // map[reflect.Type]decoderFunc
+
+func typeDecoder(t reflect.Type) decoderFunc {
+	if fi, ok := decoderCache.Load(t); ok {
+		return fi.(decoderFunc)
+	}
+	var (
+		wg sync.WaitGroup
+		f  decoderFunc
+	)
+	wg.Add(1)
+	fi, loaded := decoderCache.LoadOrStore(t, decoderFunc(func(e *decodeState, v reflect.Value) {
+		wg.Wait()
+		f(e, v)
+	}))
+	if loaded {
+		return fi.(decoderFunc)
+	}
+	f = newTypeDecoder(t)
+	wg.Done()
+	decoderCache.Store(t, f)
+	return f
+}
+func newTypeDecoder(t reflect.Type) decoderFunc {
+	switch t.Kind() {
+	case reflect.Bool:
+		return boolDecoder
+	case reflect.Int:
+		return intDecoder
+	case reflect.Int32:
+		return int32Decoder
+	case reflect.Uint:
+		return uintDecoder
+	case reflect.Uint32:
+		return uint32Decoder
+	case reflect.Int8:
+		return int8Decoder
+	case reflect.Int16:
+		return int16Decoder
+	case reflect.Int64:
+		return int64Decoder
+	case reflect.Uint8:
+		return uint8Decoder
+	case reflect.Uint16:
+		return uint16Decoder
+	case reflect.Uint64:
+		return uint64Decoder
+	case reflect.Float32:
+		return float32Decoder
+	case reflect.Float64:
+		return float64Decoder
+	case reflect.String:
+		return stringDecoder
+	case reflect.Struct:
+		return newStructDecoder(t)
+	case reflect.Slice:
+		return newSliceDecoder(t)
+	case reflect.Array:
+		return newArrayDecoder(t)
+	default:
+		panic(errors.New("proto unpack: unsupported type: " + t.String()))
+	}
+}
+
+func boolDecoder(d *decodeState, v reflect.Value) {
+	b, err := d.ReadByte()
+	if err != nil {
+		panic(err)
+	}
+	if b == 1 {
+		v.SetBool(true)
+	} else {
+		v.SetBool(false)
+	}
+}
+
+//读取四个字节
+func intDecoder(d *decodeState, v reflect.Value) {
+	var t int32
+	d.ReadInt(&t)
+	v.SetInt(int64(t))
+}
+
+//读取四个字节
+func int32Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*int32)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+//读取四个字节
+func uintDecoder(d *decodeState, v reflect.Value) {
+	var t uint32
+	d.ReadInt(&t)
+	v.SetUint(uint64(t))
+}
+func uint32Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*uint32)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func int8Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*int8)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func int16Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*int16)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func int64Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*int64)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func uint8Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*uint8)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func uint16Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*uint16)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+func uint64Decoder(d *decodeState, v reflect.Value) {
+	d.ReadInt((*uint64)((unsafe.Pointer)(v.UnsafeAddr())))
+}
+
+//float32
+func float32Decoder(d *decodeState, v reflect.Value) {
+	var b uint32 = 0
+	d.ReadInt(&b)
+	v.SetFloat(float64(math.Float32frombits(b)))
+}
+
+//float64
+func float64Decoder(d *decodeState, v reflect.Value) {
+	var b uint64 = 0
+	d.ReadInt(&b)
+	v.SetFloat(math.Float64frombits(b))
+}
+
+//string解析
+func stringDecoder(d *decodeState, v reflect.Value) {
+	var l uint32 = 0
+	d.ReadInt(&l)
+	if l == 0 {
+		return
+	}
+	b := make([]byte, l)
+	_, err := io.ReadFull(d, b)
+	if err != nil {
+		panic(err)
+	}
+	v.SetString(string(b))
+}
+
+func newSliceDecoder(t reflect.Type) decoderFunc {
+	if t.Elem().Kind() == reflect.Uint8 {
+		return decodeByteSlice
+	}
+	dec := sliceDecoder{newArrayDecoder(t)}
+	return dec.decode
+}
+
+func newArrayDecoder(t reflect.Type) decoderFunc {
+	dec := arrayDecoder{typeDecoder(t.Elem())}
+	return dec.decode
+}
+
+func decodeByteSlice(d *decodeState, v reflect.Value) {
+	var l uint32
+	d.ReadInt(&l)
+	if l == 0 {
+		return
+	}
+	v.SetBytes(d.ReadBytesLen(l))
+}
+
+type sliceDecoder struct {
+	arrayDec decoderFunc
+}
+
+type arrayDecoder struct {
+	elemDec decoderFunc
+}
+
+func (se sliceDecoder) decode(d *decodeState, v reflect.Value) {
+	se.arrayDec(d, v)
+}
+
+func (ae arrayDecoder) decode(d *decodeState, v reflect.Value) {
+	var l uint32
+	d.ReadInt(&l)
+	if l == 0 {
+		return
+	}
+	L := int(l)
+	if v.Kind() == reflect.Slice {
+		v.Set(reflect.MakeSlice(v.Type(), L, L))
+	}
+	for i := 0; i < L; i++ {
+		ae.elemDec(d, v.Index(i))
+	}
+}
+
+type structDecoder struct {
+	fields []deField
+}
+
+func (se structDecoder) decode(d *decodeState, v reflect.Value) {
+	for i := range se.fields {
+		f := &se.fields[i]
+		f.decoder(d, v.Field(i))
+	}
+}
+
+var deFieldCache sync.Map // map[reflect.Type]structDecoder
+
+type deField struct {
+	decoder decoderFunc
+}
+
+func newStructDecoder(t reflect.Type) decoderFunc {
+	return cachedDeFields(t).decode
+}
+
+func cachedDeFields(t reflect.Type) structDecoder {
+	if f, ok := deFieldCache.Load(t); ok {
+		return f.(structDecoder)
+	}
+	f, _ := deFieldCache.LoadOrStore(t, typeDeFields(t))
+	return f.(structDecoder)
+}
+
+func typeDeFields(t reflect.Type) structDecoder {
+	var fields []deField
+	for i := 0; i < t.NumField(); i++ {
+		sf := t.Field(i)
+		st := sf.Type
+		if st == t {
+			panic("proto:not support recursive type:" + st.String())
+		}
+		if sf.PkgPath != "" {
+			panic("proto:not support type:" + t.String() + ",name:" + sf.Name)
+		}
+		fields = append(fields, deField{decoder: typeDecoder(st)})
+	}
+	return structDecoder{fields}
+}

+ 265 - 0
pkg/util/proto_encode.go

@@ -0,0 +1,265 @@
+//协议序列化
+package util
+
+import (
+	"bytes"
+	"encoding/binary"
+	"errors"
+	"fmt"
+	"math"
+	"reflect"
+	"runtime/debug"
+	"sync"
+)
+
+//协议转为二进制
+func Pack(v interface{}) ([]byte, error) {
+	e := newEncodeState()
+	err := e.marshal(v)
+	if err != nil {
+		return nil, err
+	}
+	buf := append([]byte(nil), e.Bytes()...)
+	encodeStatePool.Put(e)
+	return buf, nil
+}
+
+var encodeStatePool sync.Pool
+
+type encodeState struct {
+	bytes.Buffer
+}
+
+//写入数字类型(v需要明确数字类型,不能为int,uint)
+func (e *encodeState) WriteInt(v interface{}) {
+	err := binary.Write(e, binary.BigEndian, v)
+	if err != nil {
+		panic(err)
+	}
+}
+
+func (e *encodeState) reflectValue(v reflect.Value) {
+	typeEncoder(v.Type())(e, v)
+}
+
+func (e *encodeState) marshal(v interface{}) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			if je, ok := r.(error); ok {
+				err = je
+			} else {
+				err = errors.New("pack error:" + fmt.Sprint(r) + " debug:" + string(debug.Stack()))
+			}
+		}
+	}()
+	rv := reflect.ValueOf(v)
+	if rv.Kind() == reflect.Ptr {
+		rv = rv.Elem()
+	}
+	e.reflectValue(rv)
+	return
+}
+
+func newEncodeState() *encodeState {
+	if v := encodeStatePool.Get(); v != nil {
+		e := v.(*encodeState)
+		e.Reset()
+		return e
+	}
+	return &encodeState{}
+}
+
+type protoError struct{ error }
+
+type encoderFunc func(e *encodeState, v reflect.Value)
+
+var encoderCache sync.Map // map[reflect.Type]encoderFunc
+
+func typeEncoder(t reflect.Type) encoderFunc {
+	if fi, ok := encoderCache.Load(t); ok {
+		return fi.(encoderFunc)
+	}
+	var (
+		wg sync.WaitGroup
+		f  encoderFunc
+	)
+	wg.Add(1)
+	fi, loaded := encoderCache.LoadOrStore(t, encoderFunc(func(e *encodeState, v reflect.Value) {
+		wg.Wait()
+		f(e, v)
+	}))
+	if loaded {
+		return fi.(encoderFunc)
+	}
+	f = newTypeEncoder(t)
+	wg.Done()
+	encoderCache.Store(t, f)
+	return f
+}
+
+func newTypeEncoder(t reflect.Type) encoderFunc {
+	switch t.Kind() {
+	case reflect.Bool:
+		return boolEncoder
+	case reflect.Int:
+		return intEncoder
+	case reflect.Uint:
+		return uintEncoder
+	case reflect.Int32, reflect.Int8, reflect.Int16, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		return intComEncoder
+	case reflect.Float32:
+		return float32Encoder
+	case reflect.Float64:
+		return float64Encoder
+	case reflect.String:
+		return stringEncoder
+	case reflect.Struct:
+		return newStructEncoder(t)
+	case reflect.Slice:
+		return newSliceEncoder(t)
+	case reflect.Array:
+		return newArrayEncoder(t)
+	default:
+		panic(errors.New("proto: unsupported type: " + t.String()))
+	}
+}
+
+func boolEncoder(e *encodeState, v reflect.Value) {
+	if v.Bool() {
+		e.WriteByte(1)
+	} else {
+		e.WriteByte(0)
+	}
+}
+
+//通用
+func intComEncoder(e *encodeState, v reflect.Value) {
+	e.WriteInt(v.Interface())
+}
+
+//4字节
+func intEncoder(e *encodeState, v reflect.Value) {
+	e.WriteInt(int32(v.Int()))
+}
+
+//4字节
+func uintEncoder(e *encodeState, v reflect.Value) {
+	e.WriteInt(uint32(v.Uint()))
+}
+
+//float32
+func float32Encoder(e *encodeState, v reflect.Value) {
+	e.WriteInt(math.Float32bits((float32)(v.Float())))
+}
+
+//float64
+func float64Encoder(e *encodeState, v reflect.Value) {
+	e.WriteInt(math.Float64bits((v.Float())))
+}
+
+//string
+func stringEncoder(e *encodeState, v reflect.Value) {
+	buf := []byte(v.String())
+	l := len(buf)
+	if l > 70000 {
+		panic(errors.New(fmt.Sprint("proto: stringLen len > 70000 l:", l)))
+	}
+	e.WriteInt(uint32(l))
+	e.Write(buf)
+}
+
+func encodeByteSlice(e *encodeState, v reflect.Value) {
+	res := v.Bytes()
+	l := len(res)
+	if l >= 5000000 {
+		panic(errors.New(fmt.Sprint("proto: []uint8 len > 5000000 l:", l)))
+	}
+	e.WriteInt(uint32(l))
+	e.Write(res)
+}
+
+type sliceEncoder struct {
+	arrayEnc encoderFunc
+}
+
+func (se sliceEncoder) encode(e *encodeState, v reflect.Value) {
+	if v.IsNil() {
+		e.WriteInt(uint32(0))
+		return
+	}
+	se.arrayEnc(e, v)
+}
+
+func newSliceEncoder(t reflect.Type) encoderFunc {
+	// Byte slices get special treatment; arrays don't.
+	if t.Elem().Kind() == reflect.Uint8 {
+		return encodeByteSlice
+	}
+	enc := sliceEncoder{newArrayEncoder(t)}
+	return enc.encode
+}
+
+type arrayEncoder struct {
+	elemEnc encoderFunc
+}
+
+func (ae arrayEncoder) encode(e *encodeState, v reflect.Value) {
+	l := v.Len()
+	if l > 5000000 {
+		panic(errors.New("proto: not support arrLen> 5000000 type:" + v.Type().String()))
+	}
+	e.WriteInt(uint32(l))
+	for i := 0; i < l; i++ {
+		ae.elemEnc(e, v.Index(i))
+	}
+}
+
+func newArrayEncoder(t reflect.Type) encoderFunc {
+	enc := arrayEncoder{typeEncoder(t.Elem())}
+	return enc.encode
+}
+
+type structEncoder struct {
+	fields []field
+}
+
+func (se structEncoder) encode(e *encodeState, v reflect.Value) {
+	for i := range se.fields {
+		f := &se.fields[i]
+		f.encoder(e, v.Field(i))
+	}
+}
+
+func newStructEncoder(t reflect.Type) encoderFunc {
+	return cachedTypeFields(t).encode
+}
+
+type field struct {
+	encoder encoderFunc
+}
+
+func typeFields(t reflect.Type) structEncoder {
+	var fields []field
+	for i := 0; i < t.NumField(); i++ {
+		sf := t.Field(i)
+		st := sf.Type
+		if st == t {
+			panic("proto:not support recursive type:" + st.String())
+		}
+		if sf.PkgPath != "" {
+			panic("proto:not support type:" + t.String() + ",name:" + sf.Name)
+		}
+		fields = append(fields, field{encoder: typeEncoder(st)})
+	}
+	return structEncoder{fields}
+}
+
+var fieldCache sync.Map // map[reflect.Type]structEncoder
+
+func cachedTypeFields(t reflect.Type) structEncoder {
+	if f, ok := fieldCache.Load(t); ok {
+		return f.(structEncoder)
+	}
+	f, _ := fieldCache.LoadOrStore(t, typeFields(t))
+	return f.(structEncoder)
+}

+ 33 - 0
pkg/util/redis.go

@@ -0,0 +1,33 @@
+package util
+
+import (
+	"github.com/go-redis/redis"
+	"strconv"
+)
+
+// RedisClient Redis缓存客户端单例
+type Redis struct {
+	Db        string
+	Addr      string
+	Password  string
+	Client    *redis.Client
+	ConnTimes int
+	stop      bool
+}
+
+// Redis 在中间件中初始化redis链接
+func Connect(dbs string, addr string, password string) *redis.Client {
+	db, _ := strconv.ParseUint(dbs, 10, 64)
+	client := redis.NewClient(&redis.Options{
+		Addr:     addr,
+		Password: password,
+		DB:       int(db),
+	})
+
+	_, err := client.Ping().Result()
+	if err != nil {
+		panic("redis connect err: " + dbs + " " + addr + " " + password)
+	}
+
+	return client
+}

+ 72 - 0
pkg/util/safe_array.go

@@ -0,0 +1,72 @@
+package util
+
+import "sync"
+
+type SafeArray[T any] struct {
+	mu   sync.Mutex
+	list []T
+}
+
+// Push 一个元素
+func (sa *SafeArray[T]) Push(v T) {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+	sa.list = append(sa.list, v)
+}
+
+// PushMany 多个元素
+func (sa *SafeArray[T]) PushMany(vs []T) {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+	sa.list = append(sa.list, vs...)
+}
+
+// Pop 最后一个元素
+func (sa *SafeArray[T]) Pop() (zero T, ok bool) {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+	if len(sa.list) == 0 {
+		return zero, false
+	}
+	last := sa.list[len(sa.list)-1]
+	sa.list = sa.list[:len(sa.list)-1]
+	return last, true
+}
+
+// Flush 清空并返回所有元素
+func (sa *SafeArray[T]) Flush() []T {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+	res := sa.list
+	sa.list = make([]T, 0)
+	return res
+}
+
+func (sa *SafeArray[T]) View() []T {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+
+	listCopy := make([]T, len(sa.list))
+	copy(listCopy, sa.list)
+	return listCopy
+}
+
+func (sa *SafeArray[T]) Iterate(fn func(T)) {
+	sa.mu.Lock()
+	defer sa.mu.Unlock()
+
+	for _, item := range sa.list {
+		fn(item)
+	}
+}
+
+func (sa *SafeArray[T]) ForEach(fn func(T)) {
+	sa.mu.Lock()
+	listCopy := make([]T, len(sa.list))
+	copy(listCopy, sa.list)
+	sa.mu.Unlock()
+
+	for _, item := range listCopy {
+		fn(item)
+	}
+}

+ 45 - 0
pkg/util/safe_map.go

@@ -0,0 +1,45 @@
+package util
+
+import (
+	"sync"
+	"sync/atomic"
+)
+
+type SafeMap[K comparable, V any] struct {
+	m      sync.Map
+	length int64 // 用于记录映射的长度
+}
+
+func (sm *SafeMap[K, V]) Set(key K, value V) {
+	sm.m.Store(key, value)
+	atomic.AddInt64(&sm.length, 1)
+}
+
+func (sm *SafeMap[K, V]) Get(key K) (V, bool) {
+	value, ok := sm.m.Load(key)
+	if !ok {
+		var zero V
+		return zero, false
+	}
+	return value.(V), true
+}
+
+func (sm *SafeMap[K, V]) Delete(key K) {
+	sm.m.Delete(key)
+	atomic.AddInt64(&sm.length, -1)
+}
+
+func (sm *SafeMap[K, V]) Length() int {
+	return int(atomic.LoadInt64(&sm.length))
+}
+
+func (sm *SafeMap[K, V]) Range(fn func(V) bool) {
+	sm.m.Range(func(key, value any) bool {
+		return fn(value.(V))
+	})
+}
+
+func (sm *SafeMap[K, V]) Clear() {
+	sm.m = sync.Map{}
+	sm.length = 0
+}

+ 42 - 0
pkg/util/times.go

@@ -0,0 +1,42 @@
+package util
+
+import "time"
+
+var TimeLayout = "2006-01-02 15:04:05" //转化所需模板
+
+func DateTime2TimeStr(datatime string) int64 {
+	loc, _ := time.LoadLocation("Local")                          //重要:获取时区
+	theTime, _ := time.ParseInLocation(TimeLayout, datatime, loc) //使用模板在对应时区转化为time.time类型
+	return theTime.Unix()
+}
+
+func TimeStr2DateTime(timestr int64, format string) string {
+	return time.Unix(timestr, 0).Format(format)
+}
+
+// 获取相差时间(小时)
+func GetHourDiffer(start_time, end_time string) int64 {
+	var hour int64
+	t1, err := time.ParseInLocation(TimeLayout, start_time, time.Local)
+	t2, err := time.ParseInLocation(TimeLayout, end_time, time.Local)
+	if err == nil && t1.Before(t2) {
+		diff := t2.Unix() - t1.Unix()
+		hour = diff / 3600
+		return hour
+	} else {
+		return hour
+	}
+}
+
+// 获取相差时间(秒)
+func GetSecondDiffer(start_time, end_time string) int64 {
+	var scond int64
+	t1, err := time.ParseInLocation(TimeLayout, start_time, time.Local)
+	t2, err := time.ParseInLocation(TimeLayout, end_time, time.Local)
+	if err == nil && t1.Before(t2) {
+		diff := t2.Unix() - t1.Unix()
+		return diff
+	} else {
+		return scond
+	}
+}

+ 95 - 0
pkg/util/token.go

@@ -0,0 +1,95 @@
+package util
+
+import (
+	"dsbqj-admin/pkg/logger"
+	"encoding/base64"
+
+	"github.com/goccy/go-json"
+)
+
+//var jwtSecret []byte
+//
+//type Claims struct {
+//	ID       string
+//	UserName string
+//	jwt.StandardClaims
+//}
+//
+//// GenerateToken generate tokens used for auth
+//func GenerateToken(user model.User, secret string) (string, error) {
+//	nowTime := time.Now()
+//	expireTime := nowTime.Add(3 * time.Hour)
+//
+//	claims := Claims{
+//		EncodeMD5(hashid.HashID(user.ID, hashid.UserID)),
+//		EncodeMD5(user.UserName),
+//
+//		jwt.StandardClaims{
+//			ExpiresAt: expireTime.Unix(),
+//			Issuer:    "gin-blog",
+//		},
+//	}
+//
+//	tokenClaims := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
+//	token, err := tokenClaims.SignedString(jwtSecret)
+//
+//	return token, err
+//}
+//
+//// ParseToken parsing token
+//func ParseToken(token string, secret string) (*Claims, error) {
+//	tokenClaims, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
+//		return jwtSecret, nil
+//	})
+//
+//	if tokenClaims != nil {
+//		if claims, ok := tokenClaims.Claims.(*Claims); ok && tokenClaims.Valid {
+//			return claims, nil
+//		}
+//	}
+//
+//	return nil, err
+//}
+
+type Token struct {
+	ID        uint   `json:"id"`
+	Status    string `json:"status"`
+	Role      string `json:"role"`
+	Gender    string `json:"gender"`
+	ExpiresIn int64  `json:"expires_in"`
+	CreateAt  int64  `json:"create_at"`
+}
+
+func GenerateToken(src interface{}, key string) string {
+
+	ci := NewAesCipher128([]byte(key), []byte("Power-Doom"))
+
+	//转json
+	j, err := json.Marshal(src)
+	if err != nil {
+		logger.Error("数据转换错误", err)
+		return ""
+	}
+
+	res := ci.Encrypt(j)
+	return base64.StdEncoding.EncodeToString(res)
+}
+
+func ParseToken(src string, key string) []byte {
+	defer func() {
+		err := recover() //内置函数,可以捕捉到函数异常
+		if err != nil {
+			//这里是打印错误,还可以进行报警处理,例如微信,邮箱通知
+			logger.Error("token解析错误:", err)
+		}
+	}()
+
+	plain, err := base64.StdEncoding.DecodeString(src)
+	if err != nil {
+		logger.Error("token解析错误", err)
+		return []byte{}
+	}
+
+	ci := NewAesCipher128([]byte(key), []byte("Power-Doom"))
+	return ci.Decrypt(plain)
+}

+ 36 - 0
pkg/util/verfiy.go

@@ -0,0 +1,36 @@
+package util
+
+import (
+	"bytes"
+	"dsbqj-admin/pkg/logger"
+	"github.com/goccy/go-json"
+	"io/ioutil"
+	"net/http"
+)
+
+func CheckWords(w string) (string, bool) {
+	data := map[string]string{}
+	data["content"] = w
+
+	sendbuf, _ := json.Marshal(data)
+
+	resp, err := http.Post("http://47.110.50.126:5928/sensitive/check", "application/json", bytes.NewReader(sendbuf))
+	if err != nil {
+		logger.Error("CheckWords com httppostjson err!err:", err.Error())
+		return w, false
+	}
+
+	buf, err1 := ioutil.ReadAll(resp.Body)
+	if err1 != nil {
+		logger.Error("CheckWords read body err!err", err1.Error())
+		return w, false
+	}
+
+	var result map[string]interface{}
+	err = json.Unmarshal(buf, &result)
+	if err != nil {
+		logger.Info("%s", err.Error())
+	}
+	logger.Info("check user name result:===?> %v", result)
+	return w, result["data"].(bool)
+}

+ 20 - 0
pkg/validator/main.go

@@ -0,0 +1,20 @@
+package validator
+
+import (
+	"github.com/gin-gonic/gin/binding"
+	"github.com/go-playground/validator/v10"
+)
+
+func gender(fl validator.FieldLevel) bool {
+	v := fl.Field().String()
+	if v != "" && v != "female" && v != "male" {
+		return false
+	}
+	return true
+}
+
+func Init() {
+	if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
+		v.RegisterValidation("gender", gender)
+	}
+}

+ 14 - 0
test/mongo_test.go

@@ -0,0 +1,14 @@
+package test
+
+import (
+	"fmt"
+	"go.mongodb.org/mongo-driver/bson/primitive"
+	"testing"
+)
+
+func TestMongo(t *testing.T) {
+	var id primitive.ObjectID
+	id = primitive.NewObjectID()
+	fmt.Println(id)
+
+}