ソースを参照

第一次上传仓库

wangbin 3 年 前
コミット
885b78b337
共有100 個のファイルを変更した18736 個の追加0 個の削除を含む
  1. 44 0
      .gitignore
  2. 24 0
      Dockerfile
  3. 58 0
      README.md
  4. 15 0
      api/v1/enter.go
  5. 15 0
      api/v1/example/enter.go
  6. 142 0
      api/v1/example/exa_breakpoint_continue.go
  7. 142 0
      api/v1/example/exa_customer.go
  8. 111 0
      api/v1/example/exa_excel.go
  9. 94 0
      api/v1/example/exa_file_upload_download.go
  10. 19 0
      api/v1/log/enter.go
  11. 118 0
      api/v1/log/log_coding.go
  12. 107 0
      api/v1/log/log_env.go
  13. 218 0
      api/v1/log/loging.go
  14. 38 0
      api/v1/system/enter.go
  15. 172 0
      api/v1/system/sys_api.go
  16. 165 0
      api/v1/system/sys_authority.go
  17. 66 0
      api/v1/system/sys_authority_btn.go
  18. 242 0
      api/v1/system/sys_auto_code.go
  19. 98 0
      api/v1/system/sys_auto_code_history.go
  20. 42 0
      api/v1/system/sys_captcha.go
  21. 55 0
      api/v1/system/sys_casbin.go
  22. 117 0
      api/v1/system/sys_dictionary.go
  23. 117 0
      api/v1/system/sys_dictionary_detail.go
  24. 60 0
      api/v1/system/sys_initdb.go
  25. 29 0
      api/v1/system/sys_jwt_blacklist.go
  26. 226 0
      api/v1/system/sys_menu.go
  27. 118 0
      api/v1/system/sys_operation_record.go
  28. 78 0
      api/v1/system/sys_system.go
  29. 377 0
      api/v1/system/sys_user.go
  30. 3 0
      compile.bat
  31. 159 0
      config.yaml
  32. 18 0
      config/auto_code.go
  33. 7 0
      config/captcha.go
  34. 29 0
      config/config.go
  35. 14 0
      config/cors.go
  36. 29 0
      config/db_list.go
  37. 11 0
      config/email.go
  38. 5 0
      config/excel.go
  39. 13 0
      config/gorm_mysql.go
  40. 21 0
      config/gorm_pgsql.go
  41. 8 0
      config/jwt.go
  42. 10 0
      config/oss_aliyun.go
  43. 13 0
      config/oss_aws.go
  44. 9 0
      config/oss_huawei.go
  45. 6 0
      config/oss_local.go
  46. 11 0
      config/oss_qiniu.go
  47. 10 0
      config/oss_tencent.go
  48. 7 0
      config/redis.go
  49. 12 0
      config/system.go
  50. 13 0
      config/timer.go
  51. 60 0
      config/zap.go
  52. 9 0
      core/internal/constant.go
  53. 29 0
      core/internal/file_rotatelogs.go
  54. 107 0
      core/internal/zap.go
  55. 49 0
      core/server.go
  56. 19 0
      core/server_other.go
  57. 21 0
      core/server_win.go
  58. 79 0
      core/viper.go
  59. 28 0
      core/zap.go
  60. 7701 0
      docs/docs.go
  61. 4656 0
      docs/swagger.yaml
  62. 51 0
      global/global.go
  63. 14 0
      global/model.go
  64. 60 0
      go.mod
  65. 849 0
      go.sum
  66. 32 0
      initialize/db_list.go
  67. 99 0
      initialize/ensure_tables.go
  68. 62 0
      initialize/gorm.go
  69. 52 0
      initialize/gorm_mysql.go
  70. 50 0
      initialize/gorm_pgsql.go
  71. 55 0
      initialize/internal/gorm.go
  72. 35 0
      initialize/internal/logger.go
  73. 28 0
      initialize/plugin.go
  74. 26 0
      initialize/redis.go
  75. 10 0
      initialize/register_init.go
  76. 86 0
      initialize/router.go
  77. 49 0
      initialize/timer.go
  78. 22 0
      initialize/validator.go
  79. 36 0
      main.go
  80. 34 0
      middleware/casbin_rbac.go
  81. 73 0
      middleware/cors.go
  82. 60 0
      middleware/email.go
  83. 61 0
      middleware/error.go
  84. 76 0
      middleware/jwt.go
  85. 92 0
      middleware/limit_ip.go
  86. 27 0
      middleware/loadtls.go
  87. 87 0
      middleware/logger.go
  88. 22 0
      middleware/need_init.go
  89. 135 0
      middleware/operation.go
  90. 28 0
      model/common/request/common.go
  91. 8 0
      model/common/response/common.go
  92. 55 0
      model/common/response/response.go
  93. 24 0
      model/example/exa_breakpoint_continue.go
  94. 15 0
      model/example/exa_customer.go
  95. 8 0
      model/example/exa_excel.go
  96. 17 0
      model/example/exa_file_upload_download.go
  97. 11 0
      model/example/response/exa_breakpoint_continue.go
  98. 7 0
      model/example/response/exa_customer.go
  99. 7 0
      model/example/response/exa_file_upload_download.go
  100. 0 0
      model/log/log_coding.go

+ 44 - 0
.gitignore

@@ -0,0 +1,44 @@
+### This project temple
+work-together
+
+### project
+assetFiles
+
+### Beego temple
+
+## beego api
+
+commentsRouter_.go
+commentsRouter_controllers.go
+swagger.json
+swagger.yml
+
+### Golang build temple
+
+build/
+*.exe
+*.tmp
+*.exe~
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+### godep
+-
+#vendor/
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+/captures
+/.idea
+/log/*
+log-server
+config.docker.yaml

+ 24 - 0
Dockerfile

@@ -0,0 +1,24 @@
+FROM golang:alpine as builder
+
+WORKDIR /go/src/log-server
+COPY . .
+
+RUN go env -w GO111MODULE=on \
+    && go env -w GOPROXY=https://goproxy.cn,direct \
+    && go env -w CGO_ENABLED=0 \
+    && go env \
+    && go mod tidy \
+    && go build -o server .
+
+FROM alpine:latest
+
+LABEL MAINTAINER="SliverHorn@sliver_horn@qq.com"
+
+WORKDIR /go/src/log-server
+
+COPY --from=0 /go/src/log-server/server ./
+COPY --from=0 /go/src/log-server/resource ./resource/
+COPY --from=0 /go/src/log-server/config.docker.yaml ./
+
+EXPOSE 8888
+ENTRYPOINT ./server -c config.docker.yaml

+ 58 - 0
README.md

@@ -0,0 +1,58 @@
+## server项目结构
+
+```shell
+├── api
+│   └── v1
+├── config
+├── core
+├── docs
+├── global
+├── initialize
+│   └── internal
+├── middleware
+├── model
+│   ├── request
+│   └── response
+├── packfile
+├── resource
+│   ├── excel
+│   ├── page
+│   └── template
+├── router
+├── service
+├── source
+└── utils
+    ├── timer
+    └── upload
+```
+
+| 文件夹       | 说明                    | 描述                        |
+| ------------ | ----------------------- | --------------------------- |
+| `api`        | api层                   | api层 |
+| `--v1`       | v1版本接口              | v1版本接口                  |
+| `config`     | 配置包                  | config.yaml对应的配置结构体 |
+| `core`       | 核心文件                | 核心组件(zap, viper, server)的初始化 |
+| `docs`       | swagger文档目录         | swagger文档目录 |
+| `global`     | 全局对象                | 全局对象 |
+| `initialize` | 初始化 | router,redis,gorm,validator, timer的初始化 |
+| `--internal` | 初始化内部函数 | gorm 的 longger 自定义,在此文件夹的函数只能由 `initialize` 层进行调用 |
+| `middleware` | 中间件层 | 用于存放 `gin` 中间件代码 |
+| `model`      | 模型层                  | 模型对应数据表              |
+| `--request`  | 入参结构体              | 接收前端发送到后端的数据。  |
+| `--response` | 出参结构体              | 返回给前端的数据结构体      |
+| `packfile`   | 静态文件打包            | 静态文件打包 |
+| `resource`   | 静态资源文件夹          | 负责存放静态文件                |
+| `--excel` | excel导入导出默认路径 | excel导入导出默认路径 |
+| `--page` | 表单生成器 | 表单生成器 打包后的dist |
+| `--template` | 模板 | 模板文件夹,存放的是代码生成器的模板 |
+| `router`     | 路由层                  | 路由层 |
+| `service`    | service层               | 存放业务逻辑问题 |
+| `source` | source层 | 存放初始化数据的函数 |
+| `utils`      | 工具包                  | 工具函数封装            |
+| `--timer` | timer | 定时器接口封装 |
+| `--upload`      | oss                  | oss接口封装        |
+
+##在项目目录下运行
+go mod tidy
+
+go run main.go

+ 15 - 0
api/v1/enter.go

@@ -0,0 +1,15 @@
+package v1
+
+import (
+	"log-server/api/v1/example"
+	"log-server/api/v1/log"
+	"log-server/api/v1/system"
+)
+
+type ApiGroup struct {
+	SystemApiGroup  system.ApiGroup
+	ExampleApiGroup example.ApiGroup
+	LogApiGroup     log.GroupLog
+}
+
+var ApiGroupApp = new(ApiGroup)

+ 15 - 0
api/v1/example/enter.go

@@ -0,0 +1,15 @@
+package example
+
+import "log-server/service"
+
+type ApiGroup struct {
+	ExcelApi
+	CustomerApi
+	FileUploadAndDownloadApi
+}
+
+var (
+	excelService                 = service.ServiceGroupApp.ExampleServiceGroup.ExcelService
+	customerService              = service.ServiceGroupApp.ExampleServiceGroup.CustomerService
+	fileUploadAndDownloadService = service.ServiceGroupApp.ExampleServiceGroup.FileUploadAndDownloadService
+)

+ 142 - 0
api/v1/example/exa_breakpoint_continue.go

@@ -0,0 +1,142 @@
+package example
+
+import (
+	"fmt"
+	"io/ioutil"
+	"mime/multipart"
+	"strconv"
+
+	"log-server/model/example"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	exampleRes "log-server/model/example/response"
+	"log-server/utils"
+)
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 断点续传到服务器
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "an example for breakpoint resume, 断点续传示例"
+// @Success 200 {object} response.Response{msg=string} "断点续传到服务器"
+// @Router /fileUploadAndDownload/breakpointContinue [post]
+func (b *FileUploadAndDownloadApi) BreakpointContinue(c *gin.Context) {
+	fileMd5 := c.Request.FormValue("fileMd5")
+	fileName := c.Request.FormValue("fileName")
+	chunkMd5 := c.Request.FormValue("chunkMd5")
+	chunkNumber, _ := strconv.Atoi(c.Request.FormValue("chunkNumber"))
+	chunkTotal, _ := strconv.Atoi(c.Request.FormValue("chunkTotal"))
+	_, FileHeader, err := c.Request.FormFile("file")
+	if err != nil {
+		global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
+		response.FailWithMessage("接收文件失败", c)
+		return
+	}
+	f, err := FileHeader.Open()
+	if err != nil {
+		global.GVA_LOG.Error("文件读取失败!", zap.Error(err))
+		response.FailWithMessage("文件读取失败", c)
+		return
+	}
+	defer func(f multipart.File) {
+		err := f.Close()
+		if err != nil {
+			fmt.Println(err)
+		}
+	}(f)
+	cen, _ := ioutil.ReadAll(f)
+	if !utils.CheckMd5(cen, chunkMd5) {
+		global.GVA_LOG.Error("检查md5失败!", zap.Error(err))
+		response.FailWithMessage("检查md5失败", c)
+		return
+	}
+	file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
+	if err != nil {
+		global.GVA_LOG.Error("查找或创建记录失败!", zap.Error(err))
+		response.FailWithMessage("查找或创建记录失败", c)
+		return
+	}
+	pathC, err := utils.BreakPointContinue(cen, fileName, chunkNumber, chunkTotal, fileMd5)
+	if err != nil {
+		global.GVA_LOG.Error("断点续传失败!", zap.Error(err))
+		response.FailWithMessage("断点续传失败", c)
+		return
+	}
+
+	if err = fileUploadAndDownloadService.CreateFileChunk(file.ID, pathC, chunkNumber); err != nil {
+		global.GVA_LOG.Error("创建文件记录失败!", zap.Error(err))
+		response.FailWithMessage("创建文件记录失败", c)
+		return
+	}
+	response.OkWithMessage("切片创建成功", c)
+}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 查找文件
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "Find the file, 查找文件"
+// @Success 200 {object} response.Response{data=exampleRes.FileResponse,msg=string} "查找文件,返回包括文件详情"
+// @Router /fileUploadAndDownload/findFile [post]
+func (b *FileUploadAndDownloadApi) FindFile(c *gin.Context) {
+	fileMd5 := c.Query("fileMd5")
+	fileName := c.Query("fileName")
+	chunkTotal, _ := strconv.Atoi(c.Query("chunkTotal"))
+	file, err := fileUploadAndDownloadService.FindOrCreateFile(fileMd5, fileName, chunkTotal)
+	if err != nil {
+		global.GVA_LOG.Error("查找失败!", zap.Error(err))
+		response.FailWithMessage("查找失败", c)
+	} else {
+		response.OkWithDetailed(exampleRes.FileResponse{File: file}, "查找成功", c)
+	}
+}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 创建文件
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "上传文件完成"
+// @Success 200 {object} response.Response{data=exampleRes.FilePathResponse,msg=string} "创建文件,返回包括文件路径"
+// @Router /fileUploadAndDownload/findFile [post]
+func (b *FileUploadAndDownloadApi) BreakpointContinueFinish(c *gin.Context) {
+	fileMd5 := c.Query("fileMd5")
+	fileName := c.Query("fileName")
+	filePath, err := utils.MakeFile(fileName, fileMd5)
+	if err != nil {
+		global.GVA_LOG.Error("文件创建失败!", zap.Error(err))
+		response.FailWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建失败", c)
+	} else {
+		response.OkWithDetailed(exampleRes.FilePathResponse{FilePath: filePath}, "文件创建成功", c)
+	}
+}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 删除切片
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "删除缓存切片"
+// @Success 200 {object} response.Response{msg=string} "删除切片"
+// @Router /fileUploadAndDownload/removeChunk [post]
+func (b *FileUploadAndDownloadApi) RemoveChunk(c *gin.Context) {
+	var file example.ExaFile
+	_ = c.ShouldBindJSON(&file)
+	err := utils.RemoveChunk(file.FileMd5)
+	if err != nil {
+		global.GVA_LOG.Error("缓存切片删除失败!", zap.Error(err))
+		return
+	}
+	err = fileUploadAndDownloadService.DeleteFileChunk(file.FileMd5, file.FilePath)
+	if err != nil {
+		global.GVA_LOG.Error(err.Error(), zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	} else {
+		response.OkWithMessage("缓存切片删除成功", c)
+	}
+}

+ 142 - 0
api/v1/example/exa_customer.go

@@ -0,0 +1,142 @@
+package example
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/example"
+	exampleRes "log-server/model/example/response"
+	"log-server/utils"
+)
+
+type CustomerApi struct{}
+
+// @Tags ExaCustomer
+// @Summary 创建客户
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body example.ExaCustomer true "客户用户名, 客户手机号码"
+// @Success 200 {object} response.Response{msg=string} "创建客户"
+// @Router /customer/customer [post]
+func (e *CustomerApi) CreateExaCustomer(c *gin.Context) {
+	var customer example.ExaCustomer
+	_ = c.ShouldBindJSON(&customer)
+	if err := utils.Verify(customer, utils.CustomerVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	customer.SysUserID = utils.GetUserID(c)
+	customer.SysUserAuthorityID = utils.GetUserAuthorityId(c)
+	if err := customerService.CreateExaCustomer(customer); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags ExaCustomer
+// @Summary 删除客户
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body example.ExaCustomer true "客户ID"
+// @Success 200 {object} response.Response{msg=string} "删除客户"
+// @Router /customer/customer [delete]
+func (e *CustomerApi) DeleteExaCustomer(c *gin.Context) {
+	var customer example.ExaCustomer
+	_ = c.ShouldBindJSON(&customer)
+	if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := customerService.DeleteExaCustomer(customer); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags ExaCustomer
+// @Summary 更新客户信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body example.ExaCustomer true "客户ID, 客户信息"
+// @Success 200 {object} response.Response{msg=string} "更新客户信息"
+// @Router /customer/customer [put]
+func (e *CustomerApi) UpdateExaCustomer(c *gin.Context) {
+	var customer example.ExaCustomer
+	_ = c.ShouldBindJSON(&customer)
+	if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := utils.Verify(customer, utils.CustomerVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := customerService.UpdateExaCustomer(&customer); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// @Tags ExaCustomer
+// @Summary 获取单一客户信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query example.ExaCustomer true "客户ID"
+// @Success 200 {object} response.Response{data=exampleRes.ExaCustomerResponse,msg=string} "获取单一客户信息,返回包括客户详情"
+// @Router /customer/customer [get]
+func (e *CustomerApi) GetExaCustomer(c *gin.Context) {
+	var customer example.ExaCustomer
+	_ = c.ShouldBindQuery(&customer)
+	if err := utils.Verify(customer.GVA_MODEL, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	data, err := customerService.GetExaCustomer(customer.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(exampleRes.ExaCustomerResponse{Customer: data}, "获取成功", c)
+	}
+}
+
+// @Tags ExaCustomer
+// @Summary 分页获取权限客户列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query request.PageInfo true "页码, 每页大小"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取权限客户列表,返回包括列表,总数,页码,每页数量"
+// @Router /customer/customerList [get]
+func (e *CustomerApi) GetExaCustomerList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	_ = c.ShouldBindQuery(&pageInfo)
+	if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	customerList, total, err := customerService.GetCustomerInfoList(utils.GetUserAuthorityId(c), pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败"+err.Error(), c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     customerList,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 111 - 0
api/v1/example/exa_excel.go

@@ -0,0 +1,111 @@
+package example
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/example"
+	"os"
+	"strings"
+)
+
+type ExcelApi struct{}
+
+// /excel/importExcel 接口,与upload接口作用类似,只是把文件存到resource/excel目录下,用于导入Excel时存放Excel文件(ExcelImport.xlsx)
+// /excel/loadExcel接口,用于读取resource/excel目录下的文件((ExcelImport.xlsx)并加载为[]model.SysBaseMenu类型的示例数据
+// /excel/exportExcel 接口,用于读取前端传来的tableData,生成Excel文件并返回
+// /excel/downloadTemplate 接口,用于下载resource/excel目录下的 ExcelTemplate.xlsx 文件,作为导入的模板
+
+// @Tags excel
+// @Summary 导出Excel
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce  application/octet-stream
+// @Param data body example.ExcelInfo true "导出Excel文件信息"
+// @Success 200
+// @Router /excel/exportExcel [post]
+func (e *ExcelApi) ExportExcel(c *gin.Context) {
+	var excelInfo example.ExcelInfo
+	_ = c.ShouldBindJSON(&excelInfo)
+	if strings.Index(excelInfo.FileName, "..") > -1 {
+		response.FailWithMessage("包含非法字符", c)
+		return
+	}
+	filePath := global.GVA_CONFIG.Excel.Dir + excelInfo.FileName
+	err := excelService.ParseInfoList2Excel(excelInfo.InfoList, filePath)
+	if err != nil {
+		global.GVA_LOG.Error("转换Excel失败!", zap.Error(err))
+		response.FailWithMessage("转换Excel失败", c)
+		return
+	}
+	c.Writer.Header().Add("success", "true")
+	c.File(filePath)
+}
+
+// @Tags excel
+// @Summary 导入Excel文件
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "导入Excel文件"
+// @Success 200 {object} response.Response{msg=string} "导入Excel文件"
+// @Router /excel/importExcel [post]
+func (e *ExcelApi) ImportExcel(c *gin.Context) {
+	_, header, err := c.Request.FormFile("file")
+	if err != nil {
+		global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
+		response.FailWithMessage("接收文件失败", c)
+		return
+	}
+	_ = c.SaveUploadedFile(header, global.GVA_CONFIG.Excel.Dir+"ExcelImport.xlsx")
+	response.OkWithMessage("导入成功", c)
+}
+
+// @Tags excel
+// @Summary 加载Excel数据
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "加载Excel数据,返回包括列表,总数,页码,每页数量"
+// @Router /excel/loadExcel [get]
+func (e *ExcelApi) LoadExcel(c *gin.Context) {
+	menus, err := excelService.ParseExcel2InfoList()
+	if err != nil {
+		global.GVA_LOG.Error("加载数据失败!", zap.Error(err))
+		response.FailWithMessage("加载数据失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     menus,
+		Total:    int64(len(menus)),
+		Page:     1,
+		PageSize: 999,
+	}, "加载数据成功", c)
+}
+
+// @Tags excel
+// @Summary 下载模板
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param fileName query string true "模板名称"
+// @Success 200
+// @Router /excel/downloadTemplate [get]
+func (e *ExcelApi) DownloadTemplate(c *gin.Context) {
+	fileName := c.Query("fileName")
+	filePath := global.GVA_CONFIG.Excel.Dir + fileName
+
+	fi, err := os.Stat(filePath)
+	if err != nil {
+		global.GVA_LOG.Error("文件不存在!", zap.Error(err))
+		response.FailWithMessage("文件不存在", c)
+		return
+	}
+	if fi.IsDir() {
+		global.GVA_LOG.Error("不支持下载文件夹!", zap.Error(err))
+		response.FailWithMessage("不支持下载文件夹", c)
+		return
+	}
+	c.Writer.Header().Add("success", "true")
+	c.File(filePath)
+}

+ 94 - 0
api/v1/example/exa_file_upload_download.go

@@ -0,0 +1,94 @@
+package example
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/example"
+	exampleRes "log-server/model/example/response"
+)
+
+type FileUploadAndDownloadApi struct{}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 上传文件示例
+// @Security ApiKeyAuth
+// @accept multipart/form-data
+// @Produce  application/json
+// @Param file formData file true "上传文件示例"
+// @Success 200 {object} response.Response{data=exampleRes.ExaFileResponse,msg=string} "上传文件示例,返回包括文件详情"
+// @Router /fileUploadAndDownload/upload [post]
+func (b *FileUploadAndDownloadApi) UploadFile(c *gin.Context) {
+	var file example.ExaFileUploadAndDownload
+	noSave := c.DefaultQuery("noSave", "0")
+	_, header, err := c.Request.FormFile("file")
+	if err != nil {
+		global.GVA_LOG.Error("接收文件失败!", zap.Error(err))
+		response.FailWithMessage("接收文件失败", c)
+		return
+	}
+	file, err = fileUploadAndDownloadService.UploadFile(header, noSave) // 文件上传后拿到文件路径
+	if err != nil {
+		global.GVA_LOG.Error("修改数据库链接失败!", zap.Error(err))
+		response.FailWithMessage("修改数据库链接失败", c)
+		return
+	}
+	response.OkWithDetailed(exampleRes.ExaFileResponse{File: file}, "上传成功", c)
+}
+
+// EditFileName 编辑文件名或者备注
+func (b *FileUploadAndDownloadApi) EditFileName(c *gin.Context) {
+	var file example.ExaFileUploadAndDownload
+	_ = c.ShouldBindJSON(&file)
+	if err := fileUploadAndDownloadService.EditFileName(file); err != nil {
+		global.GVA_LOG.Error("编辑失败!", zap.Error(err))
+		response.FailWithMessage("编辑失败", c)
+		return
+	}
+	response.OkWithMessage("编辑成功", c)
+}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 删除文件
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body example.ExaFileUploadAndDownload true "传入文件里面id即可"
+// @Success 200 {object} response.Response{msg=string} "删除文件"
+// @Router /fileUploadAndDownload/deleteFile [post]
+func (b *FileUploadAndDownloadApi) DeleteFile(c *gin.Context) {
+	var file example.ExaFileUploadAndDownload
+	_ = c.ShouldBindJSON(&file)
+	if err := fileUploadAndDownloadService.DeleteFile(file); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// @Tags ExaFileUploadAndDownload
+// @Summary 分页文件列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.PageInfo true "页码, 每页大小"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页文件列表,返回包括列表,总数,页码,每页数量"
+// @Router /fileUploadAndDownload/getFileList [post]
+func (b *FileUploadAndDownloadApi) GetFileList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	_ = c.ShouldBindJSON(&pageInfo)
+	list, total, err := fileUploadAndDownloadService.GetFileRecordInfoList(pageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 19 - 0
api/v1/log/enter.go

@@ -0,0 +1,19 @@
+package log
+
+import (
+	"log-server/service"
+)
+
+type GroupLog struct {
+	ApiLogCoding
+	ApiEnvLog
+	ApiLoging
+}
+
+var (
+	ServiceLogCoding       = service.ServiceGroupApp.LogServiceGroup.ServiceLogCoding
+	ServiceLogEnv          = service.ServiceGroupApp.LogServiceGroup.ServiceLogEnv
+	ServiceLogList         = service.ServiceGroupApp.LogServiceGroup.ServiceLogList
+	ServiceReportPointsLog = service.ServiceGroupApp.LogServiceGroup.ServiceReportPointsLog
+	ServiceStatisticsLog   = service.ServiceGroupApp.LogServiceGroup.ServiceStatisticsLog
+)

+ 118 - 0
api/v1/log/log_coding.go

@@ -0,0 +1,118 @@
+package log
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/log/request"
+	"log-server/utils"
+	"strconv"
+)
+
+type ApiLogCoding struct {
+}
+
+// @Tags logCoding
+// @Summary 创建基础日志码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.CodingLogRequest true "parent_id父id, coding日志码, describe日志描述"
+// @Success 200 {object} response.Response{msg=string} "创建基础日志码"
+// @Router /logCoding/create [post]
+func (s *ApiLogCoding) CreateCoding(c *gin.Context) {
+	var api request.CodingLogRequest
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.LogCodingVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	codeStr := strconv.Itoa(api.Coding)
+	if len(codeStr) != 7 {
+		response.FailWithMessage("编号是7位", c)
+		return
+	}
+	if err := ServiceLogCoding.CreateCoding(api); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags logCoding
+// @Summary 更新基础日志码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.CodingLogRequest true "coding日志码, describe日志描述"
+// @Success 200 {object} response.Response{msg=string} "更新基础日志码"
+// @Router /logCoding/update [post]
+func (s *ApiLogCoding) UpdateCoding(c *gin.Context) {
+	var api request.CodingLogRequest
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.LogCodingVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	codeStr := strconv.Itoa(api.Coding)
+	if len(codeStr) != 7 {
+		response.FailWithMessage("编号是7位", c)
+		return
+	}
+	if api.Id == 0 {
+		response.FailWithMessage("参数id没有", c)
+		return
+	}
+	if err := ServiceLogCoding.UpdateLogCoding(api); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags logCoding
+// @Summary 根据id获取coding
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "根据id获取coding"
+// @Success 200 {object} response.Response{data=response.GetLogCodingReply} "根据id获取coding,返回包括coding详情"
+// @Router /logCoding/getCodeById [post]
+func (s *ApiLogCoding) GetCodeById(c *gin.Context) {
+	var idInfo request.GetById
+	_ = c.ShouldBindJSON(&idInfo)
+	if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	api, err := ServiceLogCoding.GetCodeById(idInfo.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(api, "获取成功", c)
+	}
+}
+
+// @Tags logCoding
+// @Summary 根据id获取coding
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "根据id获取coding"
+// @Success 200 {object} response.Response{data=response.GetLogCodingReply} "根据id获取coding,返回包括coding详情"
+// @Router /logCoding/getCodeList [post]
+func (s *ApiLogCoding) GetCodeList(c *gin.Context) {
+	var paramsInfo request.GetCodeListRequest
+	_ = c.ShouldBindJSON(&paramsInfo)
+	api, _, err := ServiceLogCoding.GetCodeLogList(paramsInfo.OrderKey, paramsInfo.Desc)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(api, "获取成功", c)
+	}
+}

+ 107 - 0
api/v1/log/log_env.go

@@ -0,0 +1,107 @@
+package log
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/log"
+	"log-server/model/log/request"
+	"log-server/utils"
+)
+
+type ApiEnvLog struct {
+}
+
+// @Tags logEnv
+// @Summary 创建基础环境码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.EnvLogRequest true " coding环境编码, describe环境描述""
+// @Success 200 {object} response.Response{msg=string} "创建环境编码"
+// @Router /logEnv/create [post]
+func (s *ApiEnvLog) CreateEnv(c *gin.Context) {
+	var api log.EnvLog
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.LogCodingVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := ServiceLogEnv.CreateEnv(api); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags logEnv
+// @Summary 更新环境编码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.EnvLogRequest true "coding环境编码, describe环境描述"
+// @Success 200 {object} response.Response{msg=string} "更新环境编码"
+// @Router /logEnv/update [post]
+func (s *ApiEnvLog) UpdateEnv(c *gin.Context) {
+	var api request.EnvLogRequest
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.LogCodingVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if api.Id == 0 {
+		response.FailWithMessage("参数id没有", c)
+		return
+	}
+	if err := ServiceLogEnv.UpdateEnv(api); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags logEnv
+// @Summary 根据id获取log env
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "根据id获取log env"
+// @Success 200 {object} response.Response{data=response.EnvLogReply} "根据id获取返回log env详情"
+// @Router /logEnv/getCodeById [post]
+func (s *ApiEnvLog) GetEnvById(c *gin.Context) {
+	var idInfo request.GetById
+	_ = c.ShouldBindJSON(&idInfo)
+	if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	api, err := ServiceLogEnv.GetEnvById(idInfo.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(api, "获取成功", c)
+	}
+}
+
+// @Tags logEnv
+// @Summary 根据id获取log env
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=[]response.EnvLogReply} "返回log env列表"
+// @Router /logEnv/getEnvList [post]
+func (s *ApiEnvLog) GetEnvList(c *gin.Context) {
+	var paramsInfo request.GetCodeListRequest
+	_ = c.ShouldBindJSON(&paramsInfo)
+	api, _, err := ServiceLogEnv.GetEnvList(paramsInfo.OrderKey, paramsInfo.Desc)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(api, "获取成功", c)
+	}
+}

ファイルの差分が大きいため隠しています
+ 218 - 0
api/v1/log/loging.go


+ 38 - 0
api/v1/system/enter.go

@@ -0,0 +1,38 @@
+package system
+
+import "log-server/service"
+
+type ApiGroup struct {
+	DBApi
+	JwtApi
+	BaseApi
+	SystemApi
+	CasbinApi
+	AutoCodeApi
+	SystemApiApi
+	AuthorityApi
+	DictionaryApi
+	AuthorityMenuApi
+	OperationRecordApi
+	AutoCodeHistoryApi
+	DictionaryDetailApi
+	AuthorityBtnApi
+}
+
+var (
+	apiService              = service.ServiceGroupApp.SystemServiceGroup.ApiService
+	jwtService              = service.ServiceGroupApp.SystemServiceGroup.JwtService
+	menuService             = service.ServiceGroupApp.SystemServiceGroup.MenuService
+	userService             = service.ServiceGroupApp.SystemServiceGroup.UserService
+	initDBService           = service.ServiceGroupApp.SystemServiceGroup.InitDBService
+	casbinService           = service.ServiceGroupApp.SystemServiceGroup.CasbinService
+	autoCodeService         = service.ServiceGroupApp.SystemServiceGroup.AutoCodeService
+	baseMenuService         = service.ServiceGroupApp.SystemServiceGroup.BaseMenuService
+	authorityService        = service.ServiceGroupApp.SystemServiceGroup.AuthorityService
+	dictionaryService       = service.ServiceGroupApp.SystemServiceGroup.DictionaryService
+	systemConfigService     = service.ServiceGroupApp.SystemServiceGroup.SystemConfigService
+	operationRecordService  = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
+	autoCodeHistoryService  = service.ServiceGroupApp.SystemServiceGroup.AutoCodeHistoryService
+	dictionaryDetailService = service.ServiceGroupApp.SystemServiceGroup.DictionaryDetailService
+	authorityBtnService     = service.ServiceGroupApp.SystemServiceGroup.AuthorityBtnService
+)

+ 172 - 0
api/v1/system/sys_api.go

@@ -0,0 +1,172 @@
+package system
+
+import (
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemReq "log-server/model/system/request"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type SystemApiApi struct{}
+
+// @Tags SysApi
+// @Summary 创建基础api
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
+// @Success 200 {object} response.Response{msg=string} "创建基础api"
+// @Router /api/createApi [post]
+func (s *SystemApiApi) CreateApi(c *gin.Context) {
+	var api system.SysApi
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.ApiVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := apiService.CreateApi(api); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 删除api
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysApi true "ID"
+// @Success 200 {object} response.Response{msg=string} "删除api"
+// @Router /api/deleteApi [post]
+func (s *SystemApiApi) DeleteApi(c *gin.Context) {
+	var api system.SysApi
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api.GVA_MODEL, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := apiService.DeleteApi(api); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 分页获取API列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.SearchApiParams true "分页获取API列表"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取API列表,返回包括列表,总数,页码,每页数量"
+// @Router /api/getApiList [post]
+func (s *SystemApiApi) GetApiList(c *gin.Context) {
+	var pageInfo systemReq.SearchApiParams
+	_ = c.ShouldBindJSON(&pageInfo)
+	if err := utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if list, total, err := apiService.GetAPIInfoList(pageInfo.SysApi, pageInfo.PageInfo, pageInfo.OrderKey, pageInfo.Desc); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 根据id获取api
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "根据id获取api"
+// @Success 200 {object} response.Response{data=systemRes.SysAPIResponse} "根据id获取api,返回包括api详情"
+// @Router /api/getApiById [post]
+func (s *SystemApiApi) GetApiById(c *gin.Context) {
+	var idInfo request.GetById
+	_ = c.ShouldBindJSON(&idInfo)
+	if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	api, err := apiService.GetApiById(idInfo.ID)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysAPIResponse{Api: api}, "获取成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 修改基础api
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysApi true "api路径, api中文描述, api组, 方法"
+// @Success 200 {object} response.Response{msg=string} "修改基础api"
+// @Router /api/updateApi [post]
+func (s *SystemApiApi) UpdateApi(c *gin.Context) {
+	var api system.SysApi
+	_ = c.ShouldBindJSON(&api)
+	if err := utils.Verify(api, utils.ApiVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := apiService.UpdateApi(api); err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败", c)
+	} else {
+		response.OkWithMessage("修改成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 获取所有的Api 不分页
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=systemRes.SysAPIListResponse,msg=string} "获取所有的Api 不分页,返回包括api列表"
+// @Router /api/getAllApis [post]
+func (s *SystemApiApi) GetAllApis(c *gin.Context) {
+	if apis, err := apiService.GetAllApis(); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysAPIListResponse{Apis: apis}, "获取成功", c)
+	}
+}
+
+// @Tags SysApi
+// @Summary 删除选中Api
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.IdsReq true "ID"
+// @Success 200 {object} response.Response{msg=string} "删除选中Api"
+// @Router /api/deleteApisByIds [delete]
+func (s *SystemApiApi) DeleteApisByIds(c *gin.Context) {
+	var ids request.IdsReq
+	_ = c.ShouldBindJSON(&ids)
+	if err := apiService.DeleteApisByIds(ids); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}

+ 165 - 0
api/v1/system/sys_authority.go

@@ -0,0 +1,165 @@
+package system
+
+import (
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemReq "log-server/model/system/request"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type AuthorityApi struct{}
+
+// @Tags Authority
+// @Summary 创建角色
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
+// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "创建角色,返回包括系统角色详情"
+// @Router /authority/createAuthority [post]
+func (a *AuthorityApi) CreateAuthority(c *gin.Context) {
+	var authority system.SysAuthority
+	_ = c.ShouldBindJSON(&authority)
+	if err := utils.Verify(authority, utils.AuthorityVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if authBack, err := authorityService.CreateAuthority(authority); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败"+err.Error(), c)
+	} else {
+		_ = menuService.AddMenuAuthority(systemReq.DefaultMenu(), authority.AuthorityId)
+		_ = casbinService.UpdateCasbin(authority.AuthorityId, systemReq.DefaultCasbin())
+		response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "创建成功", c)
+	}
+}
+
+// @Tags Authority
+// @Summary 拷贝角色
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body response.SysAuthorityCopyResponse true "旧角色id, 新权限id, 新权限名, 新父角色id"
+// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "拷贝角色,返回包括系统角色详情"
+// @Router /authority/copyAuthority [post]
+func (a *AuthorityApi) CopyAuthority(c *gin.Context) {
+	var copyInfo systemRes.SysAuthorityCopyResponse
+	_ = c.ShouldBindJSON(&copyInfo)
+	if err := utils.Verify(copyInfo, utils.OldAuthorityVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := utils.Verify(copyInfo.Authority, utils.AuthorityVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if authBack, err := authorityService.CopyAuthority(copyInfo); err != nil {
+		global.GVA_LOG.Error("拷贝失败!", zap.Error(err))
+		response.FailWithMessage("拷贝失败"+err.Error(), c)
+	} else {
+		response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authBack}, "拷贝成功", c)
+	}
+}
+
+// @Tags Authority
+// @Summary 删除角色
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAuthority true "删除角色"
+// @Success 200 {object} response.Response{msg=string} "删除角色"
+// @Router /authority/deleteAuthority [post]
+func (a *AuthorityApi) DeleteAuthority(c *gin.Context) {
+	var authority system.SysAuthority
+	_ = c.ShouldBindJSON(&authority)
+	if err := utils.Verify(authority, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := authorityService.DeleteAuthority(&authority); err != nil { // 删除角色之前需要判断是否有用户正在使用此角色
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败"+err.Error(), c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags Authority
+// @Summary 更新角色信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAuthority true "权限id, 权限名, 父角色id"
+// @Success 200 {object} response.Response{data=systemRes.SysAuthorityResponse,msg=string} "更新角色信息,返回包括系统角色详情"
+// @Router /authority/updateAuthority [post]
+func (a *AuthorityApi) UpdateAuthority(c *gin.Context) {
+	var auth system.SysAuthority
+	_ = c.ShouldBindJSON(&auth)
+	if err := utils.Verify(auth, utils.AuthorityVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if authority, err := authorityService.UpdateAuthority(auth); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败"+err.Error(), c)
+	} else {
+		response.OkWithDetailed(systemRes.SysAuthorityResponse{Authority: authority}, "更新成功", c)
+	}
+}
+
+// @Tags Authority
+// @Summary 分页获取角色列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.PageInfo true "页码, 每页大小"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取角色列表,返回包括列表,总数,页码,每页数量"
+// @Router /authority/getAuthorityList [post]
+func (a *AuthorityApi) GetAuthorityList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	_ = c.ShouldBindJSON(&pageInfo)
+	if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if list, total, err := authorityService.GetAuthorityInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败"+err.Error(), c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}
+
+// @Tags Authority
+// @Summary 设置角色资源权限
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAuthority true "设置角色资源权限"
+// @Success 200 {object} response.Response{msg=string} "设置角色资源权限"
+// @Router /authority/setDataAuthority [post]
+func (a *AuthorityApi) SetDataAuthority(c *gin.Context) {
+	var auth system.SysAuthority
+	_ = c.ShouldBindJSON(&auth)
+	if err := utils.Verify(auth, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := authorityService.SetDataAuthority(auth); err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败"+err.Error(), c)
+	} else {
+		response.OkWithMessage("设置成功", c)
+	}
+}

+ 66 - 0
api/v1/system/sys_authority_btn.go

@@ -0,0 +1,66 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system/request"
+)
+
+type AuthorityBtnApi struct{}
+
+// @Tags AuthorityBtn
+// @Summary 获取权限按钮
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
+// @Success 200 {object} response.Response{data=response.SysAuthorityBtnRes,msg=string} "返回列表成功"
+// @Router /authorityBtn/getAuthorityBtn [post]
+func (a *AuthorityBtnApi) GetAuthorityBtn(c *gin.Context) {
+	var req request.SysAuthorityBtnReq
+	_ = c.ShouldBindJSON(&req)
+	if res, err := authorityBtnService.GetAuthorityBtn(req); err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+	} else {
+		response.OkWithDetailed(res, "查询成功", c)
+	}
+}
+
+// @Tags AuthorityBtn
+// @Summary 设置权限按钮
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.SysAuthorityBtnReq true "菜单id, 角色id, 选中的按钮id"
+// @Success 200 {object} response.Response{msg=string} "返回列表成功"
+// @Router /authorityBtn/setAuthorityBtn [post]
+func (a *AuthorityBtnApi) SetAuthorityBtn(c *gin.Context) {
+	var req request.SysAuthorityBtnReq
+	_ = c.ShouldBindJSON(&req)
+	if err := authorityBtnService.SetAuthorityBtn(req); err != nil {
+		global.GVA_LOG.Error("分配失败!", zap.Error(err))
+		response.FailWithMessage("分配失败", c)
+	} else {
+		response.OkWithMessage("分配成功", c)
+	}
+}
+
+// @Tags AuthorityBtn
+// @Summary 设置权限按钮
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{msg=string} "删除成功"
+// @Router /authorityBtn/canRemoveAuthorityBtn [post]
+func (a *AuthorityBtnApi) CanRemoveAuthorityBtn(c *gin.Context) {
+	id := c.Query("id")
+	if err := authorityBtnService.CanRemoveAuthorityBtn(id); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}

+ 242 - 0
api/v1/system/sys_auto_code.go

@@ -0,0 +1,242 @@
+package system
+
+import (
+	"errors"
+	"fmt"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
+	"net/url"
+	"os"
+	"strings"
+
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type AutoCodeApi struct{}
+
+var caser = cases.Title(language.English)
+
+// PreviewTemp
+// @Tags AutoCode
+// @Summary 预览创建后的代码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.AutoCodeStruct true "预览创建代码"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "预览创建后的代码"
+// @Router /autoCode/preview [post]
+func (autoApi *AutoCodeApi) PreviewTemp(c *gin.Context) {
+	var a system.AutoCodeStruct
+	_ = c.ShouldBindJSON(&a)
+	if err := utils.Verify(a, utils.AutoCodeVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	a.KeyWord() // 处理go关键字
+	a.PackageT = caser.String(a.Package)
+	autoCode, err := autoCodeService.PreviewTemp(a)
+	if err != nil {
+		global.GVA_LOG.Error("预览失败!", zap.Error(err))
+		response.FailWithMessage("预览失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"autoCode": autoCode}, "预览成功", c)
+	}
+}
+
+// CreateTemp
+// @Tags AutoCode
+// @Summary 自动代码模板
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.AutoCodeStruct true "创建自动代码"
+// @Success 200 {string} string "{"success":true,"data":{},"msg":"创建成功"}"
+// @Router /autoCode/createTemp [post]
+func (autoApi *AutoCodeApi) CreateTemp(c *gin.Context) {
+	var a system.AutoCodeStruct
+	_ = c.ShouldBindJSON(&a)
+	if err := utils.Verify(a, utils.AutoCodeVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	a.KeyWord() // 处理go关键字
+	var apiIds []uint
+	if a.AutoCreateApiToSql {
+		if ids, err := autoCodeService.AutoCreateApi(&a); err != nil {
+			global.GVA_LOG.Error("自动化创建失败!请自行清空垃圾数据!", zap.Error(err))
+			c.Writer.Header().Add("success", "false")
+			c.Writer.Header().Add("msg", url.QueryEscape("自动化创建失败!请自行清空垃圾数据!"))
+			return
+		} else {
+			apiIds = ids
+		}
+	}
+	a.PackageT = caser.String(a.Package)
+	err := autoCodeService.CreateTemp(a, apiIds...)
+	if err != nil {
+		if errors.Is(err, system.AutoMoveErr) {
+			c.Writer.Header().Add("success", "true")
+			c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
+		} else {
+			c.Writer.Header().Add("success", "false")
+			c.Writer.Header().Add("msg", url.QueryEscape(err.Error()))
+			_ = os.Remove("./ginvueadmin.zip")
+		}
+	} else {
+		c.Writer.Header().Add("Content-Disposition", fmt.Sprintf("attachment; filename=%s", "ginvueadmin.zip")) // fmt.Sprintf("attachment; filename=%s", filename)对下载的文件重命名
+		c.Writer.Header().Add("Content-Type", "application/json")
+		c.Writer.Header().Add("success", "true")
+		c.File("./ginvueadmin.zip")
+		_ = os.Remove("./ginvueadmin.zip")
+	}
+}
+
+// GetDB
+// @Tags AutoCode
+// @Summary 获取当前所有数据库
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前所有数据库"
+// @Router /autoCode/getDatabase [get]
+func (autoApi *AutoCodeApi) GetDB(c *gin.Context) {
+	dbs, err := autoCodeService.Database().GetDB()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"dbs": dbs}, "获取成功", c)
+	}
+}
+
+// GetTables
+// @Tags AutoCode
+// @Summary 获取当前数据库所有表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前数据库所有表"
+// @Router /autoCode/getTables [get]
+func (autoApi *AutoCodeApi) GetTables(c *gin.Context) {
+	dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
+	tables, err := autoCodeService.Database().GetTables(dbName)
+	if err != nil {
+		global.GVA_LOG.Error("查询table失败!", zap.Error(err))
+		response.FailWithMessage("查询table失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"tables": tables}, "获取成功", c)
+	}
+}
+
+// GetColumn
+// @Tags AutoCode
+// @Summary 获取当前表所有字段
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取当前表所有字段"
+// @Router /autoCode/getColumn [get]
+func (autoApi *AutoCodeApi) GetColumn(c *gin.Context) {
+	dbName := c.DefaultQuery("dbName", global.GVA_CONFIG.Mysql.Dbname)
+	tableName := c.Query("tableName")
+	columns, err := autoCodeService.Database().GetColumn(tableName, dbName)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"columns": columns}, "获取成功", c)
+	}
+}
+
+// CreatePackage
+// @Tags AutoCode
+// @Summary 创建package
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAutoCode true "创建package"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
+// @Router /autoCode/createPackage [post]
+func (autoApi *AutoCodeApi) CreatePackage(c *gin.Context) {
+	var a system.SysAutoCode
+	_ = c.ShouldBindJSON(&a)
+	if err := utils.Verify(a, utils.AutoPackageVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	err := autoCodeService.CreateAutoCode(&a)
+	if err != nil {
+		global.GVA_LOG.Error("创建成功!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// GetPackage
+// @Tags AutoCode
+// @Summary 获取package
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建package成功"
+// @Router /autoCode/getPackage [post]
+func (autoApi *AutoCodeApi) GetPackage(c *gin.Context) {
+	pkgs, err := autoCodeService.GetPackage()
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"pkgs": pkgs}, "获取成功", c)
+	}
+}
+
+// DelPackage
+// @Tags AutoCode
+// @Summary 删除package
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAutoCode true "创建package"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "删除package成功"
+// @Router /autoCode/delPackage [post]
+func (autoApi *AutoCodeApi) DelPackage(c *gin.Context) {
+	var a system.SysAutoCode
+	_ = c.ShouldBindJSON(&a)
+	err := autoCodeService.DelPackage(a)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// AutoPlug
+// @Tags AutoCode
+// @Summary 创建插件模板
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysAutoCode true "创建插件模板"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "创建插件模板成功"
+// @Router /autoCode/createPlug [post]
+func (autoApi *AutoCodeApi) AutoPlug(c *gin.Context) {
+	var a system.AutoPlugReq
+	_ = c.ShouldBindJSON(&a)
+	a.Snake = strings.ToLower(a.PlugName)
+	a.NeedModel = a.HasRequest || a.HasResponse
+	err := autoCodeService.CreatePlug(a)
+	if err != nil {
+		global.GVA_LOG.Error("预览失败!", zap.Error(err))
+		response.FailWithMessage("预览失败", c)
+	} else {
+		response.Ok(c)
+	}
+}

+ 98 - 0
api/v1/system/sys_auto_code_history.go

@@ -0,0 +1,98 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	systemReq "log-server/model/system/request"
+)
+
+type AutoCodeHistoryApi struct{}
+
+// First
+// @Tags AutoCode
+// @Summary 获取meta信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "请求参数"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取meta信息"
+// @Router /autoCode/getMeta [post]
+func (a *AutoCodeHistoryApi) First(c *gin.Context) {
+	var info request.GetById
+	_ = c.ShouldBindJSON(&info)
+	data, err := autoCodeHistoryService.First(&info)
+	if err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithDetailed(gin.H{"meta": data}, "获取成功", c)
+}
+
+// Delete
+// @Tags AutoCode
+// @Summary 删除回滚记录
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "请求参数"
+// @Success 200 {object} response.Response{msg=string} "删除回滚记录"
+// @Router /autoCode/delSysHistory [post]
+func (a *AutoCodeHistoryApi) Delete(c *gin.Context) {
+	var info request.GetById
+	_ = c.ShouldBindJSON(&info)
+	err := autoCodeHistoryService.Delete(&info)
+	if err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+		return
+	}
+	response.OkWithMessage("删除成功", c)
+}
+
+// RollBack
+// @Tags AutoCode
+// @Summary 回滚自动生成代码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.RollBack true "请求参数"
+// @Success 200 {object} response.Response{msg=string} "回滚自动生成代码"
+// @Router /autoCode/rollback [post]
+func (a *AutoCodeHistoryApi) RollBack(c *gin.Context) {
+	var info systemReq.RollBack
+	_ = c.ShouldBindJSON(&info)
+	if err := autoCodeHistoryService.RollBack(&info); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	response.OkWithMessage("回滚成功", c)
+}
+
+// GetList
+// @Tags AutoCode
+// @Summary 查询回滚记录
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.SysAutoHistory true "请求参数"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "查询回滚记录,返回包括列表,总数,页码,每页数量"
+// @Router /autoCode/getSysHistory [post]
+func (a *AutoCodeHistoryApi) GetList(c *gin.Context) {
+	var search systemReq.SysAutoHistory
+	_ = c.ShouldBindJSON(&search)
+	list, total, err := autoCodeHistoryService.GetList(search.PageInfo)
+	if err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+		return
+	}
+	response.OkWithDetailed(response.PageResult{
+		List:     list,
+		Total:    total,
+		Page:     search.Page,
+		PageSize: search.PageSize,
+	}, "获取成功", c)
+}

+ 42 - 0
api/v1/system/sys_captcha.go

@@ -0,0 +1,42 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"github.com/mojocn/base64Captcha"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	systemRes "log-server/model/system/response"
+)
+
+// 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码
+// var store = captcha.NewDefaultRedisStore()
+var store = base64Captcha.DefaultMemStore
+
+type BaseApi struct{}
+
+// Captcha
+// @Tags Base
+// @Summary 生成验证码
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=systemRes.SysCaptchaResponse,msg=string} "生成验证码,返回包括随机数id,base64,验证码长度"
+// @Router /base/captcha [post]
+func (b *BaseApi) Captcha(c *gin.Context) {
+	// 字符,公式,验证码配置
+	// 生成默认数字的driver
+	driver := base64Captcha.NewDriverDigit(global.GVA_CONFIG.Captcha.ImgHeight, global.GVA_CONFIG.Captcha.ImgWidth, global.GVA_CONFIG.Captcha.KeyLong, 0.7, 80)
+	// cp := base64Captcha.NewCaptcha(driver, store.UseWithCtx(c))   // v8下使用redis
+	cp := base64Captcha.NewCaptcha(driver, store)
+	if id, b64s, err := cp.Generate(); err != nil {
+		global.GVA_LOG.Error("验证码获取失败!", zap.Error(err))
+		response.FailWithMessage("验证码获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysCaptchaResponse{
+			CaptchaId:     id,
+			PicPath:       b64s,
+			CaptchaLength: global.GVA_CONFIG.Captcha.KeyLong,
+		}, "验证码获取成功", c)
+	}
+}

+ 55 - 0
api/v1/system/sys_casbin.go

@@ -0,0 +1,55 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system/request"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+)
+
+type CasbinApi struct{}
+
+// @Tags Casbin
+// @Summary 更新角色api权限
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
+// @Success 200 {object} response.Response{msg=string} "更新角色api权限"
+// @Router /casbin/UpdateCasbin [post]
+func (cas *CasbinApi) UpdateCasbin(c *gin.Context) {
+	var cmr request.CasbinInReceive
+	_ = c.ShouldBindJSON(&cmr)
+	if err := utils.Verify(cmr, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := casbinService.UpdateCasbin(cmr.AuthorityId, cmr.CasbinInfos); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// @Tags Casbin
+// @Summary 获取权限列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.CasbinInReceive true "权限id, 权限模型列表"
+// @Success 200 {object} response.Response{data=systemRes.PolicyPathResponse,msg=string} "获取权限列表,返回包括casbin详情列表"
+// @Router /casbin/getPolicyPathByAuthorityId [post]
+func (cas *CasbinApi) GetPolicyPathByAuthorityId(c *gin.Context) {
+	var casbin request.CasbinInReceive
+	_ = c.ShouldBindJSON(&casbin)
+	if err := utils.Verify(casbin, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	paths := casbinService.GetPolicyPathByAuthorityId(casbin.AuthorityId)
+	response.OkWithDetailed(systemRes.PolicyPathResponse{Paths: paths}, "获取成功", c)
+}

+ 117 - 0
api/v1/system/sys_dictionary.go

@@ -0,0 +1,117 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	"log-server/model/system/request"
+	"log-server/utils"
+)
+
+type DictionaryApi struct{}
+
+// @Tags SysDictionary
+// @Summary 创建SysDictionary
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionary true "SysDictionary模型"
+// @Success 200 {object} response.Response{msg=string} "创建SysDictionary"
+// @Router /sysDictionary/createSysDictionary [post]
+func (s *DictionaryApi) CreateSysDictionary(c *gin.Context) {
+	var dictionary system.SysDictionary
+	_ = c.ShouldBindJSON(&dictionary)
+	if err := dictionaryService.CreateSysDictionary(dictionary); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags SysDictionary
+// @Summary 删除SysDictionary
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionary true "SysDictionary模型"
+// @Success 200 {object} response.Response{msg=string} "删除SysDictionary"
+// @Router /sysDictionary/deleteSysDictionary [delete]
+func (s *DictionaryApi) DeleteSysDictionary(c *gin.Context) {
+	var dictionary system.SysDictionary
+	_ = c.ShouldBindJSON(&dictionary)
+	if err := dictionaryService.DeleteSysDictionary(dictionary); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags SysDictionary
+// @Summary 更新SysDictionary
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionary true "SysDictionary模型"
+// @Success 200 {object} response.Response{msg=string} "更新SysDictionary"
+// @Router /sysDictionary/updateSysDictionary [put]
+func (s *DictionaryApi) UpdateSysDictionary(c *gin.Context) {
+	var dictionary system.SysDictionary
+	_ = c.ShouldBindJSON(&dictionary)
+	if err := dictionaryService.UpdateSysDictionary(&dictionary); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// @Tags SysDictionary
+// @Summary 用id查询SysDictionary
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query system.SysDictionary true "ID或字典英名"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionary"
+// @Router /sysDictionary/findSysDictionary [get]
+func (s *DictionaryApi) FindSysDictionary(c *gin.Context) {
+	var dictionary system.SysDictionary
+	_ = c.ShouldBindQuery(&dictionary)
+	if sysDictionary, err := dictionaryService.GetSysDictionary(dictionary.Type, dictionary.ID, dictionary.Status); err != nil {
+		global.GVA_LOG.Error("字典未创建或未开启!", zap.Error(err))
+		response.FailWithMessage("字典未创建或未开启", c)
+	} else {
+		response.OkWithDetailed(gin.H{"resysDictionary": sysDictionary}, "查询成功", c)
+	}
+}
+
+// @Tags SysDictionary
+// @Summary 分页获取SysDictionary列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query request.SysDictionarySearch true "页码, 每页大小, 搜索条件"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionary列表,返回包括列表,总数,页码,每页数量"
+// @Router /sysDictionary/getSysDictionaryList [get]
+func (s *DictionaryApi) GetSysDictionaryList(c *gin.Context) {
+	var pageInfo request.SysDictionarySearch
+	_ = c.ShouldBindQuery(&pageInfo)
+	if err := utils.Verify(pageInfo.PageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if list, total, err := dictionaryService.GetSysDictionaryInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 117 - 0
api/v1/system/sys_dictionary_detail.go

@@ -0,0 +1,117 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	"log-server/model/system/request"
+	"log-server/utils"
+)
+
+type DictionaryDetailApi struct{}
+
+// @Tags SysDictionaryDetail
+// @Summary 创建SysDictionaryDetail
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
+// @Success 200 {object} response.Response{msg=string} "创建SysDictionaryDetail"
+// @Router /sysDictionaryDetail/createSysDictionaryDetail [post]
+func (s *DictionaryDetailApi) CreateSysDictionaryDetail(c *gin.Context) {
+	var detail system.SysDictionaryDetail
+	_ = c.ShouldBindJSON(&detail)
+	if err := dictionaryDetailService.CreateSysDictionaryDetail(detail); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags SysDictionaryDetail
+// @Summary 删除SysDictionaryDetail
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionaryDetail true "SysDictionaryDetail模型"
+// @Success 200 {object} response.Response{msg=string} "删除SysDictionaryDetail"
+// @Router /sysDictionaryDetail/deleteSysDictionaryDetail [delete]
+func (s *DictionaryDetailApi) DeleteSysDictionaryDetail(c *gin.Context) {
+	var detail system.SysDictionaryDetail
+	_ = c.ShouldBindJSON(&detail)
+	if err := dictionaryDetailService.DeleteSysDictionaryDetail(detail); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags SysDictionaryDetail
+// @Summary 更新SysDictionaryDetail
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysDictionaryDetail true "更新SysDictionaryDetail"
+// @Success 200 {object} response.Response{msg=string} "更新SysDictionaryDetail"
+// @Router /sysDictionaryDetail/updateSysDictionaryDetail [put]
+func (s *DictionaryDetailApi) UpdateSysDictionaryDetail(c *gin.Context) {
+	var detail system.SysDictionaryDetail
+	_ = c.ShouldBindJSON(&detail)
+	if err := dictionaryDetailService.UpdateSysDictionaryDetail(&detail); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// @Tags SysDictionaryDetail
+// @Summary 用id查询SysDictionaryDetail
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query system.SysDictionaryDetail true "用id查询SysDictionaryDetail"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysDictionaryDetail"
+// @Router /sysDictionaryDetail/findSysDictionaryDetail [get]
+func (s *DictionaryDetailApi) FindSysDictionaryDetail(c *gin.Context) {
+	var detail system.SysDictionaryDetail
+	_ = c.ShouldBindQuery(&detail)
+	if err := utils.Verify(detail, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if reSysDictionaryDetail, err := dictionaryDetailService.GetSysDictionaryDetail(detail.ID); err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"reSysDictionaryDetail": reSysDictionaryDetail}, "查询成功", c)
+	}
+}
+
+// @Tags SysDictionaryDetail
+// @Summary 分页获取SysDictionaryDetail列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query request.SysDictionaryDetailSearch true "页码, 每页大小, 搜索条件"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysDictionaryDetail列表,返回包括列表,总数,页码,每页数量"
+// @Router /sysDictionaryDetail/getSysDictionaryDetailList [get]
+func (s *DictionaryDetailApi) GetSysDictionaryDetailList(c *gin.Context) {
+	var pageInfo request.SysDictionaryDetailSearch
+	_ = c.ShouldBindQuery(&pageInfo)
+	if list, total, err := dictionaryDetailService.GetSysDictionaryDetailInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 60 - 0
api/v1/system/sys_initdb.go

@@ -0,0 +1,60 @@
+package system
+
+import (
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system/request"
+
+	"github.com/gin-gonic/gin"
+)
+
+type DBApi struct{}
+
+// InitDB
+// @Tags InitDB
+// @Summary 初始化用户数据库
+// @Produce  application/json
+// @Param data body request.InitDB true "初始化数据库参数"
+// @Success 200 {object} response.Response{data=string} "初始化用户数据库"
+// @Router /init/initdb [post]
+func (i *DBApi) InitDB(c *gin.Context) {
+	if global.GVA_DB != nil {
+		global.GVA_LOG.Error("已存在数据库配置!")
+		response.FailWithMessage("已存在数据库配置", c)
+		return
+	}
+	var dbInfo request.InitDB
+	if err := c.ShouldBindJSON(&dbInfo); err != nil {
+		global.GVA_LOG.Error("参数校验不通过!", zap.Error(err))
+		response.FailWithMessage("参数校验不通过", c)
+		return
+	}
+	if err := initDBService.InitDB(dbInfo); err != nil {
+		global.GVA_LOG.Error("自动创建数据库失败!", zap.Error(err))
+		response.FailWithMessage("自动创建数据库失败,请查看后台日志,检查后在进行初始化", c)
+		return
+	}
+	response.OkWithMessage("自动创建数据库成功", c)
+}
+
+// CheckDB
+// @Tags CheckDB
+// @Summary 初始化用户数据库
+// @Produce  application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "初始化用户数据库"
+// @Router /init/checkdb [post]
+func (i *DBApi) CheckDB(c *gin.Context) {
+	var (
+		message  = "前往初始化数据库"
+		needInit = true
+	)
+
+	if global.GVA_DB != nil {
+		message = "数据库无需初始化"
+		needInit = false
+	}
+	global.GVA_LOG.Info(message)
+	response.OkWithDetailed(gin.H{"needInit": needInit}, message, c)
+	return
+}

+ 29 - 0
api/v1/system/sys_jwt_blacklist.go

@@ -0,0 +1,29 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+)
+
+type JwtApi struct{}
+
+// @Tags Jwt
+// @Summary jwt加入黑名单
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{msg=string} "jwt加入黑名单"
+// @Router /jwt/jsonInBlacklist [post]
+func (j *JwtApi) JsonInBlacklist(c *gin.Context) {
+	token := c.Request.Header.Get("x-token")
+	jwt := system.JwtBlacklist{Jwt: token}
+	if err := jwtService.JsonInBlacklist(jwt); err != nil {
+		global.GVA_LOG.Error("jwt作废失败!", zap.Error(err))
+		response.FailWithMessage("jwt作废失败", c)
+	} else {
+		response.OkWithMessage("jwt作废成功", c)
+	}
+}

+ 226 - 0
api/v1/system/sys_menu.go

@@ -0,0 +1,226 @@
+package system
+
+import (
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemReq "log-server/model/system/request"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type AuthorityMenuApi struct{}
+
+// @Tags AuthorityMenu
+// @Summary 获取用户动态路由
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body request.Empty true "空"
+// @Success 200 {object} response.Response{data=systemRes.SysMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单详情列表"
+// @Router /menu/getMenu [post]
+func (a *AuthorityMenuApi) GetMenu(c *gin.Context) {
+	if menus, err := menuService.GetMenuTree(utils.GetUserAuthorityId(c)); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		if menus == nil {
+			menus = []system.SysMenu{}
+		}
+		response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c)
+	}
+}
+
+// @Tags AuthorityMenu
+// @Summary 获取用户动态路由
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body request.Empty true "空"
+// @Success 200 {object} response.Response{data=systemRes.SysBaseMenusResponse,msg=string} "获取用户动态路由,返回包括系统菜单列表"
+// @Router /menu/getBaseMenuTree [post]
+func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) {
+	if menus, err := menuService.GetBaseMenuTree(); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysBaseMenusResponse{Menus: menus}, "获取成功", c)
+	}
+}
+
+// @Tags AuthorityMenu
+// @Summary 增加menu和角色关联关系
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.AddMenuAuthorityInfo true "角色ID"
+// @Success 200 {object} response.Response{msg=string} "增加menu和角色关联关系"
+// @Router /menu/addMenuAuthority [post]
+func (a *AuthorityMenuApi) AddMenuAuthority(c *gin.Context) {
+	var authorityMenu systemReq.AddMenuAuthorityInfo
+	_ = c.ShouldBindJSON(&authorityMenu)
+	if err := utils.Verify(authorityMenu, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := menuService.AddMenuAuthority(authorityMenu.Menus, authorityMenu.AuthorityId); err != nil {
+		global.GVA_LOG.Error("添加失败!", zap.Error(err))
+		response.FailWithMessage("添加失败", c)
+	} else {
+		response.OkWithMessage("添加成功", c)
+	}
+}
+
+// @Tags AuthorityMenu
+// @Summary 获取指定角色menu
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetAuthorityId true "角色ID"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取指定角色menu"
+// @Router /menu/getMenuAuthority [post]
+func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) {
+	var param request.GetAuthorityId
+	_ = c.ShouldBindJSON(&param)
+	if err := utils.Verify(param, utils.AuthorityIdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if menus, err := menuService.GetMenuAuthority(&param); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"menus": menus}, "获取成功", c)
+	}
+}
+
+// @Tags Menu
+// @Summary 新增菜单
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
+// @Success 200 {object} response.Response{msg=string} "新增菜单"
+// @Router /menu/addBaseMenu [post]
+func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) {
+	var menu system.SysBaseMenu
+	_ = c.ShouldBindJSON(&menu)
+	if err := utils.Verify(menu, utils.MenuVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := utils.Verify(menu.Meta, utils.MenuMetaVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := menuService.AddBaseMenu(menu); err != nil {
+		global.GVA_LOG.Error("添加失败!", zap.Error(err))
+
+		response.FailWithMessage("添加失败", c)
+	} else {
+		response.OkWithMessage("添加成功", c)
+	}
+}
+
+// @Tags Menu
+// @Summary 删除菜单
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "菜单id"
+// @Success 200 {object} response.Response{msg=string} "删除菜单"
+// @Router /menu/deleteBaseMenu [post]
+func (a *AuthorityMenuApi) DeleteBaseMenu(c *gin.Context) {
+	var menu request.GetById
+	_ = c.ShouldBindJSON(&menu)
+	if err := utils.Verify(menu, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := baseMenuService.DeleteBaseMenu(menu.ID); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags Menu
+// @Summary 更新菜单
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
+// @Success 200 {object} response.Response{msg=string} "更新菜单"
+// @Router /menu/updateBaseMenu [post]
+func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) {
+	var menu system.SysBaseMenu
+	_ = c.ShouldBindJSON(&menu)
+	if err := utils.Verify(menu, utils.MenuVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := utils.Verify(menu.Meta, utils.MenuMetaVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if err := baseMenuService.UpdateBaseMenu(menu); err != nil {
+		global.GVA_LOG.Error("更新失败!", zap.Error(err))
+		response.FailWithMessage("更新失败", c)
+	} else {
+		response.OkWithMessage("更新成功", c)
+	}
+}
+
+// @Tags Menu
+// @Summary 根据id获取菜单
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "菜单id"
+// @Success 200 {object} response.Response{data=systemRes.SysBaseMenuResponse,msg=string} "根据id获取菜单,返回包括系统菜单列表"
+// @Router /menu/getBaseMenuById [post]
+func (a *AuthorityMenuApi) GetBaseMenuById(c *gin.Context) {
+	var idInfo request.GetById
+	_ = c.ShouldBindJSON(&idInfo)
+	if err := utils.Verify(idInfo, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if menu, err := baseMenuService.GetBaseMenuById(idInfo.ID); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysBaseMenuResponse{Menu: menu}, "获取成功", c)
+	}
+}
+
+// @Tags Menu
+// @Summary 分页获取基础menu列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.PageInfo true "页码, 每页大小"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取基础menu列表,返回包括列表,总数,页码,每页数量"
+// @Router /menu/getMenuList [post]
+func (a *AuthorityMenuApi) GetMenuList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	_ = c.ShouldBindJSON(&pageInfo)
+	if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if menuList, total, err := menuService.GetInfoList(); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     menuList,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 118 - 0
api/v1/system/sys_operation_record.go

@@ -0,0 +1,118 @@
+package system
+
+import (
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemReq "log-server/model/system/request"
+	"log-server/utils"
+)
+
+type OperationRecordApi struct{}
+
+// @Tags SysOperationRecord
+// @Summary 创建SysOperationRecord
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysOperationRecord true "创建SysOperationRecord"
+// @Success 200 {object} response.Response{msg=string} "创建SysOperationRecord"
+// @Router /sysOperationRecord/createSysOperationRecord [post]
+func (s *OperationRecordApi) CreateSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord system.SysOperationRecord
+	_ = c.ShouldBindJSON(&sysOperationRecord)
+	if err := operationRecordService.CreateSysOperationRecord(sysOperationRecord); err != nil {
+		global.GVA_LOG.Error("创建失败!", zap.Error(err))
+		response.FailWithMessage("创建失败", c)
+	} else {
+		response.OkWithMessage("创建成功", c)
+	}
+}
+
+// @Tags SysOperationRecord
+// @Summary 删除SysOperationRecord
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysOperationRecord true "SysOperationRecord模型"
+// @Success 200 {object} response.Response{msg=string} "删除SysOperationRecord"
+// @Router /sysOperationRecord/deleteSysOperationRecord [delete]
+func (s *OperationRecordApi) DeleteSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord system.SysOperationRecord
+	_ = c.ShouldBindJSON(&sysOperationRecord)
+	if err := operationRecordService.DeleteSysOperationRecord(sysOperationRecord); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags SysOperationRecord
+// @Summary 批量删除SysOperationRecord
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.IdsReq true "批量删除SysOperationRecord"
+// @Success 200 {object} response.Response{msg=string} "批量删除SysOperationRecord"
+// @Router /sysOperationRecord/deleteSysOperationRecordByIds [delete]
+func (s *OperationRecordApi) DeleteSysOperationRecordByIds(c *gin.Context) {
+	var IDS request.IdsReq
+	_ = c.ShouldBindJSON(&IDS)
+	if err := operationRecordService.DeleteSysOperationRecordByIds(IDS); err != nil {
+		global.GVA_LOG.Error("批量删除失败!", zap.Error(err))
+		response.FailWithMessage("批量删除失败", c)
+	} else {
+		response.OkWithMessage("批量删除成功", c)
+	}
+}
+
+// @Tags SysOperationRecord
+// @Summary 用id查询SysOperationRecord
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query system.SysOperationRecord true "Id"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "用id查询SysOperationRecord"
+// @Router /sysOperationRecord/findSysOperationRecord [get]
+func (s *OperationRecordApi) FindSysOperationRecord(c *gin.Context) {
+	var sysOperationRecord system.SysOperationRecord
+	_ = c.ShouldBindQuery(&sysOperationRecord)
+	if err := utils.Verify(sysOperationRecord, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if reSysOperationRecord, err := operationRecordService.GetSysOperationRecord(sysOperationRecord.ID); err != nil {
+		global.GVA_LOG.Error("查询失败!", zap.Error(err))
+		response.FailWithMessage("查询失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"reSysOperationRecord": reSysOperationRecord}, "查询成功", c)
+	}
+}
+
+// @Tags SysOperationRecord
+// @Summary 分页获取SysOperationRecord列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data query request.SysOperationRecordSearch true "页码, 每页大小, 搜索条件"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取SysOperationRecord列表,返回包括列表,总数,页码,每页数量"
+// @Router /sysOperationRecord/getSysOperationRecordList [get]
+func (s *OperationRecordApi) GetSysOperationRecordList(c *gin.Context) {
+	var pageInfo systemReq.SysOperationRecordSearch
+	_ = c.ShouldBindQuery(&pageInfo)
+	if list, total, err := operationRecordService.GetSysOperationRecordInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}

+ 78 - 0
api/v1/system/sys_system.go

@@ -0,0 +1,78 @@
+package system
+
+import (
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+type SystemApi struct{}
+
+// @Tags System
+// @Summary 获取配置文件内容
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Success 200 {object} response.Response{data=systemRes.SysConfigResponse,msg=string} "获取配置文件内容,返回包括系统配置"
+// @Router /system/getSystemConfig [post]
+func (s *SystemApi) GetSystemConfig(c *gin.Context) {
+	if config, err := systemConfigService.GetSystemConfig(); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysConfigResponse{Config: config}, "获取成功", c)
+	}
+}
+
+// @Tags System
+// @Summary 设置配置文件内容
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body system.System true "设置配置文件内容"
+// @Success 200 {object} response.Response{data=string} "设置配置文件内容"
+// @Router /system/setSystemConfig [post]
+func (s *SystemApi) SetSystemConfig(c *gin.Context) {
+	var sys system.System
+	_ = c.ShouldBindJSON(&sys)
+	if err := systemConfigService.SetSystemConfig(sys); err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败", c)
+	} else {
+		response.OkWithMessage("设置成功", c)
+	}
+}
+
+// @Tags System
+// @Summary 重启系统
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Success 200 {object} response.Response{msg=string} "重启系统"
+// @Router /system/reloadSystem [post]
+func (s *SystemApi) ReloadSystem(c *gin.Context) {
+	err := utils.Reload()
+	if err != nil {
+		global.GVA_LOG.Error("重启系统失败!", zap.Error(err))
+		response.FailWithMessage("重启系统失败", c)
+	} else {
+		response.OkWithMessage("重启系统成功", c)
+	}
+}
+
+// @Tags System
+// @Summary 获取服务器信息
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取服务器信息"
+// @Router /system/getServerInfo [post]
+func (s *SystemApi) GetServerInfo(c *gin.Context) {
+	if server, err := systemConfigService.GetServerInfo(); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"server": server}, "获取成功", c)
+	}
+}

+ 377 - 0
api/v1/system/sys_user.go

@@ -0,0 +1,377 @@
+package system
+
+import (
+	"strconv"
+
+	"log-server/global"
+	"log-server/model/common/request"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	systemReq "log-server/model/system/request"
+	systemRes "log-server/model/system/response"
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"github.com/go-redis/redis/v8"
+	"go.uber.org/zap"
+)
+
+// @Tags Base
+// @Summary 用户登录
+// @Produce  application/json
+// @Param data body systemReq.Login true "用户名, 密码, 验证码"
+// @Success 200 {object} response.Response{data=systemRes.LoginResponse,msg=string} "返回包括用户信息,token,过期时间"
+// @Router /base/login [post]
+func (b *BaseApi) Login(c *gin.Context) {
+	var l systemReq.Login
+	_ = c.ShouldBindJSON(&l)
+	if err := utils.Verify(l, utils.LoginVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if store.Verify(l.CaptchaId, l.Captcha, true) {
+		u := &system.SysUser{Username: l.Username, Password: l.Password}
+		if user, err := userService.Login(u); err != nil {
+			global.GVA_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Error(err))
+			response.FailWithMessage("用户名不存在或者密码错误", c)
+		} else {
+			if user.Enable != 1 {
+				global.GVA_LOG.Error("登陆失败! 用户被禁止登录!")
+				response.FailWithMessage("用户被禁止登录", c)
+				return
+			}
+			b.TokenNext(c, *user)
+		}
+	} else {
+		response.FailWithMessage("验证码错误", c)
+	}
+}
+
+// 登录以后签发jwt
+func (b *BaseApi) TokenNext(c *gin.Context, user system.SysUser) {
+	j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
+	claims := j.CreateClaims(systemReq.BaseClaims{
+		UUID:        user.UUID,
+		ID:          user.ID,
+		NickName:    user.NickName,
+		Username:    user.Username,
+		AuthorityId: user.AuthorityId,
+	})
+	token, err := j.CreateToken(claims)
+	if err != nil {
+		global.GVA_LOG.Error("获取token失败!", zap.Error(err))
+		response.FailWithMessage("获取token失败", c)
+		return
+	}
+	if !global.GVA_CONFIG.System.UseMultipoint {
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
+		}, "登录成功", c)
+		return
+	}
+
+	if jwtStr, err := jwtService.GetRedisJWT(user.Username); err == redis.Nil {
+		if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
+			global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
+			response.FailWithMessage("设置登录状态失败", c)
+			return
+		}
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
+		}, "登录成功", c)
+	} else if err != nil {
+		global.GVA_LOG.Error("设置登录状态失败!", zap.Error(err))
+		response.FailWithMessage("设置登录状态失败", c)
+	} else {
+		var blackJWT system.JwtBlacklist
+		blackJWT.Jwt = jwtStr
+		if err := jwtService.JsonInBlacklist(blackJWT); err != nil {
+			response.FailWithMessage("jwt作废失败", c)
+			return
+		}
+		if err := jwtService.SetRedisJWT(token, user.Username); err != nil {
+			response.FailWithMessage("设置登录状态失败", c)
+			return
+		}
+		response.OkWithDetailed(systemRes.LoginResponse{
+			User:      user,
+			Token:     token,
+			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000,
+		}, "登录成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 用户注册账号
+// @Produce  application/json
+// @Param data body systemReq.Register true "用户名, 昵称, 密码, 角色ID"
+// @Success 200 {object} response.Response{data=systemRes.SysUserResponse,msg=string} "用户注册账号,返回包括用户信息"
+// @Router /user/admin_register [post]
+func (b *BaseApi) Register(c *gin.Context) {
+	var r systemReq.Register
+	_ = c.ShouldBindJSON(&r)
+	if err := utils.Verify(r, utils.RegisterVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	var authorities []system.SysAuthority
+	for _, v := range r.AuthorityIds {
+		authorities = append(authorities, system.SysAuthority{
+			AuthorityId: v,
+		})
+	}
+	user := &system.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities, Enable: r.Enable}
+	userReturn, err := userService.Register(*user)
+	if err != nil {
+		global.GVA_LOG.Error("注册失败!", zap.Error(err))
+		response.FailWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册失败", c)
+	} else {
+		response.OkWithDetailed(systemRes.SysUserResponse{User: userReturn}, "注册成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 用户修改密码
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码"
+// @Success 200 {object} response.Response{msg=string} "用户修改密码"
+// @Router /user/changePassword [post]
+func (b *BaseApi) ChangePassword(c *gin.Context) {
+	var user systemReq.ChangePasswordStruct
+	_ = c.ShouldBindJSON(&user)
+	if err := utils.Verify(user, utils.ChangePasswordVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	u := &system.SysUser{Username: user.Username, Password: user.Password}
+	if _, err := userService.ChangePassword(u, user.NewPassword); err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败,原密码与当前账户不符", c)
+	} else {
+		response.OkWithMessage("修改成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 分页获取用户列表
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.PageInfo true "页码, 每页大小"
+// @Success 200 {object} response.Response{data=response.PageResult,msg=string} "分页获取用户列表,返回包括列表,总数,页码,每页数量"
+// @Router /user/getUserList [post]
+func (b *BaseApi) GetUserList(c *gin.Context) {
+	var pageInfo request.PageInfo
+	_ = c.ShouldBindJSON(&pageInfo)
+	if err := utils.Verify(pageInfo, utils.PageInfoVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	if list, total, err := userService.GetUserInfoList(pageInfo); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(response.PageResult{
+			List:     list,
+			Total:    total,
+			Page:     pageInfo.Page,
+			PageSize: pageInfo.PageSize,
+		}, "获取成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 更改用户权限
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.SetUserAuth true "用户UUID, 角色ID"
+// @Success 200 {object} response.Response{msg=string} "设置用户权限"
+// @Router /user/setUserAuthority [post]
+func (b *BaseApi) SetUserAuthority(c *gin.Context) {
+	var sua systemReq.SetUserAuth
+	_ = c.ShouldBindJSON(&sua)
+	if UserVerifyErr := utils.Verify(sua, utils.SetUserAuthorityVerify); UserVerifyErr != nil {
+		response.FailWithMessage(UserVerifyErr.Error(), c)
+		return
+	}
+	userID := utils.GetUserID(c)
+	uuid := utils.GetUserUuid(c)
+	if err := userService.SetUserAuthority(userID, uuid, sua.AuthorityId); err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	} else {
+		claims := utils.GetUserInfo(c)
+		j := &utils.JWT{SigningKey: []byte(global.GVA_CONFIG.JWT.SigningKey)} // 唯一签名
+		claims.AuthorityId = sua.AuthorityId
+		if token, err := j.CreateToken(*claims); err != nil {
+			global.GVA_LOG.Error("修改失败!", zap.Error(err))
+			response.FailWithMessage(err.Error(), c)
+		} else {
+			c.Header("new-token", token)
+			c.Header("new-expires-at", strconv.FormatInt(claims.ExpiresAt, 10))
+			response.OkWithMessage("修改成功", c)
+		}
+
+	}
+}
+
+// @Tags SysUser
+// @Summary 设置用户权限
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body systemReq.SetUserAuthorities true "用户UUID, 角色ID"
+// @Success 200 {object} response.Response{msg=string} "设置用户权限"
+// @Router /user/setUserAuthorities [post]
+func (b *BaseApi) SetUserAuthorities(c *gin.Context) {
+	var sua systemReq.SetUserAuthorities
+	_ = c.ShouldBindJSON(&sua)
+	if err := userService.SetUserAuthorities(sua.ID, sua.AuthorityIds); err != nil {
+		global.GVA_LOG.Error("修改失败!", zap.Error(err))
+		response.FailWithMessage("修改失败", c)
+	} else {
+		response.OkWithMessage("修改成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 删除用户
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body request.GetById true "用户ID"
+// @Success 200 {object} response.Response{msg=string} "删除用户"
+// @Router /user/deleteUser [delete]
+func (b *BaseApi) DeleteUser(c *gin.Context) {
+	var reqId request.GetById
+	_ = c.ShouldBindJSON(&reqId)
+	if err := utils.Verify(reqId, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+	jwtId := utils.GetUserID(c)
+	if jwtId == uint(reqId.ID) {
+		response.FailWithMessage("删除失败, 自杀失败", c)
+		return
+	}
+	if err := userService.DeleteUser(reqId.ID); err != nil {
+		global.GVA_LOG.Error("删除失败!", zap.Error(err))
+		response.FailWithMessage("删除失败", c)
+	} else {
+		response.OkWithMessage("删除成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 设置用户信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
+// @Router /user/setUserInfo [put]
+func (b *BaseApi) SetUserInfo(c *gin.Context) {
+	var user systemReq.ChangeUserInfo
+	_ = c.ShouldBindJSON(&user)
+	if err := utils.Verify(user, utils.IdVerify); err != nil {
+		response.FailWithMessage(err.Error(), c)
+		return
+	}
+
+	if len(user.AuthorityIds) != 0 {
+		err := userService.SetUserAuthorities(user.ID, user.AuthorityIds)
+		if err != nil {
+			global.GVA_LOG.Error("设置失败!", zap.Error(err))
+			response.FailWithMessage("设置失败", c)
+			return
+		}
+	}
+
+	if err := userService.SetUserInfo(system.SysUser{
+		GVA_MODEL: global.GVA_MODEL{
+			ID: user.ID,
+		},
+		NickName:  user.NickName,
+		HeaderImg: user.HeaderImg,
+		Phone:     user.Phone,
+		Email:     user.Email,
+		SideMode:  user.SideMode,
+		Enable:    user.Enable,
+	}); err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败", c)
+	} else {
+		response.OkWithMessage("设置成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 设置用户信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Param data body system.SysUser true "ID, 用户名, 昵称, 头像链接"
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "设置用户信息"
+// @Router /user/SetSelfInfo [put]
+func (b *BaseApi) SetSelfInfo(c *gin.Context) {
+	var user systemReq.ChangeUserInfo
+	_ = c.ShouldBindJSON(&user)
+	user.ID = utils.GetUserID(c)
+	if err := userService.SetUserInfo(system.SysUser{
+		GVA_MODEL: global.GVA_MODEL{
+			ID: user.ID,
+		},
+		NickName:  user.NickName,
+		HeaderImg: user.HeaderImg,
+		Phone:     user.Phone,
+		Email:     user.Email,
+		SideMode:  user.SideMode,
+		Enable:    user.Enable,
+	}); err != nil {
+		global.GVA_LOG.Error("设置失败!", zap.Error(err))
+		response.FailWithMessage("设置失败", c)
+	} else {
+		response.OkWithMessage("设置成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 获取用户信息
+// @Security ApiKeyAuth
+// @accept application/json
+// @Produce application/json
+// @Success 200 {object} response.Response{data=map[string]interface{},msg=string} "获取用户信息"
+// @Router /user/getUserInfo [get]
+func (b *BaseApi) GetUserInfo(c *gin.Context) {
+	uuid := utils.GetUserUuid(c)
+	if ReqUser, err := userService.GetUserInfo(uuid); err != nil {
+		global.GVA_LOG.Error("获取失败!", zap.Error(err))
+		response.FailWithMessage("获取失败", c)
+	} else {
+		response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c)
+	}
+}
+
+// @Tags SysUser
+// @Summary 重置用户密码
+// @Security ApiKeyAuth
+// @Produce  application/json
+// @Param data body system.SysUser true "ID"
+// @Success 200 {object} response.Response{msg=string} "重置用户密码"
+// @Router /user/resetPassword [post]
+func (b *BaseApi) ResetPassword(c *gin.Context) {
+	var user system.SysUser
+	_ = c.ShouldBindJSON(&user)
+	if err := userService.ResetPassword(user.ID); err != nil {
+		global.GVA_LOG.Error("重置失败!", zap.Error(err))
+		response.FailWithMessage("重置失败"+err.Error(), c)
+	} else {
+		response.OkWithMessage("重置成功", c)
+	}
+}

+ 3 - 0
compile.bat

@@ -0,0 +1,3 @@
+set GOOS=linux
+set GOARCH=amd64
+go build -ldflags "-s -w"

+ 159 - 0
config.yaml

@@ -0,0 +1,159 @@
+aliyun-oss:
+  endpoint: yourEndpoint
+  access-key-id: yourAccessKeyId
+  access-key-secret: yourAccessKeySecret
+  bucket-name: yourBucketName
+  bucket-url: yourBucketUrl
+  base-path: yourBasePath
+autocode:
+  transfer-restart: true
+  root: D:\project
+  server: /server
+  server-api: /api/v1/%s
+  server-plug: /plugin/%s
+  server-initialize: /initialize
+  server-model: /model/%s
+  server-request: /model/%s/request/
+  server-router: /router/%s
+  server-service: /service/%s
+  web: /web/src
+  web-api: /api
+  web-form: /view
+  web-table: /view
+aws-s3:
+  bucket: xxxxx-10005608
+  region: ap-shanghai
+  endpoint: ""
+  s3-force-path-style: false
+  disable-ssl: false
+  secret-id: xxxxxxxx
+  secret-key: xxxxxxxx
+  base-url: https://gin.vue.admin
+  path-prefix: log-server
+captcha:
+  key-long: 6
+  img-width: 240
+  img-height: 80
+cors:
+  mode: whitelist
+  whitelist:
+  - allow-origin: example1.com
+    allow-methods: GET, POST
+    allow-headers: content-type
+    expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,
+      Content-Type
+    allow-credentials: true
+  - allow-origin: example2.com
+    allow-methods: GET, POST
+    allow-headers: content-type
+    expose-headers: Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers,
+      Content-Type
+    allow-credentials: true
+db-list:
+- disable: false
+  type: ""
+  alias-name: ""
+  path: ""
+  port: ""
+  config: ""
+  db-name: ""
+  username: ""
+  password: ""
+  max-idle-conns: 10
+  max-open-conns: 100
+  log-mode: ""
+  log-zap: false
+email:
+  to: xxx@qq.com
+  port: 465
+  from: xxx@163.com
+  host: smtp.163.com
+  is-ssl: true
+  secret: xxx
+  nickname: test
+excel:
+  dir: ./resource/excel/
+hua-wei-obs:
+  path: you-path
+  bucket: you-bucket
+  endpoint: you-endpoint
+  access-key: you-access-key
+  secret-key: you-secret-key
+jwt:
+  signing-key: 73add799-ce1e-4e6b-8631-f063d80c03d3
+  expires-time: 604800
+  buffer-time: 86400
+  issuer: qmPlus
+local:
+  path: uploads/file
+  store-path: uploads/file
+mysql:
+  path: 120.92.140.20
+  port: "3306"
+  config: charset=utf8mb4&parseTime=True&loc=Local
+  db-name: backend-server
+  username: backend-server
+  password: 123456
+  max-idle-conns: 10
+  max-open-conns: 100
+  log-mode: info
+  log-zap: false
+pgsql:
+  path: ""
+  port: ""
+  config: ""
+  db-name: ""
+  username: ""
+  password: ""
+  max-idle-conns: 10
+  max-open-conns: 100
+  log-mode: ""
+  log-zap: false
+qiniu:
+  zone: ZoneHuaDong
+  bucket: ""
+  img-path: ""
+  use-https: false
+  access-key: ""
+  secret-key: ""
+  use-cdn-domains: false
+redis:
+  db: 7
+  addr: 120.92.140.137:6379
+  password: "rootredis!fu>6Wy2"
+system:
+  env: public
+  addr: 8888
+  db-type: mysql
+  oss-type: local
+  use-multipoint: false
+  use-redis: false
+  iplimit-count: 15000
+  iplimit-time: 3600
+tencent-cos:
+  bucket: xxxxx-10005608
+  region: ap-shanghai
+  secret-id: xxxxxxxx
+  secret-key: xxxxxxxx
+  base-url: https://gin.vue.admin
+  path-prefix: log-server
+timer:
+  start: true
+  spec: '@daily'
+  detail:
+  - tableName: sys_operation_records
+    compareField: created_at
+    interval: 2160h
+  - tableName: jwt_blacklists
+    compareField: created_at
+    interval: 168h
+zap:
+  level: info
+  prefix: '[log-server]'
+  format: console
+  director: log
+  encode-level: LowercaseColorLevelEncoder
+  stacktrace-key: stacktrace
+  max-age: 30
+  show-line: true
+  log-in-console: true

+ 18 - 0
config/auto_code.go

@@ -0,0 +1,18 @@
+package config
+
+type Autocode struct {
+	TransferRestart bool   `mapstructure:"transfer-restart" json:"transfer-restart" yaml:"transfer-restart"`
+	Root            string `mapstructure:"root" json:"root" yaml:"root"`
+	Server          string `mapstructure:"server" json:"server" yaml:"server"`
+	SApi            string `mapstructure:"server-api" json:"server-api" yaml:"server-api"`
+	SPlug           string `mapstructure:"server-plug" json:"server-plug" yaml:"server-plug"`
+	SInitialize     string `mapstructure:"server-initialize" json:"server-initialize" yaml:"server-initialize"`
+	SModel          string `mapstructure:"server-model" json:"server-model" yaml:"server-model"`
+	SRequest        string `mapstructure:"server-request" json:"server-request"  yaml:"server-request"`
+	SRouter         string `mapstructure:"server-router" json:"server-router" yaml:"server-router"`
+	SService        string `mapstructure:"server-service" json:"server-service" yaml:"server-service"`
+	Web             string `mapstructure:"web" json:"web" yaml:"web"`
+	WApi            string `mapstructure:"web-api" json:"web-api" yaml:"web-api"`
+	WForm           string `mapstructure:"web-form" json:"web-form" yaml:"web-form"`
+	WTable          string `mapstructure:"web-table" json:"web-table" yaml:"web-table"`
+}

+ 7 - 0
config/captcha.go

@@ -0,0 +1,7 @@
+package config
+
+type Captcha struct {
+	KeyLong   int `mapstructure:"key-long" json:"key-long" yaml:"key-long"`       // 验证码长度
+	ImgWidth  int `mapstructure:"img-width" json:"img-width" yaml:"img-width"`    // 验证码宽度
+	ImgHeight int `mapstructure:"img-height" json:"img-height" yaml:"img-height"` // 验证码高度
+}

+ 29 - 0
config/config.go

@@ -0,0 +1,29 @@
+package config
+
+type Server struct {
+	JWT     JWT     `mapstructure:"jwt" json:"jwt" yaml:"jwt"`
+	Zap     Zap     `mapstructure:"zap" json:"zap" yaml:"zap"`
+	Redis   Redis   `mapstructure:"redis" json:"redis" yaml:"redis"`
+	Email   Email   `mapstructure:"email" json:"email" yaml:"email"`
+	System  System  `mapstructure:"system" json:"system" yaml:"system"`
+	Captcha Captcha `mapstructure:"captcha" json:"captcha" yaml:"captcha"`
+	// auto
+	AutoCode Autocode `mapstructure:"autocode" json:"autocode" yaml:"autocode"`
+	// gorm
+	Mysql  Mysql           `mapstructure:"mysql" json:"mysql" yaml:"mysql"`
+	Pgsql  Pgsql           `mapstructure:"pgsql" json:"pgsql" yaml:"pgsql"`
+	DBList []SpecializedDB `mapstructure:"db-list" json:"db-list" yaml:"db-list"`
+	// oss
+	Local      Local      `mapstructure:"local" json:"local" yaml:"local"`
+	Qiniu      Qiniu      `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"`
+	AliyunOSS  AliyunOSS  `mapstructure:"aliyun-oss" json:"aliyun-oss" yaml:"aliyun-oss"`
+	HuaWeiObs  HuaWeiObs  `mapstructure:"hua-wei-obs" json:"hua-wei-obs" yaml:"hua-wei-obs"`
+	TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencent-cos" yaml:"tencent-cos"`
+	AwsS3      AwsS3      `mapstructure:"aws-s3" json:"aws-s3" yaml:"aws-s3"`
+
+	Excel Excel `mapstructure:"excel" json:"excel" yaml:"excel"`
+	Timer Timer `mapstructure:"timer" json:"timer" yaml:"timer"`
+
+	// 跨域配置
+	Cors CORS `mapstructure:"cors" json:"cors" yaml:"cors"`
+}

+ 14 - 0
config/cors.go

@@ -0,0 +1,14 @@
+package config
+
+type CORS struct {
+	Mode      string          `mapstructure:"mode" json:"mode" yaml:"mode"`
+	Whitelist []CORSWhitelist `mapstructure:"whitelist" json:"whitelist" yaml:"whitelist"`
+}
+
+type CORSWhitelist struct {
+	AllowOrigin      string `mapstructure:"allow-origin" json:"allow-origin" yaml:"allow-origin"`
+	AllowMethods     string `mapstructure:"allow-methods" json:"allow-methods" yaml:"allow-methods"`
+	AllowHeaders     string `mapstructure:"allow-headers" json:"allow-headers" yaml:"allow-headers"`
+	ExposeHeaders    string `mapstructure:"expose-headers" json:"expose-headers" yaml:"expose-headers"`
+	AllowCredentials bool   `mapstructure:"allow-credentials" json:"allow-credentials" yaml:"allow-credentials"`
+}

+ 29 - 0
config/db_list.go

@@ -0,0 +1,29 @@
+package config
+
+type DsnProvider interface {
+	Dsn() string
+}
+
+// Embeded 结构体可以压平到上一层,从而保持 config 文件的结构和原来一样
+// 见 playground: https://go.dev/play/p/KIcuhqEoxmY
+
+// GeneralDB 也被 Pgsql 和 Mysql 原样使用
+type GeneralDB struct {
+	Path         string `mapstructure:"path" json:"path" yaml:"path"`                               // 服务器地址:端口
+	Port         string `mapstructure:"port" json:"port" yaml:"port"`                               //:端口
+	Config       string `mapstructure:"config" json:"config" yaml:"config"`                         // 高级配置
+	Dbname       string `mapstructure:"db-name" json:"db-name" yaml:"db-name"`                      // 数据库名
+	Username     string `mapstructure:"username" json:"username" yaml:"username"`                   // 数据库用户名
+	Password     string `mapstructure:"password" json:"password" yaml:"password"`                   // 数据库密码
+	MaxIdleConns int    `mapstructure:"max-idle-conns" json:"max-idle-conns" yaml:"max-idle-conns"` // 空闲中的最大连接数
+	MaxOpenConns int    `mapstructure:"max-open-conns" json:"max-open-conns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
+	LogMode      string `mapstructure:"log-mode" json:"log-mode" yaml:"log-mode"`                   // 是否开启Gorm全局日志
+	LogZap       bool   `mapstructure:"log-zap" json:"log-zap" yaml:"log-zap"`                      // 是否通过zap写入日志文件
+}
+
+type SpecializedDB struct {
+	Disable   bool   `mapstructure:"disable" json:"disable" yaml:"disable"`
+	Type      string `mapstructure:"type" json:"type" yaml:"type"`
+	AliasName string `mapstructure:"alias-name" json:"alias-name" yaml:"alias-name"`
+	GeneralDB `yaml:",inline" mapstructure:",squash"`
+}

+ 11 - 0
config/email.go

@@ -0,0 +1,11 @@
+package config
+
+type Email struct {
+	To       string `mapstructure:"to" json:"to" yaml:"to"`                   // 收件人:多个以英文逗号分隔
+	Port     int    `mapstructure:"port" json:"port" yaml:"port"`             // 端口
+	From     string `mapstructure:"from" json:"from" yaml:"from"`             // 收件人
+	Host     string `mapstructure:"host" json:"host" yaml:"host"`             // 服务器地址
+	IsSSL    bool   `mapstructure:"is-ssl" json:"is-ssl" yaml:"is-ssl"`       // 是否SSL
+	Secret   string `mapstructure:"secret" json:"secret" yaml:"secret"`       // 密钥
+	Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称
+}

+ 5 - 0
config/excel.go

@@ -0,0 +1,5 @@
+package config
+
+type Excel struct {
+	Dir string `mapstructure:"dir" json:"dir" yaml:"dir"`
+}

+ 13 - 0
config/gorm_mysql.go

@@ -0,0 +1,13 @@
+package config
+
+type Mysql struct {
+	GeneralDB `yaml:",inline" mapstructure:",squash"`
+}
+
+func (m *Mysql) Dsn() string {
+	return m.Username + ":" + m.Password + "@tcp(" + m.Path + ":" + m.Port + ")/" + m.Dbname + "?" + m.Config
+}
+
+func (m *Mysql) GetLogMode() string {
+	return m.LogMode
+}

+ 21 - 0
config/gorm_pgsql.go

@@ -0,0 +1,21 @@
+package config
+
+type Pgsql struct {
+	GeneralDB `yaml:",inline" mapstructure:",squash"`
+}
+
+// Dsn 基于配置文件获取 dsn
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (p *Pgsql) Dsn() string {
+	return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + p.Dbname + " port=" + p.Port + " " + p.Config
+}
+
+// LinkDsn 根据 dbname 生成 dsn
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (p *Pgsql) LinkDsn(dbname string) string {
+	return "host=" + p.Path + " user=" + p.Username + " password=" + p.Password + " dbname=" + dbname + " port=" + p.Port + " " + p.Config
+}
+
+func (m *Pgsql) GetLogMode() string {
+	return m.LogMode
+}

+ 8 - 0
config/jwt.go

@@ -0,0 +1,8 @@
+package config
+
+type JWT struct {
+	SigningKey  string `mapstructure:"signing-key" json:"signing-key" yaml:"signing-key"`    // jwt签名
+	ExpiresTime int64  `mapstructure:"expires-time" json:"expires-time" yaml:"expires-time"` // 过期时间
+	BufferTime  int64  `mapstructure:"buffer-time" json:"buffer-time" yaml:"buffer-time"`    // 缓冲时间
+	Issuer      string `mapstructure:"issuer" json:"issuer" yaml:"issuer"`                   // 签发者
+}

+ 10 - 0
config/oss_aliyun.go

@@ -0,0 +1,10 @@
+package config
+
+type AliyunOSS struct {
+	Endpoint        string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
+	AccessKeyId     string `mapstructure:"access-key-id" json:"access-key-id" yaml:"access-key-id"`
+	AccessKeySecret string `mapstructure:"access-key-secret" json:"access-key-secret" yaml:"access-key-secret"`
+	BucketName      string `mapstructure:"bucket-name" json:"bucket-name" yaml:"bucket-name"`
+	BucketUrl       string `mapstructure:"bucket-url" json:"bucket-url" yaml:"bucket-url"`
+	BasePath        string `mapstructure:"base-path" json:"base-path" yaml:"base-path"`
+}

+ 13 - 0
config/oss_aws.go

@@ -0,0 +1,13 @@
+package config
+
+type AwsS3 struct {
+	Bucket           string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
+	Region           string `mapstructure:"region" json:"region" yaml:"region"`
+	Endpoint         string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
+	S3ForcePathStyle bool   `mapstructure:"s3-force-path-style" json:"s3-force-path-style" yaml:"s3-force-path-style"`
+	DisableSSL       bool   `mapstructure:"disable-ssl" json:"disable-ssl" yaml:"disable-ssl"`
+	SecretID         string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
+	SecretKey        string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
+	BaseURL          string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
+	PathPrefix       string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
+}

+ 9 - 0
config/oss_huawei.go

@@ -0,0 +1,9 @@
+package config
+
+type HuaWeiObs struct {
+	Path      string `mapstructure:"path" json:"path" yaml:"path"`
+	Bucket    string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
+	Endpoint  string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"`
+	AccessKey string `mapstructure:"access-key" json:"access-key" yaml:"access-key"`
+	SecretKey string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
+}

+ 6 - 0
config/oss_local.go

@@ -0,0 +1,6 @@
+package config
+
+type Local struct {
+	Path      string `mapstructure:"path" json:"path" yaml:"path"`                   // 本地文件访问路径
+	StorePath string `mapstructure:"store-path" json:"store-path" yaml:"store-path"` // 本地文件存储路径
+}

+ 11 - 0
config/oss_qiniu.go

@@ -0,0 +1,11 @@
+package config
+
+type Qiniu struct {
+	Zone          string `mapstructure:"zone" json:"zone" yaml:"zone"`                                  // 存储区域
+	Bucket        string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`                            // 空间名称
+	ImgPath       string `mapstructure:"img-path" json:"img-path" yaml:"img-path"`                      // CDN加速域名
+	UseHTTPS      bool   `mapstructure:"use-https" json:"use-https" yaml:"use-https"`                   // 是否使用https
+	AccessKey     string `mapstructure:"access-key" json:"access-key" yaml:"access-key"`                // 秘钥AK
+	SecretKey     string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`                // 秘钥SK
+	UseCdnDomains bool   `mapstructure:"use-cdn-domains" json:"use-cdn-domains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
+}

+ 10 - 0
config/oss_tencent.go

@@ -0,0 +1,10 @@
+package config
+
+type TencentCOS struct {
+	Bucket     string `mapstructure:"bucket" json:"bucket" yaml:"bucket"`
+	Region     string `mapstructure:"region" json:"region" yaml:"region"`
+	SecretID   string `mapstructure:"secret-id" json:"secret-id" yaml:"secret-id"`
+	SecretKey  string `mapstructure:"secret-key" json:"secret-key" yaml:"secret-key"`
+	BaseURL    string `mapstructure:"base-url" json:"base-url" yaml:"base-url"`
+	PathPrefix string `mapstructure:"path-prefix" json:"path-prefix" yaml:"path-prefix"`
+}

+ 7 - 0
config/redis.go

@@ -0,0 +1,7 @@
+package config
+
+type Redis struct {
+	DB       int    `mapstructure:"db" json:"db" yaml:"db"`                   // redis的哪个数据库
+	Addr     string `mapstructure:"addr" json:"addr" yaml:"addr"`             // 服务器地址:端口
+	Password string `mapstructure:"password" json:"password" yaml:"password"` // 密码
+}

+ 12 - 0
config/system.go

@@ -0,0 +1,12 @@
+package config
+
+type System struct {
+	Env           string `mapstructure:"env" json:"env" yaml:"env"`                                  // 环境值
+	Addr          int    `mapstructure:"addr" json:"addr" yaml:"addr"`                               // 端口值
+	DbType        string `mapstructure:"db-type" json:"db-type" yaml:"db-type"`                      // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
+	OssType       string `mapstructure:"oss-type" json:"oss-type" yaml:"oss-type"`                   // Oss类型
+	UseMultipoint bool   `mapstructure:"use-multipoint" json:"use-multipoint" yaml:"use-multipoint"` // 多点登录拦截
+	UseRedis      bool   `mapstructure:"use-redis" json:"use-redis" yaml:"use-redis"`                // 使用redis
+	LimitCountIP  int    `mapstructure:"iplimit-count" json:"iplimit-count" yaml:"iplimit-count"`
+	LimitTimeIP   int    `mapstructure:"iplimit-time" json:"iplimit-time" yaml:"iplimit-time"`
+}

+ 13 - 0
config/timer.go

@@ -0,0 +1,13 @@
+package config
+
+type Timer struct {
+	Start  bool     `mapstructure:"start" json:"start" yaml:"start"` // 是否启用
+	Spec   string   `mapstructure:"spec" json:"spec" yaml:"spec"`    // CRON表达式
+	Detail []Detail `mapstructure:"detail" json:"detail" yaml:"detail"`
+}
+
+type Detail struct {
+	TableName    string `mapstructure:"tableName" json:"tableName" yaml:"tableName"`          // 需要清理的表名
+	CompareField string `mapstructure:"compareField" json:"compareField" yaml:"compareField"` // 需要比较时间的字段
+	Interval     string `mapstructure:"interval" json:"interval" yaml:"interval"`             // 时间间隔
+}

+ 60 - 0
config/zap.go

@@ -0,0 +1,60 @@
+package config
+
+import (
+	"go.uber.org/zap/zapcore"
+	"strings"
+)
+
+type Zap struct {
+	Level         string `mapstructure:"level" json:"level" yaml:"level"`                            // 级别
+	Prefix        string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`                         // 日志前缀
+	Format        string `mapstructure:"format" json:"format" yaml:"format"`                         // 输出
+	Director      string `mapstructure:"director" json:"director"  yaml:"director"`                  // 日志文件夹
+	EncodeLevel   string `mapstructure:"encode-level" json:"encode-level" yaml:"encode-level"`       // 编码级
+	StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktrace-key" yaml:"stacktrace-key"` // 栈名
+
+	MaxAge       int  `mapstructure:"max-age" json:"max-age" yaml:"max-age"`                      // 日志留存时间
+	ShowLine     bool `mapstructure:"show-line" json:"show-line" yaml:"show-line"`                // 显示行
+	LogInConsole bool `mapstructure:"log-in-console" json:"log-in-console" yaml:"log-in-console"` // 输出控制台
+}
+
+// ZapEncodeLevel 根据 EncodeLevel 返回 zapcore.LevelEncoder
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *Zap) ZapEncodeLevel() zapcore.LevelEncoder {
+	switch {
+	case z.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
+		return zapcore.LowercaseLevelEncoder
+	case z.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
+		return zapcore.LowercaseColorLevelEncoder
+	case z.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
+		return zapcore.CapitalLevelEncoder
+	case z.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
+		return zapcore.CapitalColorLevelEncoder
+	default:
+		return zapcore.LowercaseLevelEncoder
+	}
+}
+
+// TransportLevel 根据字符串转化为 zapcore.Level
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *Zap) TransportLevel() zapcore.Level {
+	z.Level = strings.ToLower(z.Level)
+	switch z.Level {
+	case "debug":
+		return zapcore.DebugLevel
+	case "info":
+		return zapcore.InfoLevel
+	case "warn":
+		return zapcore.WarnLevel
+	case "error":
+		return zapcore.WarnLevel
+	case "dpanic":
+		return zapcore.DPanicLevel
+	case "panic":
+		return zapcore.PanicLevel
+	case "fatal":
+		return zapcore.FatalLevel
+	default:
+		return zapcore.DebugLevel
+	}
+}

+ 9 - 0
core/internal/constant.go

@@ -0,0 +1,9 @@
+package internal
+
+const (
+	ConfigEnv         = "GVA_CONFIG"
+	ConfigDefaultFile = "config.yaml"
+	ConfigTestFile    = "config.test.yaml"
+	ConfigDebugFile   = "config.debug.yaml"
+	ConfigReleaseFile = "config.release.yaml"
+)

+ 29 - 0
core/internal/file_rotatelogs.go

@@ -0,0 +1,29 @@
+package internal
+
+import (
+	rotatelogs "github.com/lestrrat-go/file-rotatelogs"
+	"go.uber.org/zap/zapcore"
+	"log-server/global"
+	"os"
+	"path"
+	"time"
+)
+
+var FileRotatelogs = new(fileRotatelogs)
+
+type fileRotatelogs struct{}
+
+// GetWriteSyncer 获取 zapcore.WriteSyncer
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (r *fileRotatelogs) GetWriteSyncer(level string) (zapcore.WriteSyncer, error) {
+	fileWriter, err := rotatelogs.New(
+		path.Join(global.GVA_CONFIG.Zap.Director, "%Y-%m-%d", level+".log"),
+		rotatelogs.WithClock(rotatelogs.Local),
+		rotatelogs.WithMaxAge(time.Duration(global.GVA_CONFIG.Zap.MaxAge)*24*time.Hour), // 日志留存时间
+		rotatelogs.WithRotationTime(time.Hour*24),
+	)
+	if global.GVA_CONFIG.Zap.LogInConsole {
+		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err
+	}
+	return zapcore.AddSync(fileWriter), err
+}

+ 107 - 0
core/internal/zap.go

@@ -0,0 +1,107 @@
+package internal
+
+import (
+	"fmt"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"log-server/global"
+	"time"
+)
+
+var Zap = new(_zap)
+
+type _zap struct{}
+
+// GetEncoder 获取 zapcore.Encoder
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) GetEncoder() zapcore.Encoder {
+	if global.GVA_CONFIG.Zap.Format == "json" {
+		return zapcore.NewJSONEncoder(z.GetEncoderConfig())
+	}
+	return zapcore.NewConsoleEncoder(z.GetEncoderConfig())
+}
+
+// GetEncoderConfig 获取zapcore.EncoderConfig
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) GetEncoderConfig() zapcore.EncoderConfig {
+	return zapcore.EncoderConfig{
+		MessageKey:     "message",
+		LevelKey:       "level",
+		TimeKey:        "time",
+		NameKey:        "logger",
+		CallerKey:      "caller",
+		StacktraceKey:  global.GVA_CONFIG.Zap.StacktraceKey,
+		LineEnding:     zapcore.DefaultLineEnding,
+		EncodeLevel:    global.GVA_CONFIG.Zap.ZapEncodeLevel(),
+		EncodeTime:     z.CustomTimeEncoder,
+		EncodeDuration: zapcore.SecondsDurationEncoder,
+		EncodeCaller:   zapcore.FullCallerEncoder,
+	}
+}
+
+// GetEncoderCore 获取Encoder的 zapcore.Core
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) GetEncoderCore(l zapcore.Level, level zap.LevelEnablerFunc) zapcore.Core {
+	writer, err := FileRotatelogs.GetWriteSyncer(l.String()) // 使用file-rotatelogs进行日志分割
+	if err != nil {
+		fmt.Printf("Get Write Syncer Failed err:%v", err.Error())
+		return nil
+	}
+
+	return zapcore.NewCore(z.GetEncoder(), writer, level)
+}
+
+// CustomTimeEncoder 自定义日志输出时间格式
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) CustomTimeEncoder(t time.Time, encoder zapcore.PrimitiveArrayEncoder) {
+	encoder.AppendString(t.Format(global.GVA_CONFIG.Zap.Prefix + "2006/01/02 - 15:04:05.000"))
+}
+
+// GetZapCores 根据配置文件的Level获取 []zapcore.Core
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) GetZapCores() []zapcore.Core {
+	cores := make([]zapcore.Core, 0, 7)
+	for level := global.GVA_CONFIG.Zap.TransportLevel(); level <= zapcore.FatalLevel; level++ {
+		cores = append(cores, z.GetEncoderCore(level, z.GetLevelPriority(level)))
+	}
+	return cores
+}
+
+// GetLevelPriority 根据 zapcore.Level 获取 zap.LevelEnablerFunc
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (z *_zap) GetLevelPriority(level zapcore.Level) zap.LevelEnablerFunc {
+	switch level {
+	case zapcore.DebugLevel:
+		return func(level zapcore.Level) bool { // 调试级别
+			return level == zap.DebugLevel
+		}
+	case zapcore.InfoLevel:
+		return func(level zapcore.Level) bool { // 日志级别
+			return level == zap.InfoLevel
+		}
+	case zapcore.WarnLevel:
+		return func(level zapcore.Level) bool { // 警告级别
+			return level == zap.WarnLevel
+		}
+	case zapcore.ErrorLevel:
+		return func(level zapcore.Level) bool { // 错误级别
+			return level == zap.ErrorLevel
+		}
+	case zapcore.DPanicLevel:
+		return func(level zapcore.Level) bool { // dpanic级别
+			return level == zap.DPanicLevel
+		}
+	case zapcore.PanicLevel:
+		return func(level zapcore.Level) bool { // panic级别
+			return level == zap.PanicLevel
+		}
+	case zapcore.FatalLevel:
+		return func(level zapcore.Level) bool { // 终止级别
+			return level == zap.FatalLevel
+		}
+	default:
+		return func(level zapcore.Level) bool { // 调试级别
+			return level == zap.DebugLevel
+		}
+	}
+}

+ 49 - 0
core/server.go

@@ -0,0 +1,49 @@
+package core
+
+import (
+	"fmt"
+	"time"
+
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/initialize"
+	"log-server/service/system"
+)
+
+type server interface {
+	ListenAndServe() error
+}
+
+func RunWindowsServer() {
+	if global.GVA_CONFIG.System.UseMultipoint || global.GVA_CONFIG.System.UseRedis {
+		// 初始化redis服务
+		initialize.Redis()
+	}
+
+	// 从db加载jwt数据
+	if global.GVA_DB != nil {
+		system.LoadAll()
+	}
+
+	Router := initialize.Routers()
+	Router.Static("/form-generator", "./resource/page")
+
+	address := fmt.Sprintf(":%d", global.GVA_CONFIG.System.Addr)
+	s := initServer(address, Router)
+	// 保证文本顺序输出
+	// In order to ensure that the text order output can be deleted
+	time.Sleep(10 * time.Microsecond)
+	global.GVA_LOG.Info("server run success on ", zap.String("address", address))
+
+	fmt.Printf(`
+	欢迎使用 gin-vue-admin
+	当前版本:v2.5.3beta
+    加群方式:微信号:shouzi_1994 QQ群:622360840
+	插件市场:https://plugin.gin-vue-admin.com
+	GVA讨论社区:https://support.qq.com/products/371961
+	默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
+	默认前端文件运行地址:http://127.0.0.1:8080
+	如果项目让您获得了收益,希望您能请团队喝杯可乐:https://www.gin-vue-admin.com/docs/coffee
+`, address)
+	global.GVA_LOG.Error(s.ListenAndServe().Error())
+}

+ 19 - 0
core/server_other.go

@@ -0,0 +1,19 @@
+//go:build !windows
+// +build !windows
+
+package core
+
+import (
+	"time"
+
+	"github.com/fvbock/endless"
+	"github.com/gin-gonic/gin"
+)
+
+func initServer(address string, router *gin.Engine) server {
+	s := endless.NewServer(address, router)
+	s.ReadHeaderTimeout = 20 * time.Second
+	s.WriteTimeout = 20 * time.Second
+	s.MaxHeaderBytes = 1 << 20
+	return s
+}

+ 21 - 0
core/server_win.go

@@ -0,0 +1,21 @@
+//go:build windows
+// +build windows
+
+package core
+
+import (
+	"net/http"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+func initServer(address string, router *gin.Engine) server {
+	return &http.Server{
+		Addr:           address,
+		Handler:        router,
+		ReadTimeout:    20 * time.Second,
+		WriteTimeout:   20 * time.Second,
+		MaxHeaderBytes: 1 << 20,
+	}
+}

+ 79 - 0
core/viper.go

@@ -0,0 +1,79 @@
+package core
+
+import (
+	"flag"
+	"fmt"
+	"github.com/gin-gonic/gin"
+	"log-server/core/internal"
+	"os"
+	"path/filepath"
+	"time"
+
+	"github.com/songzhibin97/gkit/cache/local_cache"
+
+	"github.com/fsnotify/fsnotify"
+	"github.com/spf13/viper"
+	"log-server/global"
+	_ "log-server/packfile"
+)
+
+// Viper //
+// 优先级: 命令行 > 环境变量 > 默认值
+// Author [SliverHorn](https://github.com/SliverHorn)
+func Viper(path ...string) *viper.Viper {
+	var config string
+
+	if len(path) == 0 {
+		flag.StringVar(&config, "c", "", "choose config file.")
+		flag.Parse()
+		if config == "" { // 判断命令行参数是否为空
+			if configEnv := os.Getenv(internal.ConfigEnv); configEnv == "" { // 判断 internal.ConfigEnv 常量存储的环境变量是否为空
+				switch gin.Mode() {
+				case gin.DebugMode:
+					config = internal.ConfigDefaultFile
+					fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigDefaultFile)
+				case gin.ReleaseMode:
+					config = internal.ConfigReleaseFile
+					fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigReleaseFile)
+				case gin.TestMode:
+					config = internal.ConfigTestFile
+					fmt.Printf("您正在使用gin模式的%s环境名称,config的路径为%s\n", gin.EnvGinMode, internal.ConfigTestFile)
+				}
+			} else { // internal.ConfigEnv 常量存储的环境变量不为空 将值赋值于config
+				config = configEnv
+				fmt.Printf("您正在使用%s环境变量,config的路径为%s\n", internal.ConfigEnv, config)
+			}
+		} else { // 命令行参数不为空 将值赋值于config
+			fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%s\n", config)
+		}
+	} else { // 函数传递的可变参数的第一个值赋值于config
+		config = path[0]
+		fmt.Printf("您正在使用func Viper()传递的值,config的路径为%s\n", config)
+	}
+
+	v := viper.New()
+	v.SetConfigFile(config)
+	v.SetConfigType("yaml")
+	err := v.ReadInConfig()
+	if err != nil {
+		panic(fmt.Errorf("Fatal error config file: %s \n", err))
+	}
+	v.WatchConfig()
+
+	v.OnConfigChange(func(e fsnotify.Event) {
+		fmt.Println("config file changed:", e.Name)
+		if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
+			fmt.Println(err)
+		}
+	})
+	if err = v.Unmarshal(&global.GVA_CONFIG); err != nil {
+		fmt.Println(err)
+	}
+
+	// root 适配性 根据root位置去找到对应迁移位置,保证root路径有效
+	global.GVA_CONFIG.AutoCode.Root, _ = filepath.Abs("..")
+	global.BlackCache = local_cache.NewCache(
+		local_cache.SetDefaultExpire(time.Second * time.Duration(global.GVA_CONFIG.JWT.ExpiresTime)),
+	)
+	return v
+}

+ 28 - 0
core/zap.go

@@ -0,0 +1,28 @@
+package core
+
+import (
+	"fmt"
+	"go.uber.org/zap"
+	"go.uber.org/zap/zapcore"
+	"log-server/core/internal"
+	"log-server/global"
+	"log-server/utils"
+	"os"
+)
+
+// Zap 获取 zap.Logger
+// Author [SliverHorn](https://github.com/SliverHorn)
+func Zap() (logger *zap.Logger) {
+	if ok, _ := utils.PathExists(global.GVA_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
+		fmt.Printf("create %v directory\n", global.GVA_CONFIG.Zap.Director)
+		_ = os.Mkdir(global.GVA_CONFIG.Zap.Director, os.ModePerm)
+	}
+
+	cores := internal.Zap.GetZapCores()
+	logger = zap.New(zapcore.NewTee(cores...))
+
+	if global.GVA_CONFIG.Zap.ShowLine {
+		logger = logger.WithOptions(zap.AddCaller())
+	}
+	return logger
+}

ファイルの差分が大きいため隠しています
+ 7701 - 0
docs/docs.go


ファイルの差分が大きいため隠しています
+ 4656 - 0
docs/swagger.yaml


+ 51 - 0
global/global.go

@@ -0,0 +1,51 @@
+package global
+
+import (
+	"sync"
+
+	"github.com/songzhibin97/gkit/cache/local_cache"
+	"log-server/utils/timer"
+
+	"golang.org/x/sync/singleflight"
+
+	"go.uber.org/zap"
+
+	"log-server/config"
+
+	"github.com/go-redis/redis/v8"
+	"github.com/spf13/viper"
+	"gorm.io/gorm"
+)
+
+var (
+	GVA_DB     *gorm.DB
+	GVA_DBList map[string]*gorm.DB
+	GVA_REDIS  *redis.Client
+	GVA_CONFIG config.Server
+	GVA_VP     *viper.Viper
+	// GVA_LOG    *oplogging.Logger
+	GVA_LOG                 *zap.Logger
+	GVA_Timer               timer.Timer = timer.NewTimerTask()
+	GVA_Concurrency_Control             = &singleflight.Group{}
+
+	BlackCache local_cache.Cache
+	lock       sync.RWMutex
+)
+
+// GetGlobalDBByDBName 通过名称获取db list中的db
+func GetGlobalDBByDBName(dbname string) *gorm.DB {
+	lock.RLock()
+	defer lock.RUnlock()
+	return GVA_DBList[dbname]
+}
+
+// MustGetGlobalDBByDBName 通过名称获取db 如果不存在则panic
+func MustGetGlobalDBByDBName(dbname string) *gorm.DB {
+	lock.RLock()
+	defer lock.RUnlock()
+	db, ok := GVA_DBList[dbname]
+	if !ok || db == nil {
+		panic("db no init")
+	}
+	return db
+}

+ 14 - 0
global/model.go

@@ -0,0 +1,14 @@
+package global
+
+import (
+	"time"
+
+	"gorm.io/gorm"
+)
+
+type GVA_MODEL struct {
+	ID        uint           `gorm:"primarykey"` // 主键ID
+	CreatedAt time.Time      // 创建时间
+	UpdatedAt time.Time      // 更新时间
+	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
+}

+ 60 - 0
go.mod

@@ -0,0 +1,60 @@
+module log-server
+
+go 1.16
+
+require (
+	github.com/BurntSushi/toml v1.1.0 // indirect
+	github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible
+	github.com/aws/aws-sdk-go v1.42.27
+	github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f // indirect
+	github.com/casbin/casbin/v2 v2.11.0
+	github.com/casbin/gorm-adapter/v3 v3.0.2
+	github.com/flipped-aurora/ws v1.0.2
+	github.com/fsnotify/fsnotify v1.4.9
+	github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6
+	github.com/gin-gonic/gin v1.7.0
+	github.com/go-openapi/spec v0.20.7 // indirect
+	github.com/go-openapi/swag v0.22.3 // indirect
+	github.com/go-redis/redis/v8 v8.11.0
+	github.com/go-sql-driver/mysql v1.5.0
+	github.com/golang-jwt/jwt/v4 v4.3.0
+	github.com/gookit/color v1.3.1
+	github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible
+	github.com/jackc/pgx/v4 v4.15.0 // indirect
+	github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84
+	github.com/json-iterator/go v1.1.10 // indirect
+	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
+	github.com/lestrrat-go/strftime v1.0.6 // indirect
+	github.com/mitchellh/mapstructure v1.2.2 // indirect
+	github.com/mojocn/base64Captcha v1.3.1
+	github.com/pelletier/go-toml v1.6.0 // indirect
+	github.com/pkg/errors v0.9.1
+	github.com/qiniu/api.v7/v7 v7.4.1
+	github.com/robfig/cron/v3 v3.0.1
+	github.com/satori/go.uuid v1.2.0
+	github.com/shirou/gopsutil/v3 v3.22.5
+	github.com/songzhibin97/gkit v1.1.1
+	github.com/spf13/afero v1.2.2 // indirect
+	github.com/spf13/cast v1.3.1 // indirect
+	github.com/spf13/jwalterweatherman v1.1.0 // indirect
+	github.com/spf13/pflag v1.0.5 // indirect
+	github.com/spf13/viper v1.7.0
+	github.com/stretchr/testify v1.8.0
+	github.com/swaggo/gin-swagger v1.3.0
+	github.com/swaggo/swag v1.8.4
+	github.com/tencentyun/cos-go-sdk-v5 v0.7.19
+	github.com/unrolled/secure v1.0.7
+	github.com/xuri/excelize/v2 v2.4.1
+	go.uber.org/zap v1.16.0
+	golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506
+	golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c // indirect
+	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4
+	golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 // indirect
+	golang.org/x/text v0.3.7
+	golang.org/x/tools v0.1.12 // indirect
+	gopkg.in/ini.v1 v1.55.0 // indirect
+	gorm.io/driver/mysql v1.0.1
+	gorm.io/driver/postgres v1.2.3
+	gorm.io/gorm v1.22.5
+	nhooyr.io/websocket v1.8.6
+)

+ 849 - 0
go.sum

@@ -0,0 +1,849 @@
+cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
+cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
+cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
+cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
+cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
+cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
+cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
+cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
+cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
+dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
+github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
+github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
+github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
+github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
+github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc=
+github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
+github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
+github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
+github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
+github.com/QcloudApi/qcloud_sign_golang v0.0.0-20141224014652-e4130a326409/go.mod h1:1pk82RBxDY/JZnPQrtqHlUFfCctgdorsd9M06fMynOM=
+github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
+github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
+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/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible h1:Ft+KeWIJxFP76LqgJbvtOA1qBIoC8vGkTV3QeCOeJC4=
+github.com/aliyun/aliyun-oss-go-sdk v2.1.6+incompatible/go.mod h1:T/Aws4fEfogEE9v+HPhhw+CntffsBHJ8nXQCwKr0/g8=
+github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
+github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/aws/aws-sdk-go v1.42.27 h1:kxsBXQg3ee6LLbqjp5/oUeDgG7TENFrWYDmEVnd7spU=
+github.com/aws/aws-sdk-go v1.42.27/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc=
+github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f h1:ZNv7On9kyUzm7fvRZumSyy/IUiSC7AzL0I1jKKtwooA=
+github.com/baiyubin/aliyun-sts-go-sdk v0.0.0-20180326062324-cfa1a18b161f/go.mod h1:AuiFmCCPBSrqvVMvuqFuk0qogytodnVFVSN5CeJB8Gc=
+github.com/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
+github.com/casbin/casbin/v2 v2.2.2/go.mod h1:XXtYGrs/0zlOsJMeRteEdVi/FsB0ph7KgNfjoCoJUD8=
+github.com/casbin/casbin/v2 v2.11.0 h1:6M/sWT9gh2pUcL541be/rllWEVxcEV6wdg1t7MN6fHQ=
+github.com/casbin/casbin/v2 v2.11.0/go.mod h1:XXtYGrs/0zlOsJMeRteEdVi/FsB0ph7KgNfjoCoJUD8=
+github.com/casbin/gorm-adapter/v3 v3.0.2 h1:4F2VFElwPyFzvHfgwizD2JQxk2OFLwvRFZct1np0yBg=
+github.com/casbin/gorm-adapter/v3 v3.0.2/go.mod h1:mQI09sqvXfy5p6kZB5HBzZrgKWwxaJ4xMWpd5OGfHRY=
+github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
+github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
+github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
+github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
+github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
+github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
+github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
+github.com/codegangsta/negroni v1.0.0 h1:+aYywywx4bnKXWvoWtRfJ91vC59NbEhEY03sZjQhbVY=
+github.com/codegangsta/negroni v1.0.0/go.mod h1:v0y3T5G7Y1UlFfyxFn/QLRU4a2EuNau2iZY63YTKWo0=
+github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
+github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
+github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
+github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc h1:VRRKCwnzqk8QCaRC4os14xoKDdbHqqlJtJA0oc1ZAjg=
+github.com/denisenkom/go-mssqldb v0.0.0-20200428022330-06a60b6afbbc/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
+github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
+github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/emicklei/proto v1.9.0/go.mod h1:rn1FgRS/FANiZdD2djyH7TMA9jdRDcYQ9IEN9yvjX0A=
+github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
+github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
+github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/flipped-aurora/ws v1.0.2 h1:oEUz7sgrbPENvgli7Q4QpC0NIEbJucgR4yjcDMg/AjY=
+github.com/flipped-aurora/ws v1.0.2/go.mod h1:RdyM2Fnvxx7f7A6WSmU1aAhDrQIAVW7LS/0LsAUE5mE=
+github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
+github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
+github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 h1:6VSn3hB5U5GeA6kQw4TwWIWbOhtvR2hmbBJnTOtqTWc=
+github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6/go.mod h1:YxOVT5+yHzKvwhsiSIWmbAYM3Dr9AEEbER2dVayfBkg=
+github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
+github.com/gin-contrib/gzip v0.0.1 h1:ezvKOL6jH+jlzdHNE4h9h8q8uMpDQjyl0NN0Jd7jozc=
+github.com/gin-contrib/gzip v0.0.1/go.mod h1:fGBJBCdt6qCZuCAOwWuFhBB4OOq9EFqlo5dEaFhhu5w=
+github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
+github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
+github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
+github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
+github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
+github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
+github.com/gin-gonic/gin v1.7.0 h1:jGB9xAJQ12AIGNB4HguylppmDK1Am9ppF7XnGXXJuoU=
+github.com/gin-gonic/gin v1.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY=
+github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
+github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
+github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
+github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
+github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
+github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
+github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns=
+github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA=
+github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo=
+github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
+github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I=
+github.com/go-openapi/spec v0.20.7 h1:1Rlu/ZrOCCob0n+JKKJAWhNWMPW8bOZRg8FJaY+0SKI=
+github.com/go-openapi/spec v0.20.7/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA=
+github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
+github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
+github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
+github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g=
+github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
+github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
+github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
+github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
+github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
+github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
+github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
+github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
+github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
+github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
+github.com/go-redis/redis/v8 v8.11.0 h1:O1Td0mQ8UFChQ3N9zFQqo6kTU2cJ+/it88gDB+zg0wo=
+github.com/go-redis/redis/v8 v8.11.0/go.mod h1:DLomh7y2e3ggQXQLd1YgmvIfecPJoFl7WU5SOQ/r06M=
+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/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
+github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
+github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
+github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
+github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo=
+github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
+github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
+github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
+github.com/golang-jwt/jwt/v4 v4.3.0 h1:kHL1vqdqWNfATmA0FNMdmZNMyZI1U6O31X4rlIPoBog=
+github.com/golang-jwt/jwt/v4 v4.3.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
+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/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
+github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
+github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
+github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
+github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
+github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
+github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
+github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
+github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
+github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
+github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
+github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
+github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
+github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
+github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
+github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
+github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
+github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
+github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
+github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
+github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/gookit/color v1.3.1 h1:PPD/C7sf8u2L8XQPdPgsWRoAiLQGZEZOzU3cf5IYYUk=
+github.com/gookit/color v1.3.1/go.mod h1:R3ogXq2B9rTbXoSHJ1HyUVAZ3poOJHpd9nQmyGZsfvQ=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
+github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
+github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
+github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
+github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
+github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
+github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
+github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
+github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
+github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
+github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
+github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
+github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
+github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
+github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
+github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
+github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible h1:3kDd8PIWAdU+qGs/+0QUgsMI2ZSiJPt45Xn0su+x/Q0=
+github.com/huaweicloud/huaweicloud-sdk-go-obs v3.21.8+incompatible/go.mod h1:l7VUhRbTKCzdOacdT4oWCwATKyvZqUOlOqr0Ous3k4s=
+github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
+github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
+github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
+github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
+github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
+github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
+github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
+github.com/jackc/pgconn v1.4.0/go.mod h1:Y2O3ZDF0q4mMacyWV3AstPJpeHXWGEetiFttmq5lahk=
+github.com/jackc/pgconn v1.5.0/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.5.1-0.20200601181101-fa742c524853/go.mod h1:QeD3lBfpTFe8WUnPZWN5KY/mB8FGMIYRdd8P8Jr0fAI=
+github.com/jackc/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc=
+github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
+github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
+github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
+github.com/jackc/pgconn v1.10.1/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
+github.com/jackc/pgconn v1.11.0 h1:HiHArx4yFbwl91X3qqIHtUFoiIfLNJXCQRsnzkiwwaQ=
+github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
+github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
+github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
+github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
+github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
+github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
+github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
+github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
+github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
+github.com/jackc/pgproto3/v2 v2.0.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgproto3/v2 v2.2.0 h1:r7JypeP2D3onoQTCxWdTpCtJ4D+qpKr0TxvoyMhZ5ns=
+github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
+github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg=
+github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
+github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
+github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
+github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
+github.com/jackc/pgtype v1.2.0/go.mod h1:5m2OfMh1wTK7x+Fk952IDmI4nw3nPrvtQdM0ZT4WpC0=
+github.com/jackc/pgtype v1.3.1-0.20200510190516-8cd94a14c75a/go.mod h1:vaogEUkALtxZMCH411K+tKzNpwzCKU+AnPzBKZ+I+Po=
+github.com/jackc/pgtype v1.3.1-0.20200606141011-f6355165a91c/go.mod h1:cvk9Bgu/VzJ9/lxTO5R5sf80p0DiucVtN7ZxvaC4GmQ=
+github.com/jackc/pgtype v1.4.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig=
+github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
+github.com/jackc/pgtype v1.9.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgtype v1.10.0 h1:ILnBWrRMSXGczYvmkYD6PsYyVFUNLTnIUJHHDLmqk38=
+github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
+github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
+github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
+github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
+github.com/jackc/pgx/v4 v4.5.0/go.mod h1:EpAKPLdnTorwmPUUsqrPxy5fphV18j9q3wrfRXgo+kA=
+github.com/jackc/pgx/v4 v4.6.1-0.20200510190926-94ba730bb1e9/go.mod h1:t3/cdRQl6fOLDxqtlyhe9UWgfIi9R8+8v8GKV5TRA/o=
+github.com/jackc/pgx/v4 v4.6.1-0.20200606145419-4e5062306904/go.mod h1:ZDaNWkt9sW1JMiNn0kdYBaLelIhw7Pg4qd+Vk6tw7Hg=
+github.com/jackc/pgx/v4 v4.7.1/go.mod h1:nu42q3aPjuC1M0Nak4bnoprKlXPINqopEKqbq5AZSC4=
+github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
+github.com/jackc/pgx/v4 v4.14.0/go.mod h1:jT3ibf/A0ZVCp89rtCIN0zCJxcE74ypROmHEZYsG/j8=
+github.com/jackc/pgx/v4 v4.15.0 h1:B7dTkXsdILD3MF987WGGCcg+tvLW6bZJdEcqVFeU//w=
+github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
+github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.2.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
+github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
+github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
+github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
+github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
+github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
+github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
+github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo=
+github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
+github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84 h1:pS0A6cr4aHYZnYwC7Uw+rwgb39+nzkm2QhwZ+S6Gn5I=
+github.com/jordan-wright/email v0.0.0-20200824153738-3f5bafa1cd84/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A=
+github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
+github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
+github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr68=
+github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
+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/juju/ratelimit v1.0.1/go.mod h1:qapgC/Gy+xNh9UxzV13HGGl/6UXNN+ct+vwSgWNm/qk=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
+github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.10.3 h1:OP96hzwJVBIHYU52pVTI6CczrxPvrGfgqF9N5eTO0Q8=
+github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
+github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/konsorten/go-windows-terminal-sequences v1.0.2/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.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
+github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8=
+github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
+github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
+github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
+github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
+github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
+github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
+github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
+github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
+github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
+github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
+github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
+github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
+github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
+github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
+github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
+github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
+github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
+github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
+github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
+github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
+github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
+github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
+github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+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 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
+github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
+github.com/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0=
+github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY=
+github.com/mozillazg/go-httpheader v0.2.1 h1:geV7TrjbL8KXSyvghnFm+NyTux/hxwueTSrwhe88TQQ=
+github.com/mozillazg/go-httpheader v0.2.1/go.mod h1:jJ8xECTlalr6ValeXYdOF8fFUISeBAdw6E61aqQma60=
+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/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
+github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
+github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
+github.com/onsi/ginkgo v1.15.0 h1:1V1NfVQR87RtWAgp1lv9JZJ5Jap+XFGKPi00andXGi4=
+github.com/onsi/ginkgo v1.15.0/go.mod h1:hF8qUzuuC8DJGygJH3726JnCZX4MYbRB8yFfISqnKUg=
+github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
+github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
+github.com/onsi/gomega v1.10.5 h1:7n6FEkpFmfCoo2t+YYqXH0evK+a9ICQz0xcAy9dYcaQ=
+github.com/onsi/gomega v1.10.5/go.mod h1:gza4q3jKQJijlu05nKWRCW/GavJumGt8aNRxWg7mt48=
+github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
+github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
+github.com/pelletier/go-toml v1.6.0 h1:aetoXYr0Tv7xRU/V4B4IZJ2QcbtMUFoNb3ORp7TzIK4=
+github.com/pelletier/go-toml v1.6.0/go.mod h1:5N711Q9dKgbdkxHL+MEfF31hpT7l0S0s/t2kKREewys=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
+github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
+github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/qiniu/api.v7/v7 v7.4.1 h1:BnNUBimLk6nrA/mIwsww9yJRupmViSsb1ndLMC7a9OY=
+github.com/qiniu/api.v7/v7 v7.4.1/go.mod h1:VE5oC5rkE1xul0u1S2N0b2Uxq9/6hZzhyqjgK25XDcM=
+github.com/richardlehane/mscfb v1.0.3 h1:rD8TBkYWkObWO0oLDFCbwMeZ4KoalxQy+QgniCj3nKI=
+github.com/richardlehane/mscfb v1.0.3/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
+github.com/richardlehane/msoleps v1.0.1 h1:RfrALnSNXzmXLbGct/P2b4xkFz4e8Gmj/0Vj9M9xC1o=
+github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
+github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
+github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
+github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
+github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
+github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
+github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/shirou/gopsutil v3.20.11+incompatible h1:LJr4ZQK4mPpIV5gOa4jCOKOGb4ty4DZO54I4FGqIpto=
+github.com/shirou/gopsutil v3.20.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
+github.com/shirou/gopsutil/v3 v3.22.5 h1:atX36I/IXgFiB81687vSiBI5zrMsxcIBkP9cQMJQoJA=
+github.com/shirou/gopsutil/v3 v3.22.5/go.mod h1:so9G9VzeHt/hsd0YwqprnjHnfARAUktauykSbr+y2gA=
+github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
+github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ=
+github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
+github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
+github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
+github.com/songzhibin97/gkit v1.1.1 h1:RmnCBLA7+OY40VEa2Uf2nGfbIMs4/5QT6tXDEHsMXiQ=
+github.com/songzhibin97/gkit v1.1.1/go.mod h1:V4E7H6DQuxX17xpLOqyH1j51GQctKBIIV3i2r5xkE5s=
+github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
+github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
+github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
+github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
+github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
+github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
+github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
+github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
+github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM=
+github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
+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/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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
+github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
+github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
+github.com/swaggo/gin-swagger v1.3.0 h1:eOmp7r57oUgZPw2dJOjcGNMse9cvXcI4tTqBcnZtPsI=
+github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0m5SkWx+cS0=
+github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
+github.com/swaggo/swag v1.8.4 h1:oGB351qH1JqUqK1tsMYEE5qTBbPk394BhsZxmUfebcI=
+github.com/swaggo/swag v1.8.4/go.mod h1:jMLeXOOmYyjk8PvHTsXBdrubsNd9gUJTTCzL5iBnseg=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.19 h1:janAfTO4MglOrUFuKGTQJBuMc66+F7TgtEIt1wPsJ+k=
+github.com/tencentyun/cos-go-sdk-v5 v0.7.19/go.mod h1:wQBO5HdAkLjj2q6XQiIfDSP8DXDNrppDRw2Kp/1BODA=
+github.com/tklauser/go-sysconf v0.3.10 h1:IJ1AZGZRWbY8T5Vfk04D9WOA5WSejdflXxP03OUqALw=
+github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
+github.com/tklauser/numcpus v0.4.0 h1:E53Dm1HjH1/R2/aoCtXtPgzmElmn51aOkhCFSuZq//o=
+github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
+github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
+github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
+github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
+github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q=
+github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
+github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
+github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
+github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4=
+github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
+github.com/unrolled/secure v1.0.7 h1:BcQHp3iKZyZCKj5gRqwQG+5urnGBF00wGgoPPwtheVQ=
+github.com/unrolled/secure v1.0.7/go.mod h1:uGc1OcRF8gCVBA+ANksKmvM85Hka6SZtQIbrKc3sHS4=
+github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
+github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
+github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3 h1:EpI0bqf/eX9SdZDwlMmahKM+CDBgNbsXMhsN28XrM8o=
+github.com/xuri/efp v0.0.0-20210322160811-ab561f5b45e3/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
+github.com/xuri/excelize/v2 v2.4.1 h1:veeeFLAJwsNEBPBlDepzPIYS1eLyBVcXNZUW79exZ1E=
+github.com/xuri/excelize/v2 v2.4.1/go.mod h1:rSu0C3papjzxQA3sdK8cU544TebhrPUoTOaGPIh0Q1A=
+github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
+github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
+github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
+github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
+go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
+go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
+go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
+go.opentelemetry.io/otel v1.0.0-RC2/go.mod h1:w1thVQ7qbAy8MHb0IFj8a5Q2QU0l2ksf8u/CN8m3NOM=
+go.opentelemetry.io/otel/sdk v1.0.0-RC2/go.mod h1:fgwHyiDn4e5k40TD9VX243rOxXR+jzsWBZYA2P5jpEw=
+go.opentelemetry.io/otel/trace v1.0.0-RC2/go.mod h1:JPQ+z6nNw9mqEGT8o3eoPTdnNI+Aj5JcxEsVGREIAy4=
+go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
+go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
+go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
+go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
+go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
+go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
+go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
+go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
+go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
+go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
+go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
+go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
+golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
+golang.org/x/crypto v0.0.0-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-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
+golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506 h1:EuGTJDfeg/PGZJp3gq1K+14eSLFTsrj1eg8KQuiUyKg=
+golang.org/x/crypto v0.0.0-20220213190939-1e6e3497d506/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
+golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
+golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190501045829-6d32002ffd75/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb h1:fqpd0EBDzlHRCjiphRR5Zo/RSWWQlWv34418dnEixWk=
+golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
+golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
+golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
+golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
+golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
+golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
+golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
+golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
+golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
+golang.org/x/net v0.0.0-20190611141213-3f473d35a33a/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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
+golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
+golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c h1:JVAXQ10yGGVbSyoer5VILysz6YKjdNT2bsvlayjqhes=
+golang.org/x/net v0.0.0-20220822230855-b0a4917ee28c/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
+golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181228144115-9a3f9b0469bb/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-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/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-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/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-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/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-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220818161305-2296e01440c6 h1:Sx/u41w+OwrInGdEckYmEuU5gHoGSL4QbDz3S9s6j4U=
+golang.org/x/sys v0.0.0-20220818161305-2296e01440c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 h1:Hir2P/De0WpUhtrKGGjvSb2YxUgyZ7EFOSLIcSSpiwE=
+golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
+golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
+golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190611222205-d73e1c7e250b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
+golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
+google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
+google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
+google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
+google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
+google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
+google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
+google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
+google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
+google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
+google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
+google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
+google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
+google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
+google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
+google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
+google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
+google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
+google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
+google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
+google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
+google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
+google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
+google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
+google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
+google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
+gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
+gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
+gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
+gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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=
+gorm.io/driver/mysql v0.3.0/go.mod h1:A7H1JD9dKdcjeUTpTuWKEC+E1a74qzW7/zaXqKaTbfM=
+gorm.io/driver/mysql v1.0.1 h1:omJoilUzyrAp0xNoio88lGJCroGdIOen9hq2A/+3ifw=
+gorm.io/driver/mysql v1.0.1/go.mod h1:KtqSthtg55lFp3S5kUXqlGaelnWpKitn4k1xZTnoiPw=
+gorm.io/driver/postgres v0.2.6/go.mod h1:AsPyuhKFOplSmQwOPsycVKbe0dRxF8v18KZ7p9i8dIs=
+gorm.io/driver/postgres v1.2.3 h1:f4t0TmNMy9gh3TU2PX+EppoA6YsgFnyq8Ojtddb42To=
+gorm.io/driver/postgres v1.2.3/go.mod h1:pJV6RgYQPG47aM1f0QeOzFH9HxQc8JcmAgjRCgS0wjs=
+gorm.io/driver/sqlserver v0.2.4 h1:AGofGL/TfzTZotzIHlaLISfxEKJpzj0ATbtXJW+ga1A=
+gorm.io/driver/sqlserver v0.2.4/go.mod h1:TcPfkdce5b8qlCMgyUeUdm7HQa1ZzWUuxzI+odcueLA=
+gorm.io/gorm v0.2.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v0.2.23/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.9.19/go.mod h1:0HFTzE/SqkGTzK6TlDPPQbAYCluiVvhzoA1+aVyzenw=
+gorm.io/gorm v1.22.3/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
+gorm.io/gorm v1.22.5 h1:lYREBgc02Be/5lSCTuysZZDb6ffL2qrat6fg9CFbvXU=
+gorm.io/gorm v1.22.5/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
+honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
+honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
+honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
+nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
+nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
+rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=

+ 32 - 0
initialize/db_list.go

@@ -0,0 +1,32 @@
+package initialize
+
+import (
+	"gorm.io/gorm"
+	"log-server/config"
+	"log-server/global"
+)
+
+const sys = "system"
+
+func DBList() {
+	dbMap := make(map[string]*gorm.DB)
+	for _, info := range global.GVA_CONFIG.DBList {
+		if info.Disable {
+			continue
+		}
+		switch info.Type {
+		case "mysql":
+			dbMap[info.AliasName] = GormMysqlByConfig(config.Mysql{GeneralDB: info.GeneralDB})
+		case "pgsql":
+			dbMap[info.AliasName] = GormPgSqlByConfig(config.Pgsql{GeneralDB: info.GeneralDB})
+		default:
+			continue
+		}
+	}
+	// 做特殊判断,是否有迁移
+	// 适配低版本迁移多数据库版本
+	if sysDB, ok := dbMap[sys]; ok {
+		global.GVA_DB = sysDB
+	}
+	global.GVA_DBList = dbMap
+}

+ 99 - 0
initialize/ensure_tables.go

@@ -0,0 +1,99 @@
+package initialize
+
+import (
+	"context"
+	adapter "github.com/casbin/gorm-adapter/v3"
+	"gorm.io/gorm"
+	"log-server/model/example"
+	sysModel "log-server/model/system"
+	"log-server/service/system"
+)
+
+const initOrderEnsureTables = system.InitOrderExternal - 1
+
+type ensureTables struct{}
+
+// auto run
+func init() {
+	system.RegisterInit(initOrderEnsureTables, &ensureTables{})
+}
+
+func (ensureTables) InitializerName() string {
+	return "ensure_tables_created"
+}
+func (e *ensureTables) InitializeData(ctx context.Context) (next context.Context, err error) {
+	return ctx, nil
+}
+
+func (e *ensureTables) DataInserted(ctx context.Context) bool {
+	return true
+}
+
+func (e *ensureTables) MigrateTable(ctx context.Context) (context.Context, error) {
+	db, ok := ctx.Value("db").(*gorm.DB)
+	if !ok {
+		return ctx, system.ErrMissingDBContext
+	}
+	tables := []interface{}{
+		sysModel.SysApi{},
+		sysModel.SysUser{},
+		sysModel.SysBaseMenu{},
+		sysModel.SysAuthority{},
+		sysModel.JwtBlacklist{},
+		sysModel.SysDictionary{},
+		sysModel.SysAutoCodeHistory{},
+		sysModel.SysOperationRecord{},
+		sysModel.SysDictionaryDetail{},
+		sysModel.SysBaseMenuParameter{},
+		sysModel.SysBaseMenuBtn{},
+		sysModel.SysAuthorityBtn{},
+		sysModel.SysAutoCode{},
+
+		adapter.CasbinRule{},
+
+		example.ExaFile{},
+		example.ExaCustomer{},
+		example.ExaFileChunk{},
+		example.ExaFileUploadAndDownload{},
+	}
+	for _, t := range tables {
+		_ = db.AutoMigrate(&t)
+		// 视图 authority_menu 会被当成表来创建,引发冲突错误(更新版本的gorm似乎不会)
+		// 由于 AutoMigrate() 基本无需考虑错误,因此显式忽略
+	}
+	return ctx, nil
+}
+
+func (e *ensureTables) TableCreated(ctx context.Context) bool {
+	db, ok := ctx.Value("db").(*gorm.DB)
+	if !ok {
+		return false
+	}
+	tables := []interface{}{
+		sysModel.SysApi{},
+		sysModel.SysUser{},
+		sysModel.SysBaseMenu{},
+		sysModel.SysAuthority{},
+		sysModel.JwtBlacklist{},
+		sysModel.SysDictionary{},
+		sysModel.SysAutoCodeHistory{},
+		sysModel.SysOperationRecord{},
+		sysModel.SysDictionaryDetail{},
+		sysModel.SysBaseMenuParameter{},
+		sysModel.SysBaseMenuBtn{},
+		sysModel.SysAuthorityBtn{},
+		sysModel.SysAutoCode{},
+
+		adapter.CasbinRule{},
+
+		example.ExaFile{},
+		example.ExaCustomer{},
+		example.ExaFileChunk{},
+		example.ExaFileUploadAndDownload{},
+	}
+	yes := true
+	for _, t := range tables {
+		yes = yes && db.Migrator().HasTable(t)
+	}
+	return yes
+}

+ 62 - 0
initialize/gorm.go

@@ -0,0 +1,62 @@
+package initialize
+
+import (
+	"os"
+
+	"log-server/global"
+	"log-server/model/example"
+	"log-server/model/system"
+
+	"go.uber.org/zap"
+	"gorm.io/gorm"
+)
+
+// Gorm 初始化数据库并产生数据库全局变量
+// Author SliverHorn
+func Gorm() *gorm.DB {
+	switch global.GVA_CONFIG.System.DbType {
+	case "mysql":
+		return GormMysql()
+	case "pgsql":
+		return GormPgSql()
+	default:
+		return GormMysql()
+	}
+}
+
+// RegisterTables 注册数据库表专用
+// Author SliverHorn
+func RegisterTables(db *gorm.DB) {
+	err := db.AutoMigrate(
+		// 系统模块表
+		system.SysApi{},
+		system.SysUser{},
+		system.SysBaseMenu{},
+		system.JwtBlacklist{},
+		system.SysAuthority{},
+		system.SysDictionary{},
+		system.SysOperationRecord{},
+		system.SysAutoCodeHistory{},
+		system.SysDictionaryDetail{},
+		system.SysBaseMenuParameter{},
+		system.SysBaseMenuBtn{},
+		system.SysAuthorityBtn{},
+		system.SysAutoCode{},
+
+		// 示例模块表
+		example.ExaFile{},
+		example.ExaCustomer{},
+		example.ExaFileChunk{},
+		example.ExaFileUploadAndDownload{},
+
+		// 自动化模块表
+		// Code generated by log-server Begin; DO NOT EDIT.
+
+		// Code generated by log-server End; DO NOT EDIT.
+	)
+	if err != nil {
+		global.GVA_LOG.Error("register table failed", zap.Error(err))
+		os.Exit(0)
+	}
+	global.GVA_LOG.Info("register table success")
+}

+ 52 - 0
initialize/gorm_mysql.go

@@ -0,0 +1,52 @@
+package initialize
+
+import (
+	"gorm.io/driver/mysql"
+	"gorm.io/gorm"
+	"log-server/config"
+	"log-server/global"
+	"log-server/initialize/internal"
+)
+
+// GormMysql 初始化Mysql数据库
+// Author [piexlmax](https://github.com/piexlmax)
+// Author [SliverHorn](https://github.com/SliverHorn)
+func GormMysql() *gorm.DB {
+	m := global.GVA_CONFIG.Mysql
+	if m.Dbname == "" {
+		return nil
+	}
+	mysqlConfig := mysql.Config{
+		DSN:                       m.Dsn(), // DSN data source name
+		DefaultStringSize:         191,     // string 类型字段的默认长度
+		SkipInitializeWithVersion: false,   // 根据版本自动配置
+	}
+	if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config()); err != nil {
+		return nil
+	} else {
+		sqlDB, _ := db.DB()
+		sqlDB.SetMaxIdleConns(m.MaxIdleConns)
+		sqlDB.SetMaxOpenConns(m.MaxOpenConns)
+		return db
+	}
+}
+
+// GormMysqlByConfig 初始化Mysql数据库用过传入配置
+func GormMysqlByConfig(m config.Mysql) *gorm.DB {
+	if m.Dbname == "" {
+		return nil
+	}
+	mysqlConfig := mysql.Config{
+		DSN:                       m.Dsn(), // DSN data source name
+		DefaultStringSize:         191,     // string 类型字段的默认长度
+		SkipInitializeWithVersion: false,   // 根据版本自动配置
+	}
+	if db, err := gorm.Open(mysql.New(mysqlConfig), internal.Gorm.Config()); err != nil {
+		panic(err)
+	} else {
+		sqlDB, _ := db.DB()
+		sqlDB.SetMaxIdleConns(m.MaxIdleConns)
+		sqlDB.SetMaxOpenConns(m.MaxOpenConns)
+		return db
+	}
+}

+ 50 - 0
initialize/gorm_pgsql.go

@@ -0,0 +1,50 @@
+package initialize
+
+import (
+	"gorm.io/driver/postgres"
+	"gorm.io/gorm"
+	"log-server/config"
+	"log-server/global"
+	"log-server/initialize/internal"
+)
+
+// GormPgSql 初始化 Postgresql 数据库
+// Author [piexlmax](https://github.com/piexlmax)
+// Author [SliverHorn](https://github.com/SliverHorn)
+func GormPgSql() *gorm.DB {
+	p := global.GVA_CONFIG.Pgsql
+	if p.Dbname == "" {
+		return nil
+	}
+	pgsqlConfig := postgres.Config{
+		DSN:                  p.Dsn(), // DSN data source name
+		PreferSimpleProtocol: false,
+	}
+	if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config()); err != nil {
+		return nil
+	} else {
+		sqlDB, _ := db.DB()
+		sqlDB.SetMaxIdleConns(p.MaxIdleConns)
+		sqlDB.SetMaxOpenConns(p.MaxOpenConns)
+		return db
+	}
+}
+
+// GormPgSqlByConfig 初始化 Postgresql 数据库 通过参数
+func GormPgSqlByConfig(p config.Pgsql) *gorm.DB {
+	if p.Dbname == "" {
+		return nil
+	}
+	pgsqlConfig := postgres.Config{
+		DSN:                  p.Dsn(), // DSN data source name
+		PreferSimpleProtocol: false,
+	}
+	if db, err := gorm.Open(postgres.New(pgsqlConfig), internal.Gorm.Config()); err != nil {
+		panic(err)
+	} else {
+		sqlDB, _ := db.DB()
+		sqlDB.SetMaxIdleConns(p.MaxIdleConns)
+		sqlDB.SetMaxOpenConns(p.MaxOpenConns)
+		return db
+	}
+}

+ 55 - 0
initialize/internal/gorm.go

@@ -0,0 +1,55 @@
+package internal
+
+import (
+	"log"
+	"os"
+	"time"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/logger"
+	"log-server/global"
+)
+
+type DBBASE interface {
+	GetLogMode() string
+}
+
+var Gorm = new(_gorm)
+
+type _gorm struct{}
+
+// Config gorm 自定义配置
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (g *_gorm) Config() *gorm.Config {
+	config := &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true}
+	_default := logger.New(NewWriter(log.New(os.Stdout, "\r\n", log.LstdFlags)), logger.Config{
+		SlowThreshold: 200 * time.Millisecond,
+		LogLevel:      logger.Warn,
+		Colorful:      true,
+	})
+	var logMode DBBASE
+	switch global.GVA_CONFIG.System.DbType {
+	case "mysql":
+		logMode = &global.GVA_CONFIG.Mysql
+		break
+	case "pgsql":
+		logMode = &global.GVA_CONFIG.Pgsql
+		break
+	default:
+		logMode = &global.GVA_CONFIG.Mysql
+	}
+
+	switch logMode.GetLogMode() {
+	case "silent", "Silent":
+		config.Logger = _default.LogMode(logger.Silent)
+	case "error", "Error":
+		config.Logger = _default.LogMode(logger.Error)
+	case "warn", "Warn":
+		config.Logger = _default.LogMode(logger.Warn)
+	case "info", "Info":
+		config.Logger = _default.LogMode(logger.Info)
+	default:
+		config.Logger = _default.LogMode(logger.Info)
+	}
+	return config
+}

+ 35 - 0
initialize/internal/logger.go

@@ -0,0 +1,35 @@
+package internal
+
+import (
+	"fmt"
+
+	"gorm.io/gorm/logger"
+	"log-server/global"
+)
+
+type writer struct {
+	logger.Writer
+}
+
+// NewWriter writer 构造函数
+// Author [SliverHorn](https://github.com/SliverHorn)
+func NewWriter(w logger.Writer) *writer {
+	return &writer{Writer: w}
+}
+
+// Printf 格式化打印日志
+// Author [SliverHorn](https://github.com/SliverHorn)
+func (w *writer) Printf(message string, data ...interface{}) {
+	var logZap bool
+	switch global.GVA_CONFIG.System.DbType {
+	case "mysql":
+		logZap = global.GVA_CONFIG.Mysql.LogZap
+	case "pgsql":
+		logZap = global.GVA_CONFIG.Pgsql.LogZap
+	}
+	if logZap {
+		global.GVA_LOG.Info(fmt.Sprintf(message+"\n", data...))
+	} else {
+		w.Writer.Printf(message, data...)
+	}
+}

+ 28 - 0
initialize/plugin.go

@@ -0,0 +1,28 @@
+package initialize
+
+import (
+	"github.com/gin-gonic/gin"
+	"log-server/global"
+	"log-server/plugin/email"
+	"log-server/utils/plugin"
+)
+
+func PluginInit(group *gin.RouterGroup, Plugin ...plugin.Plugin) {
+	for i := range Plugin {
+		PluginGroup := group.Group(Plugin[i].RouterPath())
+		Plugin[i].Register(PluginGroup)
+	}
+}
+
+func InstallPlugin(PublicGroup *gin.RouterGroup, PrivateGroup *gin.RouterGroup) {
+	//  添加跟角色挂钩权限的插件 示例 本地示例模式于在线仓库模式注意上方的import 可以自行切换 效果相同
+	PluginInit(PrivateGroup, email.CreateEmailPlug(
+		global.GVA_CONFIG.Email.To,
+		global.GVA_CONFIG.Email.From,
+		global.GVA_CONFIG.Email.Host,
+		global.GVA_CONFIG.Email.Secret,
+		global.GVA_CONFIG.Email.Nickname,
+		global.GVA_CONFIG.Email.Port,
+		global.GVA_CONFIG.Email.IsSSL,
+	))
+}

+ 26 - 0
initialize/redis.go

@@ -0,0 +1,26 @@
+package initialize
+
+import (
+	"context"
+
+	"log-server/global"
+
+	"github.com/go-redis/redis/v8"
+	"go.uber.org/zap"
+)
+
+func Redis() {
+	redisCfg := global.GVA_CONFIG.Redis
+	client := redis.NewClient(&redis.Options{
+		Addr:     redisCfg.Addr,
+		Password: redisCfg.Password, // no password set
+		DB:       redisCfg.DB,       // use default DB
+	})
+	pong, err := client.Ping(context.Background()).Result()
+	if err != nil {
+		global.GVA_LOG.Error("redis connect ping failed, err:", zap.Error(err))
+	} else {
+		global.GVA_LOG.Info("redis connect ping response:", zap.String("pong", pong))
+		global.GVA_REDIS = client
+	}
+}

+ 10 - 0
initialize/register_init.go

@@ -0,0 +1,10 @@
+package initialize
+
+import (
+	_ "log-server/source/example"
+	_ "log-server/source/system"
+)
+
+func init() {
+	// do nothing,only import source package so that inits can be registered
+}

+ 86 - 0
initialize/router.go

@@ -0,0 +1,86 @@
+package initialize
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+	"github.com/swaggo/gin-swagger"
+	"github.com/swaggo/gin-swagger/swaggerFiles"
+	_ "log-server/docs"
+	"log-server/global"
+	"log-server/middleware"
+	"log-server/router"
+)
+
+// 初始化总路由
+
+func Routers() *gin.Engine {
+	Router := gin.Default()
+	systemRouter := router.RouterGroupApp.System
+	exampleRouter := router.RouterGroupApp.Example
+	logingRouter := router.RouterGroupApp.Loging
+
+	// 如果想要不使用nginx代理前端网页,可以修改 web/.env.production 下的
+	// VUE_APP_BASE_API = /
+	// VUE_APP_BASE_PATH = http://localhost
+	// 然后执行打包命令 npm run build。在打开下面4行注释
+	// Router.LoadHTMLGlob("./dist/*.html") // npm打包成dist的路径
+	// Router.Static("/favicon.ico", "./dist/favicon.ico")
+	// Router.Static("/static", "./dist/assets")   // dist里面的静态资源
+	// Router.StaticFile("/", "./dist/index.html") // 前端网页入口页面
+
+	Router.StaticFS(global.GVA_CONFIG.Local.Path, http.Dir(global.GVA_CONFIG.Local.StorePath)) // 为用户头像和文件提供静态地址
+	// Router.Use(middleware.LoadTls())  // 如果需要使用https 请打开此中间件 然后前往 core/server.go 将启动模式 更变为 Router.RunTLS("端口","你的cre/pem文件","你的key文件")
+	global.GVA_LOG.Info("use middleware logger")
+	// 跨域,如需跨域可以打开下面的注释
+	// Router.Use(middleware.Cors()) // 直接放行全部跨域请求
+	//Router.Use(middleware.CorsByRules()) // 按照配置的规则放行跨域请求
+	global.GVA_LOG.Info("use middleware cors")
+	Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler))
+	global.GVA_LOG.Info("register swagger handler")
+	// 方便统一添加路由组前缀 多服务器上线使用
+
+	PublicGroup := Router.Group("")
+	{
+		// 健康监测
+		PublicGroup.GET("/health", func(c *gin.Context) {
+			c.JSON(200, "ok")
+		})
+	}
+	{
+		systemRouter.InitBaseRouter(PublicGroup) // 注册基础功能路由 不做鉴权
+		systemRouter.InitInitRouter(PublicGroup) // 自动初始化相关
+		logingRouter.InitLogingRouter(PublicGroup)
+		logingRouter.InitCodingLogRouter(PublicGroup)
+	}
+	PrivateGroup := Router.Group("")
+	PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler())
+	{
+		systemRouter.InitApiRouter(PrivateGroup)                 // 注册功能api路由
+		systemRouter.InitJwtRouter(PrivateGroup)                 // jwt相关路由
+		systemRouter.InitUserRouter(PrivateGroup)                // 注册用户路由
+		systemRouter.InitMenuRouter(PrivateGroup)                // 注册menu路由
+		systemRouter.InitSystemRouter(PrivateGroup)              // system相关路由
+		systemRouter.InitCasbinRouter(PrivateGroup)              // 权限相关路由
+		systemRouter.InitAutoCodeRouter(PrivateGroup)            // 创建自动化代码
+		systemRouter.InitAuthorityRouter(PrivateGroup)           // 注册角色路由
+		systemRouter.InitSysDictionaryRouter(PrivateGroup)       // 字典管理
+		systemRouter.InitAutoCodeHistoryRouter(PrivateGroup)     // 自动化代码历史
+		systemRouter.InitSysOperationRecordRouter(PrivateGroup)  // 操作记录
+		systemRouter.InitSysDictionaryDetailRouter(PrivateGroup) // 字典详情管理
+		systemRouter.InitAuthorityBtnRouterRouter(PrivateGroup)  // 字典详情管理
+
+		exampleRouter.InitExcelRouter(PrivateGroup)                 // 表格导入导出
+		exampleRouter.InitCustomerRouter(PrivateGroup)              // 客户路由
+		exampleRouter.InitFileUploadAndDownloadRouter(PrivateGroup) // 文件上传下载功能路由
+
+		// Code generated by log-server Begin; DO NOT EDIT.
+
+		// Code generated by log-server End; DO NOT EDIT.
+	}
+
+	InstallPlugin(PublicGroup, PrivateGroup) // 安装插件
+
+	global.GVA_LOG.Info("router register success")
+	return Router
+}

+ 49 - 0
initialize/timer.go

@@ -0,0 +1,49 @@
+package initialize
+
+import (
+	"fmt"
+	"log-server/service/log"
+
+	"log-server/config"
+	"log-server/global"
+	"log-server/utils"
+)
+
+var serviceStatisticsLog = new(log.ServiceStatisticsLog)
+var ServiceLogList = new(log.ServiceLogList)
+
+func Timer() {
+	if global.GVA_CONFIG.Timer.Start {
+		for i := range global.GVA_CONFIG.Timer.Detail {
+			go func(detail config.Detail) {
+				_, err := global.GVA_Timer.AddTaskByFunc("ClearDB", global.GVA_CONFIG.Timer.Spec, func() {
+					err := utils.ClearTable(global.GVA_DB, detail.TableName, detail.CompareField, detail.Interval)
+					if err != nil {
+						fmt.Println("timer error:", err)
+					}
+				})
+				if err != nil {
+					fmt.Println("add timer error:", err)
+				}
+			}(global.GVA_CONFIG.Timer.Detail[i])
+		}
+	}
+
+	//_, err := global.GVA_Timer.AddTaskByFunc("everyDayResetStatisticsCache","2 8 3 * * * ", serviceStatisticsLog.EveryDayResetStatisticsCache)
+	//if err != nil {
+	//	fmt.Println("everyDayResetStatisticsCache timer error:", err)
+	//}
+	_, err := global.GVA_Timer.AddTaskByFunc("StatisticsLog", "2 8 5 * * * ", serviceStatisticsLog.CreateStatisticsLog)
+	if err != nil {
+		fmt.Println("add taskCode timer error:", err)
+	}
+
+	//_, err = global.GVA_Timer.AddTaskByFunc("CreateComputerStatisticsData","2 46 7-23 * * * ", serviceStatisticsLog.CreateComputerStatisticsData)
+	//if err != nil {
+	//	fmt.Println("add CreateComputerStatisticsData timer error:", err)
+	//}
+	_, err = global.GVA_Timer.AddTaskByFunc("createFailLog", "2 0/5 6-23 * * * ", ServiceLogList.CreateFailLog)
+	if err != nil {
+		fmt.Println("everyDayResetStatisticsCache timer error:", err)
+	}
+}

+ 22 - 0
initialize/validator.go

@@ -0,0 +1,22 @@
+package initialize
+
+import "log-server/utils"
+
+func init() {
+	_ = utils.RegisterRule("PageVerify",
+		utils.Rules{
+			"Page":     {utils.NotEmpty()},
+			"PageSize": {utils.NotEmpty()},
+		},
+	)
+	_ = utils.RegisterRule("IdVerify",
+		utils.Rules{
+			"Id": {utils.NotEmpty()},
+		},
+	)
+	_ = utils.RegisterRule("AuthorityIdVerify",
+		utils.Rules{
+			"AuthorityId": {utils.NotEmpty()},
+		},
+	)
+}

+ 36 - 0
main.go

@@ -0,0 +1,36 @@
+package main
+
+import (
+	"go.uber.org/zap"
+	"log-server/core"
+	"log-server/global"
+	"log-server/initialize"
+)
+
+//go:generate go env -w GO111MODULE=on
+//go:generate go env -w GOPROXY=https://goproxy.cn,direct
+//go:generate go mod tidy
+//go:generate go mod download
+
+// @title Swagger Example API
+// @version 0.0.1
+// @description This is a sample Server pets
+// @securityDefinitions.apikey ApiKeyAuth
+// @in header
+// @name x-token
+// @BasePath /
+func main() {
+	global.GVA_VP = core.Viper() // 初始化Viper
+	global.GVA_LOG = core.Zap()  // 初始化zap日志库
+	zap.ReplaceGlobals(global.GVA_LOG)
+	global.GVA_DB = initialize.Gorm() // gorm连接数据库
+	initialize.Redis()
+	initialize.Timer()
+	initialize.DBList()
+
+	core.RunWindowsServer()
+	defer global.GVA_REDIS.Close()
+	// 程序结束前关闭数据库链接
+	db, _ := global.GVA_DB.DB()
+	defer db.Close()
+}

+ 34 - 0
middleware/casbin_rbac.go

@@ -0,0 +1,34 @@
+package middleware
+
+import (
+	"github.com/gin-gonic/gin"
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/service"
+	"log-server/utils"
+	"strconv"
+)
+
+var casbinService = service.ServiceGroupApp.SystemServiceGroup.CasbinService
+
+// 拦截器
+func CasbinHandler() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		waitUse, _ := utils.GetClaims(c)
+		// 获取请求的PATH
+		obj := c.Request.URL.Path
+		// 获取请求方法
+		act := c.Request.Method
+		// 获取用户的角色
+		sub := strconv.Itoa(int(waitUse.AuthorityId))
+		e := casbinService.Casbin() // 判断策略中是否存在
+		success, _ := e.Enforce(sub, obj, act)
+		if global.GVA_CONFIG.System.Env == "develop" || success {
+			c.Next()
+		} else {
+			response.FailWithDetailed(gin.H{}, "权限不足", c)
+			c.Abort()
+			return
+		}
+	}
+}

+ 73 - 0
middleware/cors.go

@@ -0,0 +1,73 @@
+package middleware
+
+import (
+	"github.com/gin-gonic/gin"
+	"log-server/config"
+	"log-server/global"
+	"net/http"
+)
+
+// Cors 直接放行所有跨域请求并放行所有 OPTIONS 方法
+func Cors() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		method := c.Request.Method
+		origin := c.Request.Header.Get("Origin")
+		c.Header("Access-Control-Allow-Origin", origin)
+		c.Header("Access-Control-Allow-Headers", "Content-Type,AccessToken,X-CSRF-Token, Authorization, Token,X-Token,X-User-Id")
+		c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS,DELETE,PUT")
+		c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Content-Type, New-Token, New-Expires-At")
+		c.Header("Access-Control-Allow-Credentials", "true")
+
+		// 放行所有OPTIONS方法
+		if method == "OPTIONS" {
+			c.AbortWithStatus(http.StatusNoContent)
+		}
+		// 处理请求
+		c.Next()
+	}
+}
+
+// CorsByRules 按照配置处理跨域请求
+func CorsByRules() gin.HandlerFunc {
+	// 放行全部
+	if global.GVA_CONFIG.Cors.Mode == "allow-all" {
+		return Cors()
+	}
+	return func(c *gin.Context) {
+		whitelist := checkCors(c.GetHeader("origin"))
+
+		// 通过检查, 添加请求头
+		if whitelist != nil {
+			c.Header("Access-Control-Allow-Origin", whitelist.AllowOrigin)
+			c.Header("Access-Control-Allow-Headers", whitelist.AllowHeaders)
+			c.Header("Access-Control-Allow-Methods", whitelist.AllowMethods)
+			c.Header("Access-Control-Expose-Headers", whitelist.ExposeHeaders)
+			if whitelist.AllowCredentials {
+				c.Header("Access-Control-Allow-Credentials", "true")
+			}
+		}
+
+		// 严格白名单模式且未通过检查,直接拒绝处理请求
+		if whitelist == nil && global.GVA_CONFIG.Cors.Mode == "strict-whitelist" && !(c.Request.Method == "GET" && c.Request.URL.Path == "/health") {
+			c.AbortWithStatus(http.StatusForbidden)
+		} else {
+			// 非严格白名单模式,无论是否通过检查均放行所有 OPTIONS 方法
+			if c.Request.Method == "OPTIONS" {
+				c.AbortWithStatus(http.StatusNoContent)
+			}
+		}
+
+		// 处理请求
+		c.Next()
+	}
+}
+
+func checkCors(currentOrigin string) *config.CORSWhitelist {
+	for _, whitelist := range global.GVA_CONFIG.Cors.Whitelist {
+		// 遍历配置中的跨域头,寻找匹配项
+		if currentOrigin == whitelist.AllowOrigin {
+			return &whitelist
+		}
+	}
+	return nil
+}

+ 60 - 0
middleware/email.go

@@ -0,0 +1,60 @@
+package middleware
+
+import (
+	"bytes"
+	"io/ioutil"
+	"strconv"
+	"time"
+
+	"log-server/plugin/email/utils"
+	utils2 "log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/system"
+	"log-server/service"
+)
+
+var userService = service.ServiceGroupApp.SystemServiceGroup.UserService
+
+func ErrorToEmail() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		var username string
+		claims, _ := utils2.GetClaims(c)
+		if claims.Username != "" {
+			username = claims.Username
+		} else {
+			id, _ := strconv.Atoi(c.Request.Header.Get("x-user-id"))
+			user, err := userService.FindUserById(id)
+			if err != nil {
+				username = "Unknown"
+			}
+			username = user.Username
+		}
+		body, _ := ioutil.ReadAll(c.Request.Body)
+		// 再重新写回请求体body中,ioutil.ReadAll会清空c.Request.Body中的数据
+		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+		record := system.SysOperationRecord{
+			Ip:     c.ClientIP(),
+			Method: c.Request.Method,
+			Path:   c.Request.URL.Path,
+			Agent:  c.Request.UserAgent(),
+			Body:   string(body),
+		}
+		now := time.Now()
+
+		c.Next()
+
+		latency := time.Since(now)
+		status := c.Writer.Status()
+		record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
+		str := "接收到的请求为" + record.Body + "\n" + "请求方式为" + record.Method + "\n" + "报错信息如下" + record.ErrorMessage + "\n" + "耗时" + latency.String() + "\n"
+		if status != 200 {
+			subject := username + "" + record.Ip + "调用了" + record.Path + "报错了"
+			if err := utils.ErrorToEmail(subject, str); err != nil {
+				global.GVA_LOG.Error("ErrorToEmail Failed, err:", zap.Error(err))
+			}
+		}
+	}
+}

+ 61 - 0
middleware/error.go

@@ -0,0 +1,61 @@
+package middleware
+
+import (
+	"net"
+	"net/http"
+	"net/http/httputil"
+	"os"
+	"runtime/debug"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+)
+
+// GinRecovery recover掉项目可能出现的panic,并使用zap记录相关日志
+func GinRecovery(stack bool) gin.HandlerFunc {
+	return func(c *gin.Context) {
+		defer func() {
+			if err := recover(); err != nil {
+				// Check for a broken connection, as it is not really a
+				// condition that warrants a panic stack trace.
+				var brokenPipe bool
+				if ne, ok := err.(*net.OpError); ok {
+					if se, ok := ne.Err.(*os.SyscallError); ok {
+						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
+							brokenPipe = true
+						}
+					}
+				}
+
+				httpRequest, _ := httputil.DumpRequest(c.Request, false)
+				if brokenPipe {
+					global.GVA_LOG.Error(c.Request.URL.Path,
+						zap.Any("error", err),
+						zap.String("request", string(httpRequest)),
+					)
+					// If the connection is dead, we can't write a status to it.
+					_ = c.Error(err.(error)) // nolint: errcheck
+					c.Abort()
+					return
+				}
+
+				if stack {
+					global.GVA_LOG.Error("[Recovery from panic]",
+						zap.Any("error", err),
+						zap.String("request", string(httpRequest)),
+						zap.String("stack", string(debug.Stack())),
+					)
+				} else {
+					global.GVA_LOG.Error("[Recovery from panic]",
+						zap.Any("error", err),
+						zap.String("request", string(httpRequest)),
+					)
+				}
+				c.AbortWithStatus(http.StatusInternalServerError)
+			}
+		}()
+		c.Next()
+	}
+}

+ 76 - 0
middleware/jwt.go

@@ -0,0 +1,76 @@
+package middleware
+
+import (
+	"strconv"
+	"time"
+
+	"log-server/utils"
+
+	"log-server/global"
+	"log-server/model/common/response"
+	"log-server/model/system"
+	"log-server/service"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+var jwtService = service.ServiceGroupApp.SystemServiceGroup.JwtService
+
+func JWTAuth() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		// 我们这里jwt鉴权取头部信息 x-token 登录时回返回token信息 这里前端需要把token存储到cookie或者本地localStorage中 不过需要跟后端协商过期时间 可以约定刷新令牌或者重新登录
+		token := c.Request.Header.Get("x-token")
+		if token == "" {
+			response.FailWithDetailed(gin.H{"reload": true}, "未登录或非法访问", c)
+			c.Abort()
+			return
+		}
+		if jwtService.IsBlacklist(token) {
+			response.FailWithDetailed(gin.H{"reload": true}, "您的帐户异地登陆或令牌失效", c)
+			c.Abort()
+			return
+		}
+		j := utils.NewJWT()
+		// parseToken 解析token包含的信息
+		claims, err := j.ParseToken(token)
+		if err != nil {
+			if err == utils.TokenExpired {
+				response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
+				c.Abort()
+				return
+			}
+			response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
+			c.Abort()
+			return
+		}
+
+		// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
+		// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
+
+		//if user, err := userService.FindUserByUuid(claims.UUID.String()); err != nil || user.Enable == 2 {
+		//	_ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: token})
+		//	response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
+		//	c.Abort()
+		//}
+		if claims.ExpiresAt-time.Now().Unix() < claims.BufferTime {
+			claims.ExpiresAt = time.Now().Unix() + global.GVA_CONFIG.JWT.ExpiresTime
+			newToken, _ := j.CreateTokenByOldToken(token, *claims)
+			newClaims, _ := j.ParseToken(newToken)
+			c.Header("new-token", newToken)
+			c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt, 10))
+			if global.GVA_CONFIG.System.UseMultipoint {
+				RedisJwtToken, err := jwtService.GetRedisJWT(newClaims.Username)
+				if err != nil {
+					global.GVA_LOG.Error("get redis jwt failed", zap.Error(err))
+				} else { // 当之前的取成功时才进行拉黑操作
+					_ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: RedisJwtToken})
+				}
+				// 无论如何都要记录当前的活跃状态
+				_ = jwtService.SetRedisJWT(newToken, newClaims.Username)
+			}
+		}
+		c.Set("claims", claims)
+		c.Next()
+	}
+}

+ 92 - 0
middleware/limit_ip.go

@@ -0,0 +1,92 @@
+package middleware
+
+import (
+	"context"
+	"errors"
+	"net/http"
+	"time"
+
+	"go.uber.org/zap"
+
+	"github.com/gin-gonic/gin"
+	"log-server/global"
+	"log-server/model/common/response"
+)
+
+type LimitConfig struct {
+	// GenerationKey 根据业务生成key 下面CheckOrMark查询生成
+	GenerationKey func(c *gin.Context) string
+	// 检查函数,用户可修改具体逻辑,更加灵活
+	CheckOrMark func(key string, expire int, limit int) error
+	// Expire key 过期时间
+	Expire int
+	// Limit 周期时间
+	Limit int
+}
+
+func (l LimitConfig) LimitWithTime() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		if err := l.CheckOrMark(l.GenerationKey(c), l.Expire, l.Limit); err != nil {
+			c.JSON(http.StatusOK, gin.H{"code": response.ERROR, "msg": err})
+			c.Abort()
+			return
+		} else {
+			c.Next()
+		}
+	}
+}
+
+// DefaultGenerationKey 默认生成key
+func DefaultGenerationKey(c *gin.Context) string {
+	return "GVA_Limit" + c.ClientIP()
+}
+
+func DefaultCheckOrMark(key string, expire int, limit int) (err error) {
+	// 判断是否开启redis
+	if global.GVA_REDIS == nil {
+		return err
+	}
+	if err = SetLimitWithTime(key, limit, time.Duration(expire)*time.Second); err != nil {
+		global.GVA_LOG.Error("limit", zap.Error(err))
+	}
+	return err
+}
+
+func DefaultLimit() gin.HandlerFunc {
+	return LimitConfig{
+		GenerationKey: DefaultGenerationKey,
+		CheckOrMark:   DefaultCheckOrMark,
+		Expire:        global.GVA_CONFIG.System.LimitTimeIP,
+		Limit:         global.GVA_CONFIG.System.LimitCountIP,
+	}.LimitWithTime()
+}
+
+// SetLimitWithTime 设置访问次数
+func SetLimitWithTime(key string, limit int, expiration time.Duration) error {
+	count, err := global.GVA_REDIS.Exists(context.Background(), key).Result()
+	if err != nil {
+		return err
+	}
+	if count == 0 {
+		pipe := global.GVA_REDIS.TxPipeline()
+		pipe.Incr(context.Background(), key)
+		pipe.Expire(context.Background(), key, expiration)
+		_, err = pipe.Exec(context.Background())
+		return err
+	} else {
+		// 次数
+		if times, err := global.GVA_REDIS.Get(context.Background(), key).Int(); err != nil {
+			return err
+		} else {
+			if times >= limit {
+				if t, err := global.GVA_REDIS.PTTL(context.Background(), key).Result(); err != nil {
+					return errors.New("请求太过频繁,请稍后再试")
+				} else {
+					return errors.New("请求太过频繁, 请 " + t.String() + " 秒后尝试")
+				}
+			} else {
+				return global.GVA_REDIS.Incr(context.Background(), key).Err()
+			}
+		}
+	}
+}

+ 27 - 0
middleware/loadtls.go

@@ -0,0 +1,27 @@
+package middleware
+
+import (
+	"fmt"
+
+	"github.com/gin-gonic/gin"
+	"github.com/unrolled/secure"
+)
+
+// 用https把这个中间件在router里面use一下就好
+
+func LoadTls() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		middleware := secure.New(secure.Options{
+			SSLRedirect: true,
+			SSLHost:     "localhost:443",
+		})
+		err := middleware.Process(c.Writer, c.Request)
+		if err != nil {
+			// 如果出现错误,请不要继续
+			fmt.Println(err)
+			return
+		}
+		// 继续往下处理
+		c.Next()
+	}
+}

+ 87 - 0
middleware/logger.go

@@ -0,0 +1,87 @@
+package middleware
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"strings"
+	"time"
+
+	"github.com/gin-gonic/gin"
+)
+
+// LogLayout 日志layout
+type LogLayout struct {
+	Time      time.Time
+	Metadata  map[string]interface{} // 存储自定义原数据
+	Path      string                 // 访问路径
+	Query     string                 // 携带query
+	Body      string                 // 携带body数据
+	IP        string                 // ip地址
+	UserAgent string                 // 代理
+	Error     string                 // 错误
+	Cost      time.Duration          // 花费时间
+	Source    string                 // 来源
+}
+
+type Logger struct {
+	// Filter 用户自定义过滤
+	Filter func(c *gin.Context) bool
+	// FilterKeyword 关键字过滤(key)
+	FilterKeyword func(layout *LogLayout) bool
+	// AuthProcess 鉴权处理
+	AuthProcess func(c *gin.Context, layout *LogLayout)
+	// 日志处理
+	Print func(LogLayout)
+	// Source 服务唯一标识
+	Source string
+}
+
+func (l Logger) SetLoggerMiddleware() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		start := time.Now()
+		path := c.Request.URL.Path
+		query := c.Request.URL.RawQuery
+		var body []byte
+		if l.Filter != nil && !l.Filter(c) {
+			body, _ = c.GetRawData()
+			// 将原body塞回去
+			c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+		}
+		c.Next()
+		cost := time.Since(start)
+		layout := LogLayout{
+			Time:      time.Now(),
+			Path:      path,
+			Query:     query,
+			IP:        c.ClientIP(),
+			UserAgent: c.Request.UserAgent(),
+			Error:     strings.TrimRight(c.Errors.ByType(gin.ErrorTypePrivate).String(), "\n"),
+			Cost:      cost,
+			Source:    l.Source,
+		}
+		if l.Filter != nil && !l.Filter(c) {
+			layout.Body = string(body)
+		}
+		// 处理鉴权需要的信息
+		l.AuthProcess(c, &layout)
+		if l.FilterKeyword != nil {
+			// 自行判断key/value 脱敏等
+			l.FilterKeyword(&layout)
+		}
+		// 自行处理日志
+		l.Print(layout)
+	}
+}
+
+func DefaultLogger() gin.HandlerFunc {
+	return Logger{
+		Print: func(layout LogLayout) {
+			// 标准输出,k8s做收集
+			v, _ := json.Marshal(layout)
+			fmt.Println(string(v))
+		},
+		Source: "GVA",
+	}.SetLoggerMiddleware()
+}

+ 22 - 0
middleware/need_init.go

@@ -0,0 +1,22 @@
+package middleware
+
+import (
+	"github.com/gin-gonic/gin"
+	"log-server/global"
+	"log-server/model/common/response"
+)
+
+// 处理跨域请求,支持options访问
+func NeedInit() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		if global.GVA_DB == nil {
+			response.OkWithDetailed(gin.H{
+				"needInit": true,
+			}, "前往初始化数据库", c)
+			c.Abort()
+		} else {
+			c.Next()
+		}
+		// 处理请求
+	}
+}

+ 135 - 0
middleware/operation.go

@@ -0,0 +1,135 @@
+package middleware
+
+import (
+	"bytes"
+	"encoding/json"
+	"io/ioutil"
+	"net/http"
+	"net/url"
+	"strconv"
+	"strings"
+	"sync"
+	"time"
+
+	"log-server/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+	"log-server/global"
+	"log-server/model/system"
+	"log-server/service"
+)
+
+var operationRecordService = service.ServiceGroupApp.SystemServiceGroup.OperationRecordService
+
+var respPool sync.Pool
+
+func init() {
+	respPool.New = func() interface{} {
+		return make([]byte, 1024)
+	}
+}
+
+func OperationRecord() gin.HandlerFunc {
+	return func(c *gin.Context) {
+		var body []byte
+		var userId int
+		if c.Request.Method != http.MethodGet {
+			var err error
+			body, err = ioutil.ReadAll(c.Request.Body)
+			if err != nil {
+				global.GVA_LOG.Error("read body from request error:", zap.Error(err))
+			} else {
+				c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))
+			}
+		} else {
+			query := c.Request.URL.RawQuery
+			query, _ = url.QueryUnescape(query)
+			split := strings.Split(query, "&")
+			m := make(map[string]string)
+			for _, v := range split {
+				kv := strings.Split(v, "=")
+				if len(kv) == 2 {
+					m[kv[0]] = kv[1]
+				}
+			}
+			body, _ = json.Marshal(&m)
+		}
+		claims, _ := utils.GetClaims(c)
+		if claims.ID != 0 {
+			userId = int(claims.ID)
+		} else {
+			id, err := strconv.Atoi(c.Request.Header.Get("x-user-id"))
+			if err != nil {
+				userId = 0
+			}
+			userId = id
+		}
+		record := system.SysOperationRecord{
+			Ip:     c.ClientIP(),
+			Method: c.Request.Method,
+			Path:   c.Request.URL.Path,
+			Agent:  c.Request.UserAgent(),
+			Body:   string(body),
+			UserID: userId,
+		}
+
+		// 上传文件时候 中间件日志进行裁断操作
+		if strings.Index(c.GetHeader("Content-Type"), "multipart/form-data") > -1 {
+			if len(record.Body) > 1024 {
+				// 截断
+				newBody := respPool.Get().([]byte)
+				copy(newBody, record.Body)
+				record.Body = string(newBody)
+				defer respPool.Put(newBody[:0])
+			}
+		}
+
+		writer := responseBodyWriter{
+			ResponseWriter: c.Writer,
+			body:           &bytes.Buffer{},
+		}
+		c.Writer = writer
+		now := time.Now()
+
+		c.Next()
+
+		latency := time.Since(now)
+		record.ErrorMessage = c.Errors.ByType(gin.ErrorTypePrivate).String()
+		record.Status = c.Writer.Status()
+		record.Latency = latency
+		record.Resp = writer.body.String()
+
+		if strings.Index(c.Writer.Header().Get("Pragma"), "public") > -1 ||
+			strings.Index(c.Writer.Header().Get("Expires"), "0") > -1 ||
+			strings.Index(c.Writer.Header().Get("Cache-Control"), "must-revalidate, post-check=0, pre-check=0") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Type"), "application/force-download") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Type"), "application/octet-stream") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Type"), "application/vnd.ms-excel") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Type"), "application/download") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Disposition"), "attachment") > -1 ||
+			strings.Index(c.Writer.Header().Get("Content-Transfer-Encoding"), "binary") > -1 {
+			if len(record.Resp) > 1024 {
+				// 截断
+				newBody := respPool.Get().([]byte)
+				copy(newBody, record.Resp)
+				record.Body = string(newBody)
+				defer respPool.Put(newBody[:0])
+			}
+		}
+
+		if err := operationRecordService.CreateSysOperationRecord(record); err != nil {
+			global.GVA_LOG.Error("create operation record error:", zap.Error(err))
+		}
+	}
+}
+
+type responseBodyWriter struct {
+	gin.ResponseWriter
+	body *bytes.Buffer
+}
+
+func (r responseBodyWriter) Write(b []byte) (int, error) {
+	r.body.Write(b)
+	return r.ResponseWriter.Write(b)
+}

+ 28 - 0
model/common/request/common.go

@@ -0,0 +1,28 @@
+package request
+
+// PageInfo Paging common input parameter structure
+type PageInfo struct {
+	Page     int    `json:"page" form:"page"`         // 页码
+	PageSize int    `json:"pageSize" form:"pageSize"` // 每页大小
+	Keyword  string `json:"keyword" form:"keyword"`   //关键字
+}
+
+// GetById Find by id structure
+type GetById struct {
+	ID int `json:"id" form:"id"` // 主键ID
+}
+
+func (r *GetById) Uint() uint {
+	return uint(r.ID)
+}
+
+type IdsReq struct {
+	Ids []int `json:"ids" form:"ids"`
+}
+
+// GetAuthorityId Get role by id structure
+type GetAuthorityId struct {
+	AuthorityId uint `json:"authorityId" form:"authorityId"` // 角色ID
+}
+
+type Empty struct{}

+ 8 - 0
model/common/response/common.go

@@ -0,0 +1,8 @@
+package response
+
+type PageResult struct {
+	List     interface{} `json:"list"`
+	Total    int64       `json:"total"`
+	Page     int         `json:"page"`
+	PageSize int         `json:"pageSize"`
+}

+ 55 - 0
model/common/response/response.go

@@ -0,0 +1,55 @@
+package response
+
+import (
+	"net/http"
+
+	"github.com/gin-gonic/gin"
+)
+
+type Response struct {
+	Code int         `json:"code"`
+	Data interface{} `json:"data"`
+	Msg  string      `json:"msg"`
+}
+
+const (
+	ERROR   = 7
+	SUCCESS = 0
+)
+
+func Result(code int, data interface{}, msg string, c *gin.Context) {
+	// 开始时间
+	c.JSON(http.StatusOK, Response{
+		code,
+		data,
+		msg,
+	})
+}
+
+func Ok(c *gin.Context) {
+	Result(SUCCESS, map[string]interface{}{}, "操作成功", c)
+}
+
+func OkWithMessage(message string, c *gin.Context) {
+	Result(SUCCESS, map[string]interface{}{}, message, c)
+}
+
+func OkWithData(data interface{}, c *gin.Context) {
+	Result(SUCCESS, data, "查询成功", c)
+}
+
+func OkWithDetailed(data interface{}, message string, c *gin.Context) {
+	Result(SUCCESS, data, message, c)
+}
+
+func Fail(c *gin.Context) {
+	Result(ERROR, map[string]interface{}{}, "操作失败", c)
+}
+
+func FailWithMessage(message string, c *gin.Context) {
+	Result(ERROR, map[string]interface{}{}, message, c)
+}
+
+func FailWithDetailed(data interface{}, message string, c *gin.Context) {
+	Result(ERROR, data, message, c)
+}

+ 24 - 0
model/example/exa_breakpoint_continue.go

@@ -0,0 +1,24 @@
+package example
+
+import (
+	"log-server/global"
+)
+
+// file struct, 文件结构体
+type ExaFile struct {
+	global.GVA_MODEL
+	FileName     string
+	FileMd5      string
+	FilePath     string
+	ExaFileChunk []ExaFileChunk
+	ChunkTotal   int
+	IsFinish     bool
+}
+
+// file chunk struct, 切片结构体
+type ExaFileChunk struct {
+	global.GVA_MODEL
+	ExaFileID       uint
+	FileChunkNumber int
+	FileChunkPath   string
+}

+ 15 - 0
model/example/exa_customer.go

@@ -0,0 +1,15 @@
+package example
+
+import (
+	"log-server/global"
+	"log-server/model/system"
+)
+
+type ExaCustomer struct {
+	global.GVA_MODEL
+	CustomerName       string         `json:"customerName" form:"customerName" gorm:"comment:客户名"`                // 客户名
+	CustomerPhoneData  string         `json:"customerPhoneData" form:"customerPhoneData" gorm:"comment:客户手机号"`    // 客户手机号
+	SysUserID          uint           `json:"sysUserId" form:"sysUserId" gorm:"comment:管理ID"`                     // 管理ID
+	SysUserAuthorityID uint           `json:"sysUserAuthorityID" form:"sysUserAuthorityID" gorm:"comment:管理角色ID"` // 管理角色ID
+	SysUser            system.SysUser `json:"sysUser" form:"sysUser" gorm:"comment:管理详情"`                         // 管理详情
+}

+ 8 - 0
model/example/exa_excel.go

@@ -0,0 +1,8 @@
+package example
+
+import "log-server/model/system"
+
+type ExcelInfo struct {
+	FileName string               `json:"fileName"` // 文件名
+	InfoList []system.SysBaseMenu `json:"infoList"`
+}

+ 17 - 0
model/example/exa_file_upload_download.go

@@ -0,0 +1,17 @@
+package example
+
+import (
+	"log-server/global"
+)
+
+type ExaFileUploadAndDownload struct {
+	global.GVA_MODEL
+	Name string `json:"name" gorm:"comment:文件名"` // 文件名
+	Url  string `json:"url" gorm:"comment:文件地址"` // 文件地址
+	Tag  string `json:"tag" gorm:"comment:文件标签"` // 文件标签
+	Key  string `json:"key" gorm:"comment:编号"`   // 编号
+}
+
+func (ExaFileUploadAndDownload) TableName() string {
+	return "exa_file_upload_and_downloads"
+}

+ 11 - 0
model/example/response/exa_breakpoint_continue.go

@@ -0,0 +1,11 @@
+package response
+
+import "log-server/model/example"
+
+type FilePathResponse struct {
+	FilePath string `json:"filePath"`
+}
+
+type FileResponse struct {
+	File example.ExaFile `json:"file"`
+}

+ 7 - 0
model/example/response/exa_customer.go

@@ -0,0 +1,7 @@
+package response
+
+import "log-server/model/example"
+
+type ExaCustomerResponse struct {
+	Customer example.ExaCustomer `json:"customer"`
+}

+ 7 - 0
model/example/response/exa_file_upload_download.go

@@ -0,0 +1,7 @@
+package response
+
+import "log-server/model/example"
+
+type ExaFileResponse struct {
+	File example.ExaFileUploadAndDownload `json:"file"`
+}

+ 0 - 0
model/log/log_coding.go


この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません