增加用户/权限/菜单功能
							parent
							
								
									3d2f15ef13
								
							
						
					
					
						commit
						da4b26cdf6
					
				|  | @ -13,3 +13,6 @@ | |||
| 
 | ||||
| # Dependency directories (remove the comment below to include it) | ||||
| # vendor/ | ||||
| .idea | ||||
| .DS_Store | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										333
									
								
								README.md
								
								
								
								
							
							
						
						
									
										333
									
								
								README.md
								
								
								
								
							|  | @ -1,2 +1,335 @@ | |||
| # goweb-gin-demo | ||||
| go web脚手架 | ||||
| 
 | ||||
| # [web框架gin](https://gin-gonic.com/zh-cn/docs/introduction/) | ||||
| ## 特性 | ||||
| #### 快速 | ||||
| 基于 Radix 树的路由,小内存占用。没有反射。可预测的 API 性能。 | ||||
| 
 | ||||
| #### 支持中间件 | ||||
| 传入的 HTTP 请求可以由一系列中间件和最终操作来处理。 例如:Logger,Authorization,GZIP,最终操作 DB。 | ||||
| 
 | ||||
| #### Crash 处理 | ||||
| Gin 可以 catch 一个发生在 HTTP 请求中的 panic 并 recover 它。这样,你的服务器将始终可用。例如,你可以向 Sentry 报告这个 panic! | ||||
| 
 | ||||
| #### JSON 验证 | ||||
| Gin 可以解析并验证请求的 JSON,例如检查所需值的存在。 | ||||
| 
 | ||||
| #### 路由组 | ||||
| 更好地组织路由。是否需要授权,不同的 API 版本…… 此外,这些组可以无限制地嵌套而不会降低性能。 | ||||
| 
 | ||||
| #### 错误管理 | ||||
| Gin 提供了一种方便的方法来收集 HTTP 请求期间发生的所有错误。最终,中间件可以将它们写入日志文件,数据库并通过网络发送。 | ||||
| 
 | ||||
| #### 内置渲染 | ||||
| Gin 为 JSON,XML 和 HTML 渲染提供了易于使用的 API。 | ||||
| 
 | ||||
| #### 可扩展性 | ||||
| 新建一个中间件非常简单,去查看 [示例代码](https://gin-gonic.com/zh-cn/docs/examples/) 吧。 | ||||
| 
 | ||||
| ## 服务创建及启动 | ||||
| 
 | ||||
| [官方demo](https://gin-gonic.com/zh-cn/docs/quickstart/)   | ||||
| 把ping映射在参数为(c *gin.Context)的方法   | ||||
| ``` | ||||
| package main | ||||
| 
 | ||||
| import "github.com/gin-gonic/gin" | ||||
| 
 | ||||
| func main() { | ||||
| 	r := gin.Default() | ||||
| 	r.GET("/ping", func(c *gin.Context) { | ||||
| 		c.JSON(200, gin.H{ | ||||
| 			"message": "pong", | ||||
| 		}) | ||||
| 	}) | ||||
| 	r.Run() // 监听并在 0.0.0.0:8080 上启动服务 | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| endless 创建服务   | ||||
| ``` | ||||
| func main() { | ||||
| 	mux1 := mux.NewRouter() | ||||
| 	mux1.HandleFunc("/hello", handler). | ||||
| 		Methods("GET") | ||||
| 
 | ||||
| 	srv := endless.NewServer("localhost:4244", mux1) | ||||
| 	srv.SignalHooks[endless.PRE_SIGNAL][syscall.SIGUSR1] = append( | ||||
| 		srv.SignalHooks[endless.PRE_SIGNAL][syscall.SIGUSR1], | ||||
| 		preSigUsr1) | ||||
| 	srv.SignalHooks[endless.POST_SIGNAL][syscall.SIGUSR1] = append( | ||||
| 		srv.SignalHooks[endless.POST_SIGNAL][syscall.SIGUSR1], | ||||
| 		postSigUsr1) | ||||
| 
 | ||||
| 	err := srv.ListenAndServe() | ||||
| 	if err != nil { | ||||
| 		log.Println(err) | ||||
| 	} | ||||
| 	log.Println("Server on 4244 stopped") | ||||
| 
 | ||||
| 	os.Exit(0) | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 通过Swagger测试接口 | ||||
| [官方地址](https://github.com/swaggo) ,其中包含`swag`可执行程序和`gin-swagger`go web模块, [使用手册](https://github.com/swaggo/gin-swagger)   | ||||
| 
 | ||||
| ``` | ||||
| // @BasePath /api/v1 | ||||
| 
 | ||||
| // PingExample godoc | ||||
| // @Summary ping example | ||||
| // @Schemes | ||||
| // @Description do ping | ||||
| // @Tags example | ||||
| // @Accept json | ||||
| // @Produce json | ||||
| // @Success 200 {string} Helloworld | ||||
| // @Router /example/helloworld [get] | ||||
| func Helloworld(g *gin.Context)  { | ||||
| 	g.JSON(http.StatusOK,"helloworld") | ||||
| } | ||||
| ```   | ||||
| 
 | ||||
| 生成文档`swag init`  | ||||
| ``` | ||||
| docs/ | ||||
| ├── docs.go | ||||
| ├── swagger.json | ||||
| └── swagger.yaml | ||||
| ``` | ||||
| 
 | ||||
| > 需要注意把docs文件夹导入到代码中 `import "github.com/ymm135/goweb-gin-demo/docs"`   | ||||
| 
 | ||||
| # 验证码获取及校验   | ||||
| 首选通过`/base/captcha`获取验证码,其中包含验证码图片地址:`data:image/png;...`   | ||||
| ``` | ||||
| { | ||||
|     "code":0, | ||||
|     "data":{ | ||||
|         "captchaId":"hrzSvHSdGo4Emm9oG9OY", | ||||
|         "picPath":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPAAAABQCAMAAAAQlwhOAAAA81BMVEUAAAA1OwZVWyZrcTzN057c4q3AxpHp77pqcDtQViFIThnS2KOEilWPlWDBx5KHjVhBRxJPVSC+xI9SWCO+xI88Qg2hp3KTmWRgZjHi6LODiVSboWxcYi0/RRBbYSygpnF/hVB1e0ZudD/V26Y5PwqmrHfS2KNLURyzuYSts36PlWDm7Ldscj1YXimfpXDY3qkwNgGnrXhRVyJMUh3DyZTm7LfR16J9g05EShVbYSzf5bBobjlFSxbM0p1bYSzg5rF+hE/X3ajt876Jj1rDyZRVWyZ1e0bu9L/w9sG6wIuornm5v4pvdUCRl2JIThna4KuOlF//bP9NAAAAAXRSTlMAQObYZgAABnhJREFUeJzsW21LIz0XznHdL0VtWVmr0CqKH4rIilTQooJ9QVCx///nPDidSc5bJkmbGfW5vW5utzPJ5Jwr18lJMi/mS+Hosx1oGUdH343xcsPrvx3fpWT851M8aQsK3z8q4yt6eLf6528zXrULne8VYXx3VzD++zeG8YAdv2/inYbrjz/HmRtdX+HBYDDEx+/vmRlfX18bc3ycm3Ea8FAZDIeUcW5j14tFfoUp7gPlLBkOG3XGmEXqBVeJ9e/vg4xTXWgTI5aRFEDxnwPiO2vKrcYwGo1q+RZkC9gT5fEHZrMGGZ820+yorhAQymNTUi8Zu57IjdPThhjXADi4wkDEz4wW+JLBCo7YStkVU8nYNMeZYDtvc2ywlgfotGUFhpcUwZDXHYnt7XyMJStLyQaxlZ6GMcBJedg45Ux8gQ9Jd9q4NEXzNA78k5OTii6jTOeyNhCzomCjsfLRpuXqf6wpGbJwUvZNQZk3zIzZ9hqBtqIQxqAryLqKZboiJAUPXGTIb27N5rjNuekQfEWfA3S7XcU1g9PQA45OlS/Q45ItDQRj01xrsQ7SWwOm67e+qvzw8MAEZnXQCRQPADLA7STWjMhPwn1FYZ9tVPBAxrbxBbQ7IRUGoXADjJ+eCOPKHq3kscyC8cPLi6oEX6KrJRSm+V/r+VrEVn5SnHBXHhm/wniKKV2/uLhADdkytcdc5LoGAd4MPkoM6uQxAFzh8p7yyt8JbxzPueXPC8QGxSfAmWJLLjjh7e0Nm0hhDACTRMpsWTGlCk8mE0LX2NVl6bWafUrmZ2dn0hbfUxaMSaW0QVx4mESZKDydTqnhiUEzr1tiIdc5IzRIVYW1/MabSHDfTLSlXA2ABtmUnXdRaNx214DZQhVYY36PAXCP+RwKK9yRDYcuEQZEn6OZ0VG2RVtbW/KGB6ApxmepWpZ6RY5QuNMRjBMCwy0IlBaAbvSqkkJhdU6Zy+qKT1UN1cUIzyVf61kEwOjLwiqceyLL8MkWbP6dz+f+gAW6q+JbCtJmjOPiuvjQVmxYgXu9HkpDwkY5IHbKFub6sk1M0m4TpSx31l1qeXpQrSp3L3bE9dR8jFgAmJ2dHX4boM4Gim1GeV15nZE4leszq3fdZGws77B9QpzEqHncXoTDNUwi66lmWEbyVVF2zErSJ3Vo0jfGv6dORlQLHklYDe+VolCpDTwGuCU3RYe8jUCwEakwC7VagfWUx5tjMcCXaJ4L14B/yqtzkI2s+iEcsZcOxoub+MOICNlAsDA7ch1Um6U1gRUX8N0C/6IjgnBcHjY1oU0M8V2Q8SrsFmLVmXPN7W2D+saEJrnQpj42CqrtXGARBCiB6DVu3TkD7LbM+fm5XDuUzwXQPtl7d8Gu22yfW51wMMWPck/fETtqD7tevb29xX1AnqkUCksyQmF1LjNI/mpU2X0cTp/3SVnNM43YQPEkY01hoJs9fK9DtYwV1m4DAF6BQdencPgtgzrmouu9dfGTAeovSNQa1hUGOji73a5V+JmGZjpfgH06ZLk16aKxwU5lohRgFnkvXSoMwGaGrv31/PysNpGA/f19VSGfe7Y3vJm25DubxW/Q2AK0xgOVb9r6ZJ9wMX1tpVX96JDnhTTp4OpQvMgS7Qbw1WXtWOjL62MNaa31+9yYuxfT6XRcoYjmtSFGUO382+9LxhIH0daFwjhuO0AeeWcg60wIi6QG8TCMg4MwY0DmhMJq1s1C1gjC1XjGtwGTdxERfMlzArSkTphlUjE0e8bacI6AXYDF2e0lGz5HloAYXBXoW/uNMRwO9yrGNY8tQpR7vUTGy2LVa4CGkLgrsQajEIjCfF5jbGvsp/JdLgMKtwB5S4B0f15jS2TUp3D7aNn2Z1JVPgj4f8dg8J9j/NkO+JD0GvRdc360haQXv8uvegr8lsV7mXxqFgl8x+O7f9Xv378F472978E4AeN/7ve3VfgHP8iB1C+mLL7bd7wlwl9MeZD0pfZ0PRuNoA2F0UtpCbhd4xoPXvI1FYXdNL7j4q978rIxXl7aZby7u5tSfTwuGVcnNv9sNS/f6m723FsjiW+lsMVnf5jMUT2vKF4dWwuPgfKvxdcqvJjjj4jJ55T1Xxc/PnoZ32zqW4HLLK1IEL4jxHixCDD2Fdzc5GB8edkUY4wEhWuQS+HXLO3kwy96eJi7/dfXr8X41y/C+PAwP+PcDW6IphX+QQD/CwAA//+tw0a4PXRiBgAAAABJRU5ErkJggg==" | ||||
|     }, | ||||
|     "msg":"验证码获取成功" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 在用户登录时,请求的数据为:   | ||||
| ``` | ||||
| { | ||||
| 	  "username": "admin", | ||||
| 	  "password": "123456", | ||||
| 	  "captcha": "153842", | ||||
| 	  "captchaId": "hrzSvHSdGo4Emm9oG9OY" | ||||
| } | ||||
| ```   | ||||
| 携带着验证码及验证码的Id, 框架通过Id去匹配输入的验证码。   | ||||
| 登录城后后返回信息:   | ||||
| ``` | ||||
| { | ||||
|     "code":0, | ||||
|     "data":{ | ||||
|         "user":{ | ||||
|             "ID":1, | ||||
|             "CreatedAt":"2021-10-25T14:53:31Z", | ||||
|             "UpdatedAt":"2021-10-25T14:53:31Z", | ||||
|             "uuid":"8e600b7f-3297-4979-a445-d218205ef9a6", | ||||
|             "userName":"admin", | ||||
|             "nickName":"超级管理员", | ||||
|             "sideMode":"dark", | ||||
|             "headerImg":"https:///qmplusimg.henrongyi.top/gva_header.jpg", | ||||
|             "baseColor":"#fff", | ||||
|             "activeColor":"#1890ff", | ||||
|             "authorityId":"888", | ||||
|             "authority":{ | ||||
|                 "CreatedAt":"2021-10-25T14:53:31Z", | ||||
|                 "UpdatedAt":"2021-10-25T14:53:31Z", | ||||
|                 "DeletedAt":null, | ||||
|                 "authorityId":"888", | ||||
|                 "authorityName":"普通用户", | ||||
|                 "parentId":"0", | ||||
|                 "dataAuthorityId":null, | ||||
|                 "children":null, | ||||
|                 "defaultRouter":"dashboard" | ||||
|             }, | ||||
|             "authorities":[ | ||||
|                 { | ||||
|                     "CreatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "UpdatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "DeletedAt":null, | ||||
|                     "authorityId":"888", | ||||
|                     "authorityName":"普通用户", | ||||
|                     "parentId":"0", | ||||
|                     "dataAuthorityId":null, | ||||
|                     "children":null, | ||||
|                     "defaultRouter":"dashboard" | ||||
|                 }, | ||||
|                 { | ||||
|                     "CreatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "UpdatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "DeletedAt":null, | ||||
|                     "authorityId":"8881", | ||||
|                     "authorityName":"普通用户子角色", | ||||
|                     "parentId":"888", | ||||
|                     "dataAuthorityId":null, | ||||
|                     "children":null, | ||||
|                     "defaultRouter":"dashboard" | ||||
|                 }, | ||||
|                 { | ||||
|                     "CreatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "UpdatedAt":"2021-10-25T14:53:31Z", | ||||
|                     "DeletedAt":null, | ||||
|                     "authorityId":"9528", | ||||
|                     "authorityName":"测试角色", | ||||
|                     "parentId":"0", | ||||
|                     "dataAuthorityId":null, | ||||
|                     "children":null, | ||||
|                     "defaultRouter":"dashboard" | ||||
|                 } | ||||
|             ] | ||||
|         }, | ||||
|         "token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiOGU2MDBiN2YtMzI5Ny00OTc5LWE0NDUtZDIxODIwNWVmOWE2IiwiSUQiOjEsIlVzZXJuYW1lIjoiYWRtaW4iLCJOaWNrTmFtZSI6Iui2hee6p-euoeeQhuWRmCIsIkF1dGhvcml0eUlkIjoiODg4IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTYzNjAyMDY2MSwiaXNzIjoicW1QbHVzIiwibmJmIjoxNjM1NDE0ODYxfQ.-E9YNI6YV3XLbC5GgXiftTBI7A5ZiIheH5dqwgwnaDA", | ||||
|         "expiresAt":1636020661000 | ||||
|     }, | ||||
|     "msg":"登录成功" | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| # 获取菜单项 | ||||
| api接口`/api/menu/getMenu`   | ||||
| 
 | ||||
| 头文件携带信息: | ||||
| ``` | ||||
| x-token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVVUlEIjoiOGU2MDBiN2YtMzI5Ny00OTc5LWE0NDUtZDIxODIwNWVmOWE2IiwiSUQiOjEsIlVzZXJuYW1lIjoiYWRtaW4iLCJOaWNrTmFtZSI6Iui2hee6p-euoeeQhuWRmCIsIkF1dGhvcml0eUlkIjoiODg4IiwiQnVmZmVyVGltZSI6ODY0MDAsImV4cCI6MTYzNjAyMDY2MSwiaXNzIjoicW1QbHVzIiwibmJmIjoxNjM1NDE0ODYxfQ.-E9YNI6YV3XLbC5GgXiftTBI7A5ZiIheH5dqwgwnaDA | ||||
| x-user-id: 1 | ||||
| ```   | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| ## 使用[Casbin](https://github.com/casbin/casbin) 控制权限 | ||||
| Casbin is a powerful and efficient open-source access control library. It provides support for enforcing authorization based on various access control models.   | ||||
| 
 | ||||
| 可以 [在线](https://casbin.org/editor/) 书写规则:     | ||||
| 
 | ||||
|    | ||||
| 
 | ||||
| 创建一个Casbin enforcer   | ||||
| ``` | ||||
| import "github.com/casbin/casbin/v2" | ||||
| 
 | ||||
| e, err := casbin.NewEnforcer("path/to/model.conf", "path/to/policy.csv") | ||||
| ```  | ||||
| 
 | ||||
| 检查权限   | ||||
| ``` | ||||
| sub := "alice" // the user that wants to access a resource. | ||||
| obj := "data1" // the resource that is going to be accessed. | ||||
| act := "read" // the operation that the user performs on the resource. | ||||
| 
 | ||||
| ok, err := e.Enforce(sub, obj, act) | ||||
| 
 | ||||
| if err != nil { | ||||
|     // handle err | ||||
| } | ||||
| 
 | ||||
| if ok == true { | ||||
|     // permit alice to read data1 | ||||
| } else { | ||||
|     // deny the request, show an error | ||||
| } | ||||
| 
 | ||||
| // You could use BatchEnforce() to enforce some requests in batches. | ||||
| // This method returns a bool slice, and this slice's index corresponds to the row index of the two-dimensional array. | ||||
| // e.g. results[0] is the result of {"alice", "data1", "read"} | ||||
| results, err := e.BatchEnforce([][]interface{}{{"alice", "data1", "read"}, {"bob", "data2", "write"}, {"jack", "data3", "read"}}) | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| # 文件上传及下载   | ||||
| 
 | ||||
| # 反射reflect  | ||||
| [官方文档](https://pkg.go.dev/reflect)   | ||||
| 
 | ||||
| 包反射实现运行时反射,允许程序操作任意类型的对象。典型的用法是取一个静态类型 interface{} 的值,通过调用 TypeOf 提取其动态类型信息,返回一个 Type。   | ||||
| 
 | ||||
| 对 ValueOf 的调用返回一个表示运行时数据的值。Zero 接受一个 Type 并返回一个表示该类型的零值的 Value。     | ||||
| 
 | ||||
| 有关 Go 中反射的介绍,请参阅 [“反射定律”](https://golang.org/doc/articles/laws_of_reflection.html)    | ||||
| 
 | ||||
| 动态调用有参数的函数   | ||||
| ``` | ||||
| type T struct{} | ||||
| 
 | ||||
| func main() { | ||||
|     name := "Do" | ||||
|     t := &T{} | ||||
|     a := reflect.ValueOf(1111) | ||||
|     b := reflect.ValueOf("world") | ||||
|     in := []reflect.Value{a, b} | ||||
|     reflect.ValueOf(t).MethodByName(name).Call(in) | ||||
| } | ||||
| 
 | ||||
| func (t *T) Do(a int, b string) { | ||||
|     fmt.Println("hello" + b, a) | ||||
| } | ||||
| ```  | ||||
| 
 | ||||
| # 疑问及拓展 | ||||
| #### 为什么函数或方法中变量名很多都是大写字母开始的? | ||||
| - 任何需要对外暴露的名字必须以大写字母开头,不需要对外暴露的则应该以小写字母开头。      | ||||
| 但是gin-vue-admin代码中有很多大小,连方法参数都是大写字母开始的?   | ||||
| ``` | ||||
| # 函数中的变量Router | ||||
| Router := initialize.Routers() | ||||
| 
 | ||||
| # 方法参数Router | ||||
| func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { | ||||
| ``` | ||||
| 
 | ||||
| #### unsupported Scan, storing driver.Value type []uint8 into type *time.Time | ||||
| 需要在连接数据库时增加参数:  | ||||
| ``` | ||||
| db, err := sqlx.Connect("mysql", "myuser:mypass@tcp(127.0.0.1:3306)/mydb?parseTime=true")  | ||||
| ``` | ||||
| 
 | ||||
| Sets the location for time.Time values (when using parseTime=true). "Local" sets the system's location. See [time.LoadLocation](https://pkg.go.dev/time#LoadLocation) for details.   | ||||
| 
 | ||||
|    | ||||
| ####  Error 1075: Incorrect table definition; there can be only one auto column and it must be defined as a key | ||||
| 
 | ||||
| 日志信息 | ||||
| > 2021/10/29 11:44:55 /Users/zero/go/pkg/mod/github.com/casbin/gorm-adapter/v3@v3.4.4/adapter.go:373  | ||||
| > Error 1075: Incorrect table definition; there can be only one auto column and it must be defined as a key | ||||
| >  [7.259ms] [rows:0] ALTER TABLE `casbin_rule` ADD `id` bigint unsigned AUTO_INCREMENT   | ||||
| 
 | ||||
| 断点调试gorm-adapter/v3@v3.4.4/adapter.go, 最后上网搜索发现版本问题。 把版本都降低了 | ||||
| 	github.com/casbin/casbin/v2 v2.11.0 | ||||
| 	github.com/casbin/gorm-adapter/v3 v3.0.2 | ||||
| 	 | ||||
| 
 | ||||
|    | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|    | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -0,0 +1,11 @@ | |||
| package api | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/api/web" | ||||
| ) | ||||
| 
 | ||||
| type ApiGroup struct { | ||||
| 	ApiGroup   web.ApiGroup | ||||
| } | ||||
| 
 | ||||
| var ApiGroupApp = new(ApiGroup) | ||||
|  | @ -0,0 +1,41 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/mojocn/base64Captcha" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	webRes "github.com/ymm135/goweb-gin-demo/model/web/response" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
| 
 | ||||
| // 当开启多服务器部署时,替换下面的配置,使用redis共享存储验证码
 | ||||
| //var store = captcha.NewDefaultRedisStore()
 | ||||
| var store = base64Captcha.DefaultMemStore | ||||
| 
 | ||||
| type BaseApi struct { | ||||
| } | ||||
| 
 | ||||
| // @Tags Base
 | ||||
| // @Summary 生成验证码
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @accept application/json
 | ||||
| // @Produce application/json
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"验证码获取成功"}"
 | ||||
| // @Router /base/captcha [post]
 | ||||
| func (b *BaseApi) Captcha(c *gin.Context) { | ||||
| 	// 字符,公式,验证码配置
 | ||||
| 	// 生成默认数字的driver
 | ||||
| 	driver := base64Captcha.NewDriverDigit(global.GLOBAL_CONFIG.Captcha.ImgHeight, global.GLOBAL_CONFIG.Captcha.ImgWidth, global.GLOBAL_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.GLOBAL_LOG.Error("验证码获取失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("验证码获取失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithDetailed(webRes.SysCaptchaResponse{ | ||||
| 			CaptchaId: id, | ||||
| 			PicPath:   b64s, | ||||
| 		}, "验证码获取成功", c) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/service" | ||||
| ) | ||||
| 
 | ||||
| type ApiGroup struct { | ||||
| 	BaseApi | ||||
| 	JwtApi | ||||
| 	AuthorityMenuApi | ||||
| } | ||||
| 
 | ||||
| var userService = service.ServiceGroupApp.UserService | ||||
| var jwtService =  service.ServiceGroupApp.JwtService | ||||
| var menuService = service.ServiceGroupApp.MenuService | ||||
| var baseMenuService = service.ServiceGroupApp.BaseMenuService | ||||
| 
 | ||||
|  | @ -0,0 +1,30 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
| 
 | ||||
| type JwtApi struct { | ||||
| } | ||||
| 
 | ||||
| // @Tags Jwt
 | ||||
| // @Summary jwt加入黑名单
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @accept application/json
 | ||||
| // @Produce application/json
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"拉黑成功"}"
 | ||||
| // @Router /jwt/jsonInBlacklist [post]
 | ||||
| func (j *JwtApi) JsonInBlacklist(c *gin.Context) { | ||||
| 	token := c.Request.Header.Get("x-token") | ||||
| 	jwt := web.JwtBlacklist{Jwt: token} | ||||
| 	if err := jwtService.JsonInBlacklist(jwt); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("jwt作废失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("jwt作废失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithMessage("jwt作废成功", c) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,227 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	systemReq "github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| 	systemRes "github.com/ymm135/goweb-gin-demo/model/web/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/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 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @Router /menu/getMenu [post]
 | ||||
| func (a *AuthorityMenuApi) GetMenu(c *gin.Context) { | ||||
| 	if err, menus := menuService.GetMenuTree(utils.GetUserAuthorityId(c)); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("获取失败", c) | ||||
| 	} else { | ||||
| 		if menus == nil { | ||||
| 			menus = []web.SysMenu{} | ||||
| 		} | ||||
| 		response.OkWithDetailed(systemRes.SysMenusResponse{Menus: menus}, "获取成功", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @Tags AuthorityMenu
 | ||||
| // @Summary 获取用户动态路由
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @Produce  application/json
 | ||||
| // @Param data body request.Empty true "空"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @Router /menu/getBaseMenuTree [post]
 | ||||
| func (a *AuthorityMenuApi) GetBaseMenuTree(c *gin.Context) { | ||||
| 	if err, menus := menuService.GetBaseMenuTree(); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"添加成功"}"
 | ||||
| // @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.GLOBAL_LOG.Error("添加失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @Router /menu/getMenuAuthority [post]
 | ||||
| func (a *AuthorityMenuApi) GetMenuAuthority(c *gin.Context) { | ||||
| 	var param request.GetAuthorityId | ||||
| 	_ = c.ShouldBindJSON(¶m) | ||||
| 	if err := utils.Verify(param, utils.AuthorityIdVerify); err != nil { | ||||
| 		response.FailWithMessage(err.Error(), c) | ||||
| 		return | ||||
| 	} | ||||
| 	if err, menus := menuService.GetMenuAuthority(¶m); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", 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 web.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"添加成功"}"
 | ||||
| // @Router /menu/addBaseMenu [post]
 | ||||
| func (a *AuthorityMenuApi) AddBaseMenu(c *gin.Context) { | ||||
| 	var menu web.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.GLOBAL_LOG.Error("添加失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
 | ||||
| // @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.GLOBAL_LOG.Error("删除失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("删除失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithMessage("删除成功", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @Tags Menu
 | ||||
| // @Summary 更新菜单
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @accept application/json
 | ||||
| // @Produce application/json
 | ||||
| // @Param data body web.SysBaseMenu true "路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"更新成功"}"
 | ||||
| // @Router /menu/updateBaseMenu [post]
 | ||||
| func (a *AuthorityMenuApi) UpdateBaseMenu(c *gin.Context) { | ||||
| 	var menu web.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.GLOBAL_LOG.Error("更新失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @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 err, menu := baseMenuService.GetBaseMenuById(idInfo.ID); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @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 err, menuList, total := menuService.GetInfoList(); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("获取失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithDetailed(response.PageResult{ | ||||
| 			List:     menuList, | ||||
| 			Total:    total, | ||||
| 			Page:     pageInfo.Page, | ||||
| 			PageSize: pageInfo.PageSize, | ||||
| 		}, "获取成功", c) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,311 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/go-redis/redis/v8" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	systemReq "github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| 	webRes "github.com/ymm135/goweb-gin-demo/model/web/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils" | ||||
| 	"go.uber.org/zap" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| // @Tags Base
 | ||||
| // @Summary 用户登录
 | ||||
| // @Produce  application/json
 | ||||
| // @Param data body systemReq.Login true "用户名, 密码, 验证码"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"登陆成功"}"
 | ||||
| // @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 := &web.SysUser{Username: l.Username, Password: l.Password} | ||||
| 		if err, user := userService.Login(u); err != nil { | ||||
| 			global.GLOBAL_LOG.Error("登陆失败! 用户名不存在或者密码错误!", zap.Any("err", err)) | ||||
| 			response.FailWithMessage("用户名不存在或者密码错误", c) | ||||
| 		} else { | ||||
| 			b.tokenNext(c, *user) | ||||
| 		} | ||||
| 	} else { | ||||
| 		response.FailWithMessage("验证码错误", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 登录以后签发jwt
 | ||||
| func (b *BaseApi) tokenNext(c *gin.Context, user web.SysUser) { | ||||
| 	j := &utils.JWT{SigningKey: []byte(global.GLOBAL_CONFIG.JWT.SigningKey)} // 唯一签名
 | ||||
| 	claims := systemReq.CustomClaims{ | ||||
| 		UUID:        user.UUID, | ||||
| 		ID:          user.ID, | ||||
| 		NickName:    user.NickName, | ||||
| 		Username:    user.Username, | ||||
| 		AuthorityId: user.AuthorityId, | ||||
| 		BufferTime:  global.GLOBAL_CONFIG.JWT.BufferTime, // 缓冲时间1天 缓冲时间内会获得新的token刷新令牌 此时一个用户会存在两个有效令牌 但是前端只留一个 另一个会丢失
 | ||||
| 		StandardClaims: jwt.StandardClaims{ | ||||
| 			NotBefore: time.Now().Unix() - 1000,                              // 签名生效时间
 | ||||
| 			ExpiresAt: time.Now().Unix() + global.GLOBAL_CONFIG.JWT.ExpiresTime, // 过期时间 7天  配置文件
 | ||||
| 			Issuer:    "qmPlus",                                              // 签名的发行者
 | ||||
| 		}, | ||||
| 	} | ||||
| 	token, err := j.CreateToken(claims) | ||||
| 	if err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取token失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("获取token失败", c) | ||||
| 		return | ||||
| 	} | ||||
| 	if !global.GLOBAL_CONFIG.System.UseMultipoint { | ||||
| 		response.OkWithDetailed(webRes.LoginResponse{ | ||||
| 			User:      user, | ||||
| 			Token:     token, | ||||
| 			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000, | ||||
| 		}, "登录成功", c) | ||||
| 		return | ||||
| 	} | ||||
| 
 | ||||
| 	if err, jwtStr := jwtService.GetRedisJWT(user.Username); err == redis.Nil { | ||||
| 		if err := jwtService.SetRedisJWT(token, user.Username); err != nil { | ||||
| 			global.GLOBAL_LOG.Error("设置登录状态失败!", zap.Any("err", err)) | ||||
| 			response.FailWithMessage("设置登录状态失败", c) | ||||
| 			return | ||||
| 		} | ||||
| 		response.OkWithDetailed(webRes.LoginResponse{ | ||||
| 			User:      user, | ||||
| 			Token:     token, | ||||
| 			ExpiresAt: claims.StandardClaims.ExpiresAt * 1000, | ||||
| 		}, "登录成功", c) | ||||
| 	} else if err != nil { | ||||
| 		global.GLOBAL_LOG.Error("设置登录状态失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("设置登录状态失败", c) | ||||
| 	} else { | ||||
| 		var blackJWT web.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(webRes.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 {string} string "{"success":true,"data":{},"msg":"注册成功"}"
 | ||||
| // @Router /user/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 []web.SysAuthority | ||||
| 	for _, v := range r.AuthorityIds { | ||||
| 		authorities = append(authorities, web.SysAuthority{ | ||||
| 			AuthorityId: v, | ||||
| 		}) | ||||
| 	} | ||||
| 	user := &web.SysUser{Username: r.Username, NickName: r.NickName, Password: r.Password, HeaderImg: r.HeaderImg, AuthorityId: r.AuthorityId, Authorities: authorities} | ||||
| 	err, userReturn := userService.Register(*user) | ||||
| 	if err != nil { | ||||
| 		global.GLOBAL_LOG.Error("注册失败!", zap.Any("err", err)) | ||||
| 		response.FailWithDetailed(webRes.SysUserResponse{User: userReturn}, "注册失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithDetailed(webRes.SysUserResponse{User: userReturn}, "注册成功", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @Tags SysUser
 | ||||
| // @Summary 用户修改密码
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @Produce  application/json
 | ||||
| // @Param data body systemReq.ChangePasswordStruct true "用户名, 原密码, 新密码"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
 | ||||
| // @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 := &web.SysUser{Username: user.Username, Password: user.Password} | ||||
| 	if err, _ := userService.ChangePassword(u, user.NewPassword); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("修改失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @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 err, list, total := userService.GetUserInfoList(pageInfo); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
 | ||||
| // @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.GLOBAL_LOG.Error("修改失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage(err.Error(), c) | ||||
| 	} else { | ||||
| 		claims := utils.GetUserInfo(c) | ||||
| 		j := &utils.JWT{SigningKey: []byte(global.GLOBAL_CONFIG.JWT.SigningKey)} // 唯一签名
 | ||||
| 		claims.AuthorityId = sua.AuthorityId | ||||
| 		if token, err := j.CreateToken(*claims); err != nil { | ||||
| 			global.GLOBAL_LOG.Error("修改失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"修改成功"}"
 | ||||
| // @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.GLOBAL_LOG.Error("修改失败!", zap.Any("err", 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 {string} string "{"success":true,"data":{},"msg":"删除成功"}"
 | ||||
| // @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.GLOBAL_LOG.Error("删除失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("删除失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithMessage("删除成功", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @Tags SysUser
 | ||||
| // @Summary 设置用户信息
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @accept application/json
 | ||||
| // @Produce application/json
 | ||||
| // @Param data body web.SysUser true "ID, 用户名, 昵称, 头像链接"
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"设置成功"}"
 | ||||
| // @Router /user/setUserInfo [put]
 | ||||
| func (b *BaseApi) SetUserInfo(c *gin.Context) { | ||||
| 	var user web.SysUser | ||||
| 	_ = c.ShouldBindJSON(&user) | ||||
| 	if err := utils.Verify(user, utils.IdVerify); err != nil { | ||||
| 		response.FailWithMessage(err.Error(), c) | ||||
| 		return | ||||
| 	} | ||||
| 	if err, ReqUser := userService.SetUserInfo(user); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("设置失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("设置失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "设置成功", c) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // @Tags SysUser
 | ||||
| // @Summary 获取用户信息
 | ||||
| // @Security ApiKeyAuth
 | ||||
| // @accept application/json
 | ||||
| // @Produce application/json
 | ||||
| // @Success 200 {string} string "{"success":true,"data":{},"msg":"获取成功"}"
 | ||||
| // @Router /user/getUserInfo [get]
 | ||||
| func (b *BaseApi) GetUserInfo(c *gin.Context) { | ||||
| 	uuid := utils.GetUserUuid(c) | ||||
| 	if err, ReqUser := userService.GetUserInfo(uuid); err != nil { | ||||
| 		global.GLOBAL_LOG.Error("获取失败!", zap.Any("err", err)) | ||||
| 		response.FailWithMessage("获取失败", c) | ||||
| 	} else { | ||||
| 		response.OkWithDetailed(gin.H{"userInfo": ReqUser}, "获取成功", c) | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,68 @@ | |||
| # jwt configuration | ||||
| jwt: | ||||
|   signing-key: 'qmPlus' | ||||
|   expires-time: 604800 | ||||
|   buffer-time: 86400 | ||||
| 
 | ||||
| # zap logger configuration | ||||
| zap: | ||||
|   level: 'info' | ||||
|   format: 'console' | ||||
|   prefix: '[github.com/flipped-aurora/gin-vue-admin/server]' | ||||
|   director: 'log' | ||||
|   link-name: 'latest_log' | ||||
|   show-line: true | ||||
|   encode-level: 'LowercaseColorLevelEncoder' | ||||
|   stacktrace-key: 'stacktrace' | ||||
|   log-in-console: true | ||||
| 
 | ||||
| # redis configuration | ||||
| redis: | ||||
|   db: 0 | ||||
|   addr: '127.0.0.1:6379' | ||||
|   password: '' | ||||
| 
 | ||||
| # casbin configuration | ||||
| casbin: | ||||
|   model-path: './resource/rbac_model.conf' | ||||
| 
 | ||||
| # system configuration | ||||
| system: | ||||
|   env: 'public'  # Change to "develop" to skip authentication for development mode | ||||
|   addr: 8888 | ||||
|   db-type: 'mysql' | ||||
|   oss-type: 'local'    # 控制oss选择走本期还是 七牛等其他仓 自行增加其他oss仓可以在 server/utils/upload/upload.go 中 NewOss函数配置 | ||||
|   use-multipoint: false | ||||
| 
 | ||||
| # captcha configuration | ||||
| captcha: | ||||
|   key-long: 6 | ||||
|   img-width: 240 | ||||
|   img-height: 80 | ||||
| 
 | ||||
| # mysql connect configuration | ||||
| # 未初始化之前请勿手动修改数据库信息!!!如果一定要手动初始化请看(https://www.github.com/flipped-aurora/gin-vue-admin/server.com/docs/first) | ||||
| mysql: | ||||
|   path: '' | ||||
|   config: 'parseTime=true' | ||||
|   db-name: 'gva' | ||||
|   username: 'root' | ||||
|   password: 'root' | ||||
|   max-idle-conns: 10 | ||||
|   max-open-conns: 100 | ||||
|   log-mode: false | ||||
|   log-zap: false | ||||
| 
 | ||||
| # timer task db clear table | ||||
| Timer: | ||||
|   start: true | ||||
|   spec: "@daily"  # 定时任务详细配置参考 https://pkg.go.dev/github.com/robfig/cron/v3 | ||||
|   detail: [ | ||||
|     # tableName: 需要清理的表名 | ||||
|     # compareField: 需要比较时间的字段 | ||||
|     # interval: 时间间隔, 具体配置详看 time.ParseDuration() 中字符串表示 且不能为负数 | ||||
|     # 2160h = 24 * 30 * 3 -> 三个月 | ||||
|   { tableName: "sys_operation_records" , compareField: "created_at", interval: "2160h" }, | ||||
|   { tableName: "jwt_blacklists" , compareField: "created_at", interval: "168h" } | ||||
|     #{ tableName: "log2" , compareField: "created_at", interval: "2160h" } | ||||
|   ] | ||||
|  | @ -0,0 +1,17 @@ | |||
| package config | ||||
| 
 | ||||
| type Autocode struct { | ||||
| 	TransferRestart bool   `mapstructure:"transfer-restart" json:"transferRestart" 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:"serverApi" yaml:"server-api"` | ||||
| 	SInitialize     string `mapstructure:"server-initialize" json:"serverInitialize" yaml:"server-initialize"` | ||||
| 	SModel          string `mapstructure:"server-model" json:"serverModel" yaml:"server-model"` | ||||
| 	SRequest        string `mapstructure:"server-request" json:"serverRequest"  yaml:"server-request"` | ||||
| 	SRouter         string `mapstructure:"server-router" json:"serverRouter" yaml:"server-router"` | ||||
| 	SService        string `mapstructure:"server-service" json:"serverService" yaml:"server-service"` | ||||
| 	Web             string `mapstructure:"web" json:"web" yaml:"web"` | ||||
| 	WApi            string `mapstructure:"web-api" json:"webApi" yaml:"web-api"` | ||||
| 	WForm           string `mapstructure:"web-form" json:"webForm" yaml:"web-form"` | ||||
| 	WTable          string `mapstructure:"web-table" json:"webTable" yaml:"web-table"` | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| package config | ||||
| 
 | ||||
| type Captcha struct { | ||||
| 	KeyLong   int `mapstructure:"key-long" json:"keyLong" yaml:"key-long"`       // 验证码长度
 | ||||
| 	ImgWidth  int `mapstructure:"img-width" json:"imgWidth" yaml:"img-width"`    // 验证码宽度
 | ||||
| 	ImgHeight int `mapstructure:"img-height" json:"imgHeight" yaml:"img-height"` // 验证码高度
 | ||||
| } | ||||
|  | @ -0,0 +1,5 @@ | |||
| package config | ||||
| 
 | ||||
| type Casbin struct { | ||||
| 	ModelPath string `mapstructure:"model-path" json:"modelPath" yaml:"model-path"` // 存放casbin模型的相对路径
 | ||||
| } | ||||
|  | @ -0,0 +1,22 @@ | |||
| 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"` | ||||
| 	Casbin  Casbin  `mapstructure:"casbin" json:"casbin" yaml:"casbin"` | ||||
| 	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"` | ||||
| 	// oss
 | ||||
| 	Local      Local      `mapstructure:"local" json:"local" yaml:"local"` | ||||
| 	Qiniu      Qiniu      `mapstructure:"qiniu" json:"qiniu" yaml:"qiniu"` | ||||
| 	AliyunOSS  AliyunOSS  `mapstructure:"aliyun-oss" json:"aliyunOSS" yaml:"aliyun-oss"` | ||||
| 	TencentCOS TencentCOS `mapstructure:"tencent-cos" json:"tencentCOS" yaml:"tencent-cos"` | ||||
| 	Excel      Excel      `mapstructure:"excel" json:"excel" yaml:"excel"` | ||||
| 	Timer      Timer      `mapstructure:"timer" json:"timer" yaml:"timer"` | ||||
| } | ||||
|  | @ -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:"isSSL" yaml:"is-ssl"`        // 是否SSL
 | ||||
| 	Secret   string `mapstructure:"secret" json:"secret" yaml:"secret"`       // 密钥
 | ||||
| 	Nickname string `mapstructure:"nickname" json:"nickname" yaml:"nickname"` // 昵称
 | ||||
| } | ||||
|  | @ -0,0 +1,5 @@ | |||
| package config | ||||
| 
 | ||||
| type Excel struct { | ||||
| 	Dir string `mapstructure:"dir" json:"dir" yaml:"dir"` | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| package config | ||||
| 
 | ||||
| type Mysql struct { | ||||
| 	Path         string `mapstructure:"path" json:"path" yaml:"path"`                             // 服务器地址:端口
 | ||||
| 	Config       string `mapstructure:"config" json:"config" yaml:"config"`                       // 高级配置
 | ||||
| 	Dbname       string `mapstructure:"db-name" json:"dbname" 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:"maxIdleConns" yaml:"max-idle-conns"` // 空闲中的最大连接数
 | ||||
| 	MaxOpenConns int    `mapstructure:"max-open-conns" json:"maxOpenConns" yaml:"max-open-conns"` // 打开到数据库的最大连接数
 | ||||
| 	LogMode      string `mapstructure:"log-mode" json:"logMode" yaml:"log-mode"`                  // 是否开启Gorm全局日志
 | ||||
| 	LogZap       bool   `mapstructure:"log-zap" json:"logZap" yaml:"log-zap"`                     // 是否通过zap写入日志文件
 | ||||
| } | ||||
| 
 | ||||
| func (m *Mysql) Dsn() string { | ||||
| 	return m.Username + ":" + m.Password + "@tcp(" + m.Path + ")/" + m.Dbname + "?" + m.Config | ||||
| } | ||||
|  | @ -0,0 +1,7 @@ | |||
| package config | ||||
| 
 | ||||
| type JWT struct { | ||||
| 	SigningKey  string `mapstructure:"signing-key" json:"signingKey" yaml:"signing-key"`    // jwt签名
 | ||||
| 	ExpiresTime int64  `mapstructure:"expires-time" json:"expiresTime" yaml:"expires-time"` // 过期时间
 | ||||
| 	BufferTime  int64  `mapstructure:"buffer-time" json:"bufferTime" yaml:"buffer-time"`    // 缓冲时间
 | ||||
| } | ||||
|  | @ -0,0 +1,32 @@ | |||
| package config | ||||
| 
 | ||||
| type Local struct { | ||||
| 	Path string `mapstructure:"path" json:"path" yaml:"path"` // 本地文件路径
 | ||||
| } | ||||
| 
 | ||||
| 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:"imgPath" yaml:"img-path"`                     // CDN加速域名
 | ||||
| 	UseHTTPS      bool   `mapstructure:"use-https" json:"useHttps" yaml:"use-https"`                  // 是否使用https
 | ||||
| 	AccessKey     string `mapstructure:"access-key" json:"accessKey" yaml:"access-key"`               // 秘钥AK
 | ||||
| 	SecretKey     string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"`               // 秘钥SK
 | ||||
| 	UseCdnDomains bool   `mapstructure:"use-cdn-domains" json:"useCdnDomains" yaml:"use-cdn-domains"` // 上传是否使用CDN上传加速
 | ||||
| } | ||||
| 
 | ||||
| type AliyunOSS struct { | ||||
| 	Endpoint        string `mapstructure:"endpoint" json:"endpoint" yaml:"endpoint"` | ||||
| 	AccessKeyId     string `mapstructure:"access-key-id" json:"accessKeyId" yaml:"access-key-id"` | ||||
| 	AccessKeySecret string `mapstructure:"access-key-secret" json:"accessKeySecret" yaml:"access-key-secret"` | ||||
| 	BucketName      string `mapstructure:"bucket-name" json:"bucketName" yaml:"bucket-name"` | ||||
| 	BucketUrl       string `mapstructure:"bucket-url" json:"bucketUrl" yaml:"bucket-url"` | ||||
| 	BasePath        string `mapstructure:"base-path" json:"basePath" yaml:"base-path"` | ||||
| } | ||||
| 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:"secretID" yaml:"secret-id"` | ||||
| 	SecretKey  string `mapstructure:"secret-key" json:"secretKey" yaml:"secret-key"` | ||||
| 	BaseURL    string `mapstructure:"base-url" json:"baseURL" yaml:"base-url"` | ||||
| 	PathPrefix string `mapstructure:"path-prefix" json:"pathPrefix" yaml:"path-prefix"` | ||||
| } | ||||
|  | @ -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"` // 密码
 | ||||
| } | ||||
|  | @ -0,0 +1,9 @@ | |||
| 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:"dbType" yaml:"db-type"`                      // 数据库类型:mysql(默认)|sqlite|sqlserver|postgresql
 | ||||
| 	OssType       string `mapstructure:"oss-type" json:"ossType" yaml:"oss-type"`                   // Oss类型
 | ||||
| 	UseMultipoint bool   `mapstructure:"use-multipoint" json:"useMultipoint" yaml:"use-multipoint"` // 多点登录拦截
 | ||||
| } | ||||
|  | @ -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"`             // 时间间隔
 | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| package config | ||||
| 
 | ||||
| type Zap struct { | ||||
| 	Level         string `mapstructure:"level" json:"level" yaml:"level"`                           // 级别
 | ||||
| 	Format        string `mapstructure:"format" json:"format" yaml:"format"`                        // 输出
 | ||||
| 	Prefix        string `mapstructure:"prefix" json:"prefix" yaml:"prefix"`                        // 日志前缀
 | ||||
| 	Director      string `mapstructure:"director" json:"director"  yaml:"director"`                 // 日志文件夹
 | ||||
| 	LinkName      string `mapstructure:"link-name" json:"linkName" yaml:"link-name"`                // 软链接名称
 | ||||
| 	ShowLine      bool   `mapstructure:"show-line" json:"showLine" yaml:"showLine"`                 // 显示行
 | ||||
| 	EncodeLevel   string `mapstructure:"encode-level" json:"encodeLevel" yaml:"encode-level"`       // 编码级
 | ||||
| 	StacktraceKey string `mapstructure:"stacktrace-key" json:"stacktraceKey" yaml:"stacktrace-key"` // 栈名
 | ||||
| 	LogInConsole  bool   `mapstructure:"log-in-console" json:"logInConsole" yaml:"log-in-console"`  // 输出控制台
 | ||||
| } | ||||
|  | @ -0,0 +1,47 @@ | |||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/fvbock/endless" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/initialize" | ||||
| 	"go.uber.org/zap" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type server interface { | ||||
| 	ListenAndServe() error | ||||
| } | ||||
| 
 | ||||
| func RunServer() { | ||||
| 
 | ||||
| 	// 禁用控制台颜色
 | ||||
| 	gin.DisableConsoleColor() | ||||
| 
 | ||||
| 	router := initialize.Routers() | ||||
| 
 | ||||
| 	router.Static("/form-generator", "./resource/page") | ||||
| 
 | ||||
| 	address := fmt.Sprintf(":%d", global.GLOBAL_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.GLOBAL_LOG.Info("server run success on ", zap.String("address", address)) | ||||
| 
 | ||||
| 	fmt.Printf(` | ||||
| 	当前版本:V2.4.5 | ||||
| 	默认自动化文档地址:http://127.0.0.1%s/swagger/index.html
 | ||||
| 	默认前端文件运行地址:http://127.0.0.1:8080
 | ||||
| `, address) | ||||
| 	global.GLOBAL_LOG.Error(s.ListenAndServe().Error()) | ||||
| } | ||||
| 
 | ||||
| func initServer(address string, router *gin.Engine) server { | ||||
| 	s := endless.NewServer(address, router) | ||||
| 	s.ReadHeaderTimeout = 10 * time.Millisecond | ||||
| 	s.WriteTimeout = 10 * time.Second | ||||
| 	s.MaxHeaderBytes = 1 << 20 | ||||
| 	return s | ||||
| } | ||||
|  | @ -0,0 +1,66 @@ | |||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"flag" | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/songzhibin97/gkit/cache/local_cache" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	_ "github.com/ymm135/goweb-gin-demo/packfile" | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils" | ||||
| 
 | ||||
| 	"github.com/fsnotify/fsnotify" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
| 
 | ||||
| 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(utils.ConfigEnv); configEnv == "" { | ||||
| 				config = utils.ConfigFile | ||||
| 				fmt.Printf("您正在使用config的默认值,config的路径为%v\n", utils.ConfigFile) | ||||
| 			} else { | ||||
| 				config = configEnv | ||||
| 				fmt.Printf("您正在使用GLOBAL_CONFIG环境变量,config的路径为%v\n", config) | ||||
| 			} | ||||
| 		} else { | ||||
| 			fmt.Printf("您正在使用命令行的-c参数传递的值,config的路径为%v\n", config) | ||||
| 		} | ||||
| 	} else { | ||||
| 		config = path[0] | ||||
| 		fmt.Printf("您正在使用func Viper()传递的值,config的路径为%v\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.GLOBAL_CONFIG); err != nil { | ||||
| 			fmt.Println(err) | ||||
| 		} | ||||
| 	}) | ||||
| 	if err := v.Unmarshal(&global.GLOBAL_CONFIG); err != nil { | ||||
| 		fmt.Println(err) | ||||
| 	} | ||||
| 	// root 适配性
 | ||||
| 	// 根据root位置去找到对应迁移位置,保证root路径有效
 | ||||
| 	global.GLOBAL_CONFIG.AutoCode.Root, _ = filepath.Abs("..") | ||||
| 	global.BlackCache = local_cache.NewCache( | ||||
| 		local_cache.SetDefaultExpire(time.Second * time.Duration(global.GLOBAL_CONFIG.JWT.ExpiresTime)), | ||||
| 	) | ||||
| 	return v | ||||
| } | ||||
|  | @ -0,0 +1,103 @@ | |||
| package core | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils" | ||||
| 	"go.uber.org/zap" | ||||
| 	"go.uber.org/zap/zapcore" | ||||
| ) | ||||
| 
 | ||||
| var level zapcore.Level | ||||
| 
 | ||||
| func Zap() (logger *zap.Logger) { | ||||
| 	if ok, _ := utils.PathExists(global.GLOBAL_CONFIG.Zap.Director); !ok { // 判断是否有Director文件夹
 | ||||
| 		fmt.Printf("create %v directory\n", global.GLOBAL_CONFIG.Zap.Director) | ||||
| 		_ = os.Mkdir(global.GLOBAL_CONFIG.Zap.Director, os.ModePerm) | ||||
| 	} | ||||
| 
 | ||||
| 	switch global.GLOBAL_CONFIG.Zap.Level { // 初始化配置文件的Level
 | ||||
| 	case "debug": | ||||
| 		level = zap.DebugLevel | ||||
| 	case "info": | ||||
| 		level = zap.InfoLevel | ||||
| 	case "warn": | ||||
| 		level = zap.WarnLevel | ||||
| 	case "error": | ||||
| 		level = zap.ErrorLevel | ||||
| 	case "dpanic": | ||||
| 		level = zap.DPanicLevel | ||||
| 	case "panic": | ||||
| 		level = zap.PanicLevel | ||||
| 	case "fatal": | ||||
| 		level = zap.FatalLevel | ||||
| 	default: | ||||
| 		level = zap.InfoLevel | ||||
| 	} | ||||
| 
 | ||||
| 	if level == zap.DebugLevel || level == zap.ErrorLevel { | ||||
| 		logger = zap.New(getEncoderCore(), zap.AddStacktrace(level)) | ||||
| 	} else { | ||||
| 		logger = zap.New(getEncoderCore()) | ||||
| 	} | ||||
| 	if global.GLOBAL_CONFIG.Zap.ShowLine { | ||||
| 		logger = logger.WithOptions(zap.AddCaller()) | ||||
| 	} | ||||
| 	return logger | ||||
| } | ||||
| 
 | ||||
| // getEncoderConfig 获取zapcore.EncoderConfig
 | ||||
| func getEncoderConfig() (config zapcore.EncoderConfig) { | ||||
| 	config = zapcore.EncoderConfig{ | ||||
| 		MessageKey:     "message", | ||||
| 		LevelKey:       "level", | ||||
| 		TimeKey:        "time", | ||||
| 		NameKey:        "logger", | ||||
| 		CallerKey:      "caller", | ||||
| 		StacktraceKey:  global.GLOBAL_CONFIG.Zap.StacktraceKey, | ||||
| 		LineEnding:     zapcore.DefaultLineEnding, | ||||
| 		EncodeLevel:    zapcore.LowercaseLevelEncoder, | ||||
| 		EncodeTime:     CustomTimeEncoder, | ||||
| 		EncodeDuration: zapcore.SecondsDurationEncoder, | ||||
| 		EncodeCaller:   zapcore.FullCallerEncoder, | ||||
| 	} | ||||
| 	switch { | ||||
| 	case global.GLOBAL_CONFIG.Zap.EncodeLevel == "LowercaseLevelEncoder": // 小写编码器(默认)
 | ||||
| 		config.EncodeLevel = zapcore.LowercaseLevelEncoder | ||||
| 	case global.GLOBAL_CONFIG.Zap.EncodeLevel == "LowercaseColorLevelEncoder": // 小写编码器带颜色
 | ||||
| 		config.EncodeLevel = zapcore.LowercaseColorLevelEncoder | ||||
| 	case global.GLOBAL_CONFIG.Zap.EncodeLevel == "CapitalLevelEncoder": // 大写编码器
 | ||||
| 		config.EncodeLevel = zapcore.CapitalLevelEncoder | ||||
| 	case global.GLOBAL_CONFIG.Zap.EncodeLevel == "CapitalColorLevelEncoder": // 大写编码器带颜色
 | ||||
| 		config.EncodeLevel = zapcore.CapitalColorLevelEncoder | ||||
| 	default: | ||||
| 		config.EncodeLevel = zapcore.LowercaseLevelEncoder | ||||
| 	} | ||||
| 	return config | ||||
| } | ||||
| 
 | ||||
| // getEncoder 获取zapcore.Encoder
 | ||||
| func getEncoder() zapcore.Encoder { | ||||
| 	if global.GLOBAL_CONFIG.Zap.Format == "json" { | ||||
| 		return zapcore.NewJSONEncoder(getEncoderConfig()) | ||||
| 	} | ||||
| 	return zapcore.NewConsoleEncoder(getEncoderConfig()) | ||||
| } | ||||
| 
 | ||||
| // getEncoderCore 获取Encoder的zapcore.Core
 | ||||
| func getEncoderCore() (core zapcore.Core) { | ||||
| 	writer, err := utils.GetWriteSyncer() // 使用file-rotatelogs进行日志分割
 | ||||
| 	if err != nil { | ||||
| 		fmt.Printf("Get Write Syncer Failed err:%v", err.Error()) | ||||
| 		return | ||||
| 	} | ||||
| 	return zapcore.NewCore(getEncoder(), writer, level) | ||||
| } | ||||
| 
 | ||||
| // 自定义日志输出时间格式
 | ||||
| func CustomTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { | ||||
| 	enc.AppendString(t.Format(global.GLOBAL_CONFIG.Zap.Prefix + "2006/01/02 - 15:04:05.000")) | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,680 @@ | |||
| definitions: | ||||
|   request.AddMenuAuthorityInfo: | ||||
|     properties: | ||||
|       authorityId: | ||||
|         description: 角色ID | ||||
|         type: string | ||||
|       menus: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysBaseMenu' | ||||
|         type: array | ||||
|     type: object | ||||
|   request.ChangePasswordStruct: | ||||
|     properties: | ||||
|       newPassword: | ||||
|         description: 新密码 | ||||
|         type: string | ||||
|       password: | ||||
|         description: 密码 | ||||
|         type: string | ||||
|       username: | ||||
|         description: 用户名 | ||||
|         type: string | ||||
|     type: object | ||||
|   request.Empty: | ||||
|     type: object | ||||
|   request.GetAuthorityId: | ||||
|     properties: | ||||
|       authorityId: | ||||
|         description: 角色ID | ||||
|         type: string | ||||
|     type: object | ||||
|   request.GetById: | ||||
|     properties: | ||||
|       id: | ||||
|         description: 主键ID | ||||
|         type: number | ||||
|     type: object | ||||
|   request.Login: | ||||
|     properties: | ||||
|       captcha: | ||||
|         description: 验证码 | ||||
|         type: string | ||||
|       captchaId: | ||||
|         description: 验证码ID | ||||
|         type: string | ||||
|       password: | ||||
|         description: 密码 | ||||
|         type: string | ||||
|       username: | ||||
|         description: 用户名 | ||||
|         type: string | ||||
|     type: object | ||||
|   request.PageInfo: | ||||
|     properties: | ||||
|       page: | ||||
|         description: 页码 | ||||
|         type: integer | ||||
|       pageSize: | ||||
|         description: 每页大小 | ||||
|         type: integer | ||||
|     type: object | ||||
|   request.Register: | ||||
|     properties: | ||||
|       authorityId: | ||||
|         type: string | ||||
|       authorityIds: | ||||
|         items: | ||||
|           type: string | ||||
|         type: array | ||||
|       headerImg: | ||||
|         type: string | ||||
|       nickName: | ||||
|         type: string | ||||
|       passWord: | ||||
|         type: string | ||||
|       userName: | ||||
|         type: string | ||||
|     type: object | ||||
|   request.SetUserAuth: | ||||
|     properties: | ||||
|       authorityId: | ||||
|         description: 角色ID | ||||
|         type: string | ||||
|     type: object | ||||
|   request.SetUserAuthorities: | ||||
|     properties: | ||||
|       authorityIds: | ||||
|         description: 角色ID | ||||
|         items: | ||||
|           type: string | ||||
|         type: array | ||||
|       id: | ||||
|         type: integer | ||||
|     type: object | ||||
|   web.SysAuthority: | ||||
|     properties: | ||||
|       authorityId: | ||||
|         description: 角色ID | ||||
|         type: string | ||||
|       authorityName: | ||||
|         description: 角色名 | ||||
|         type: string | ||||
|       children: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysAuthority' | ||||
|         type: array | ||||
|       createdAt: | ||||
|         description: 创建时间 | ||||
|         type: string | ||||
|       dataAuthorityId: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysAuthority' | ||||
|         type: array | ||||
|       defaultRouter: | ||||
|         description: 默认菜单(默认dashboard) | ||||
|         type: string | ||||
|       deletedAt: | ||||
|         type: string | ||||
|       menus: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysBaseMenu' | ||||
|         type: array | ||||
|       parentId: | ||||
|         description: 父角色ID | ||||
|         type: string | ||||
|       updatedAt: | ||||
|         description: 更新时间 | ||||
|         type: string | ||||
|     type: object | ||||
|   web.SysBaseMenu: | ||||
|     properties: | ||||
|       authoritys: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysAuthority' | ||||
|         type: array | ||||
|       children: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysBaseMenu' | ||||
|         type: array | ||||
|       closeTab: | ||||
|         description: 自动关闭tab | ||||
|         type: boolean | ||||
|       component: | ||||
|         description: 对应前端文件路径 | ||||
|         type: string | ||||
|       createdAt: | ||||
|         description: 创建时间 | ||||
|         type: string | ||||
|       defaultMenu: | ||||
|         description: 是否是基础路由(开发中) | ||||
|         type: boolean | ||||
|       hidden: | ||||
|         description: 是否在列表隐藏 | ||||
|         type: boolean | ||||
|       icon: | ||||
|         description: 菜单图标 | ||||
|         type: string | ||||
|       id: | ||||
|         description: 主键ID | ||||
|         type: integer | ||||
|       keepAlive: | ||||
|         description: 是否缓存 | ||||
|         type: boolean | ||||
|       name: | ||||
|         description: 路由name | ||||
|         type: string | ||||
|       parameters: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysBaseMenuParameter' | ||||
|         type: array | ||||
|       parentId: | ||||
|         description: 父菜单ID | ||||
|         type: string | ||||
|       path: | ||||
|         description: 路由path | ||||
|         type: string | ||||
|       sort: | ||||
|         description: 排序标记 | ||||
|         type: integer | ||||
|       title: | ||||
|         description: 菜单名 | ||||
|         type: string | ||||
|       updatedAt: | ||||
|         description: 更新时间 | ||||
|         type: string | ||||
|     type: object | ||||
|   web.SysBaseMenuParameter: | ||||
|     properties: | ||||
|       createdAt: | ||||
|         description: 创建时间 | ||||
|         type: string | ||||
|       id: | ||||
|         description: 主键ID | ||||
|         type: integer | ||||
|       key: | ||||
|         description: 地址栏携带参数的key | ||||
|         type: string | ||||
|       sysBaseMenuID: | ||||
|         type: integer | ||||
|       type: | ||||
|         description: 地址栏携带参数为params还是query | ||||
|         type: string | ||||
|       updatedAt: | ||||
|         description: 更新时间 | ||||
|         type: string | ||||
|       value: | ||||
|         description: 地址栏携带参数的值 | ||||
|         type: string | ||||
|     type: object | ||||
|   web.SysUser: | ||||
|     properties: | ||||
|       activeColor: | ||||
|         description: 活跃颜色 | ||||
|         type: string | ||||
|       authorities: | ||||
|         items: | ||||
|           $ref: '#/definitions/web.SysAuthority' | ||||
|         type: array | ||||
|       authority: | ||||
|         $ref: '#/definitions/web.SysAuthority' | ||||
|       authorityId: | ||||
|         description: 用户角色ID | ||||
|         type: string | ||||
|       baseColor: | ||||
|         description: 基础颜色 | ||||
|         type: string | ||||
|       createdAt: | ||||
|         description: 创建时间 | ||||
|         type: string | ||||
|       headerImg: | ||||
|         description: 用户头像 | ||||
|         type: string | ||||
|       id: | ||||
|         description: 主键ID | ||||
|         type: integer | ||||
|       nickName: | ||||
|         description: 用户昵称 | ||||
|         type: string | ||||
|       sideMode: | ||||
|         description: 用户侧边主题 | ||||
|         type: string | ||||
|       updatedAt: | ||||
|         description: 更新时间 | ||||
|         type: string | ||||
|       userName: | ||||
|         description: 用户登录名 | ||||
|         type: string | ||||
|       uuid: | ||||
|         description: 用户UUID | ||||
|         type: string | ||||
|     type: object | ||||
| info: | ||||
|   contact: {} | ||||
| paths: | ||||
|   /base/captcha: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"验证码获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 生成验证码 | ||||
|       tags: | ||||
|       - Base | ||||
|   /base/login: | ||||
|     post: | ||||
|       parameters: | ||||
|       - description: 用户名, 密码, 验证码 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.Login' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"登陆成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       summary: 用户登录 | ||||
|       tags: | ||||
|       - Base | ||||
|   /jwt/jsonInBlacklist: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"拉黑成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: jwt加入黑名单 | ||||
|       tags: | ||||
|       - Jwt | ||||
|   /menu/addBaseMenu: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/web.SysBaseMenu' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"添加成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 新增菜单 | ||||
|       tags: | ||||
|       - Menu | ||||
|   /menu/addMenuAuthority: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 角色ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.AddMenuAuthorityInfo' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"添加成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 增加menu和角色关联关系 | ||||
|       tags: | ||||
|       - AuthorityMenu | ||||
|   /menu/deleteBaseMenu: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 菜单id | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.GetById' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"删除成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 删除菜单 | ||||
|       tags: | ||||
|       - Menu | ||||
|   /menu/getBaseMenuById: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 菜单id | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.GetById' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 根据id获取菜单 | ||||
|       tags: | ||||
|       - Menu | ||||
|   /menu/getBaseMenuTree: | ||||
|     post: | ||||
|       parameters: | ||||
|       - description: 空 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.Empty' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 获取用户动态路由 | ||||
|       tags: | ||||
|       - AuthorityMenu | ||||
|   /menu/getMenu: | ||||
|     post: | ||||
|       parameters: | ||||
|       - description: 空 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.Empty' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 获取用户动态路由 | ||||
|       tags: | ||||
|       - AuthorityMenu | ||||
|   /menu/getMenuAuthority: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 角色ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.GetAuthorityId' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 获取指定角色menu | ||||
|       tags: | ||||
|       - AuthorityMenu | ||||
|   /menu/getMenuList: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 页码, 每页大小 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.PageInfo' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 分页获取基础menu列表 | ||||
|       tags: | ||||
|       - Menu | ||||
|   /menu/updateBaseMenu: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 路由path, 父菜单ID, 路由name, 对应前端文件路径, 排序标记 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/web.SysBaseMenu' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"更新成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 更新菜单 | ||||
|       tags: | ||||
|       - Menu | ||||
|   /user/changePassword: | ||||
|     post: | ||||
|       parameters: | ||||
|       - description: 用户名, 原密码, 新密码 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.ChangePasswordStruct' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"修改成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 用户修改密码 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/deleteUser: | ||||
|     delete: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 用户ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.GetById' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"删除成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 删除用户 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/getUserInfo: | ||||
|     get: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 获取用户信息 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/getUserList: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 页码, 每页大小 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.PageInfo' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"获取成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 分页获取用户列表 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/register: | ||||
|     post: | ||||
|       parameters: | ||||
|       - description: 用户名, 昵称, 密码, 角色ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.Register' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"注册成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       summary: 用户注册账号 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/setUserAuthorities: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 用户UUID, 角色ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.SetUserAuthorities' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"修改成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 设置用户权限 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/setUserAuthority: | ||||
|     post: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: 用户UUID, 角色ID | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/request.SetUserAuth' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"修改成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 更改用户权限 | ||||
|       tags: | ||||
|       - SysUser | ||||
|   /user/setUserInfo: | ||||
|     put: | ||||
|       consumes: | ||||
|       - application/json | ||||
|       parameters: | ||||
|       - description: ID, 用户名, 昵称, 头像链接 | ||||
|         in: body | ||||
|         name: data | ||||
|         required: true | ||||
|         schema: | ||||
|           $ref: '#/definitions/web.SysUser' | ||||
|       produces: | ||||
|       - application/json | ||||
|       responses: | ||||
|         "200": | ||||
|           description: '{"success":true,"data":{},"msg":"设置成功"}' | ||||
|           schema: | ||||
|             type: string | ||||
|       security: | ||||
|       - ApiKeyAuth: [] | ||||
|       summary: 设置用户信息 | ||||
|       tags: | ||||
|       - SysUser | ||||
| swagger: "2.0" | ||||
|  | @ -0,0 +1,29 @@ | |||
| package global | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/songzhibin97/gkit/cache/local_cache" | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils/timer" | ||||
| 
 | ||||
| 	"golang.org/x/sync/singleflight" | ||||
| 
 | ||||
| 	"go.uber.org/zap" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/config" | ||||
| 
 | ||||
| 	"github.com/go-redis/redis/v8" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| var ( | ||||
| 	GLOBAL_DB     *gorm.DB | ||||
| 	GLOBAL_REDIS  *redis.Client | ||||
| 	GLOBAL_CONFIG config.Server | ||||
| 	GLOBAL_VP     *viper.Viper | ||||
| 	//GLOBAL_LOG    *oplogging.Logger
 | ||||
| 	GLOBAL_LOG                 *zap.Logger | ||||
| 	GLOBAL_Timer               timer.Timer = timer.NewTimerTask() | ||||
| 	GLOBAL_Concurrency_Control             = &singleflight.Group{} | ||||
| 
 | ||||
| 	BlackCache local_cache.Cache | ||||
| ) | ||||
|  | @ -0,0 +1,13 @@ | |||
| package global | ||||
| 
 | ||||
| import ( | ||||
| 	"gorm.io/gorm" | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type GLOBAL_MODEL struct { | ||||
| 	ID        uint           `gorm:"primarykey"` // 主键ID
 | ||||
| 	CreatedAt time.Time      // 创建时间
 | ||||
| 	UpdatedAt time.Time      // 更新时间
 | ||||
| 	DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` // 删除时间
 | ||||
| } | ||||
|  | @ -0,0 +1,33 @@ | |||
| module github.com/ymm135/goweb-gin-demo | ||||
| 
 | ||||
| go 1.16 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/casbin/casbin/v2 v2.11.0 | ||||
| 	github.com/casbin/gorm-adapter/v3 v3.0.2 | ||||
| 	github.com/dgrijalva/jwt-go v3.2.0+incompatible | ||||
| 	github.com/fsnotify/fsnotify v1.4.9 | ||||
| 	github.com/fvbock/endless v0.0.0-20170109170031-447134032cb6 | ||||
| 	github.com/gin-gonic/gin v1.7.4 | ||||
| 	github.com/go-openapi/spec v0.20.4 // indirect | ||||
| 	github.com/go-redis/redis/v8 v8.11.0 | ||||
| 	github.com/go-sql-driver/mysql v1.6.0 | ||||
| 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible | ||||
| 	github.com/lestrrat-go/strftime v1.0.5 // indirect | ||||
| 	github.com/mailru/easyjson v0.7.7 // indirect | ||||
| 	github.com/mojocn/base64Captcha v1.3.1 | ||||
| 	github.com/robfig/cron/v3 v3.0.1 | ||||
| 	github.com/satori/go.uuid v1.2.0 | ||||
| 	github.com/songzhibin97/gkit v1.1.1 | ||||
| 	github.com/spf13/viper v1.7.0 | ||||
| 	github.com/stretchr/testify v1.7.0 | ||||
| 	github.com/swaggo/gin-swagger v1.3.2 | ||||
| 	github.com/swaggo/swag v1.7.4 | ||||
| 	go.uber.org/zap v1.16.0 | ||||
| 	golang.org/x/net v0.0.0-20211020060615-d418f374d309 // indirect | ||||
| 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c | ||||
| 	golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect | ||||
| 	golang.org/x/tools v0.1.7 // indirect | ||||
| 	gorm.io/driver/mysql v1.1.2 | ||||
| 	gorm.io/gorm v1.21.15 | ||||
| ) | ||||
|  | @ -0,0 +1,698 @@ | |||
| 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 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/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/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 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= | ||||
| github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= | ||||
| github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= | ||||
| github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= | ||||
| 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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= | ||||
| 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/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/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 h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= | ||||
| github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= | ||||
| github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= | ||||
| github.com/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/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.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.7.0/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= | ||||
| github.com/gin-gonic/gin v1.7.4 h1:QmUZXrvJ9qZ3GfWvQ+2wnW/1ePrTEJqPKMYEU3lD/DM= | ||||
| github.com/gin-gonic/gin v1.7.4/go.mod h1:jD2toBW3GZUr5UMcdrwQA10I7RuaFOl/SGeDjXkfUtY= | ||||
| github.com/go-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-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-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= | ||||
| 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.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= | ||||
| github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= | ||||
| github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= | ||||
| github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= | ||||
| github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg= | ||||
| github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= | ||||
| github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= | ||||
| 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.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= | ||||
| github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= | ||||
| github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= | ||||
| 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.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/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | ||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= | ||||
| github.com/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-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.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 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/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.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/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.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/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 h1:lwofaXKPbIx6qEaK8mNm7uZuOwxHw+PnAFGDsDFpkRI= | ||||
| github.com/jackc/pgconn v1.6.1/go.mod h1:g8mKMqmSUO6AzAvha7vy07g1rbGOlc7iF0nU0ei83hc= | ||||
| 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 h1:JVX6jT/XfzNqIjye4717ITLaNwV9mWbJx0dLCpcRzdA= | ||||
| github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= | ||||
| 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 h1:q1Hsy66zh4vuNsajBUF2PNqfAMMfxU5mk594lPE9vjY= | ||||
| github.com/jackc/pgproto3/v2 v2.0.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= | ||||
| github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8 h1:Q3tB+ExeflWUW7AFcAhXqk40s9mnNYLk1nOkKNZ5GnU= | ||||
| github.com/jackc/pgservicefile v0.0.0-20200307190119-3430c5407db8/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 h1:pHQfb4jh9iKqHyxPthq1fr+0HwSNIl3btYPbw2m2lbM= | ||||
| github.com/jackc/pgtype v1.4.0/go.mod h1:JCULISAZBFGrHaOXIIFiyfzW5VY0GRitRr8NeJsrdig= | ||||
| 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 h1:aqUSOcStk6fik+lSE+tqfFhvt/EwT8q/oMtJbP9CjXI= | ||||
| github.com/jackc/pgx/v4 v4.7.1/go.mod h1:nu42q3aPjuC1M0Nak4bnoprKlXPINqopEKqbq5AZSC4= | ||||
| 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/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 h1:eVKgfIdy9b6zbWBMgFpfDPoAMifwSZagU9HmEU6zgiI= | ||||
| github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= | ||||
| 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/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 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= | ||||
| github.com/json-iterator/go v1.1.9/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/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/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.5 h1:A7H3tT8DhTz8u65w+JRpiBxM4dINQhUXAZnhBa2xeOE= | ||||
| github.com/lestrrat-go/strftime v1.0.5/go.mod h1:E1nN3pCbtMSu1yjSVeyuRFVm/U0xoR76fd03sz+Qz4g= | ||||
| 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 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= | ||||
| github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||
| 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 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= | ||||
| github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd 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/mojocn/base64Captcha v1.3.1 h1:2Wbkt8Oc8qjmNJ5GyOfSo4tgVQPsbKMftqASnq8GlT0= | ||||
| github.com/mojocn/base64Captcha v1.3.1/go.mod h1:wAQCKEc5bDujxKRmbT6/vTnTt5CjStQ8bRfPWUuz/iY= | ||||
| 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 h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | ||||
| 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 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= | ||||
| github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= | ||||
| github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= | ||||
| github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= | ||||
| github.com/prometheus/client_golang v0.9.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/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/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= | ||||
| 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 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= | ||||
| github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= | ||||
| github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= | ||||
| github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= | ||||
| github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= | ||||
| github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= | ||||
| github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= | ||||
| github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= | ||||
| 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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= | ||||
| github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= | ||||
| github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E= | ||||
| github.com/swaggo/gin-swagger v1.3.2 h1:v4x39WgGCpJh9smvidElXep42uFZEiSU7hHfmCAB5+I= | ||||
| github.com/swaggo/gin-swagger v1.3.2/go.mod h1:8GN8KIlwgjawtEvE+B8sx3q9SPJuX/ZPxyuoFVrl6gM= | ||||
| github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y= | ||||
| github.com/swaggo/swag v1.7.4 h1:up+ixy8yOqJKiFcuhMgkuYuF4xnevuhnFAXXF8OSfNg= | ||||
| github.com/swaggo/swag v1.7.4/go.mod h1:zD8h6h4SPv7t3l+4BKdRquqW1ASWjKZgT6Qv9z3kNqI= | ||||
| github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= | ||||
| github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= | ||||
| github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= | ||||
| github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= | ||||
| github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= | ||||
| github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= | ||||
| github.com/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/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= | ||||
| 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.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.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.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 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/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 h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4= | ||||
| golang.org/x/image v0.0.0-20190802002840-cff245a6509b/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.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= | ||||
| golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-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-20190827160401-ba9fcec4b297/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-20210119194325-5f4716e94777/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-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI= | ||||
| golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| 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 h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ= | ||||
| golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-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-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-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-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-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= | ||||
| golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.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.5/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-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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= | ||||
| golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ= | ||||
| golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= | ||||
| 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/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 h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| 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 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= | ||||
| gopkg.in/ini.v1 v1.51.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 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gorm.io/driver/mysql v0.3.0/go.mod h1:A7H1JD9dKdcjeUTpTuWKEC+E1a74qzW7/zaXqKaTbfM= | ||||
| gorm.io/driver/mysql v1.1.2 h1:OofcyE2lga734MxwcCW9uB4mWNXMr50uaGRVwQL2B0M= | ||||
| gorm.io/driver/mysql v1.1.2/go.mod h1:4P/X9vSc3WTrhTLZ259cpFd6xKNYiSSdSZngkSBGIMM= | ||||
| gorm.io/driver/postgres v0.2.6 h1:hoE6SzA5wKOo6AYxz2V7ooxnzD6S6ToLAHHDDawt+b0= | ||||
| gorm.io/driver/postgres v0.2.6/go.mod h1:AsPyuhKFOplSmQwOPsycVKbe0dRxF8v18KZ7p9i8dIs= | ||||
| 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.21.12/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= | ||||
| gorm.io/gorm v1.21.15 h1:gAyaDoPw0lCyrSFWhBlahbUA1U4P5RViC1uIqoB+1Rk= | ||||
| gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0= | ||||
| 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= | ||||
| rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | ||||
										
											Binary file not shown.
										
									
								
							|  | @ -0,0 +1,9 @@ | |||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="Go" enabled="true" /> | ||||
|   <component name="NewModuleRootManager" inherit-compiler-output="true"> | ||||
|     <exclude-output /> | ||||
|     <content url="file://$MODULE_DIR$" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
|  | @ -0,0 +1,79 @@ | |||
| package initialize | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/initialize/internal" | ||||
| 
 | ||||
| 	"gorm.io/driver/mysql" | ||||
| 	"gorm.io/gorm" | ||||
| 	"gorm.io/gorm/logger" | ||||
| ) | ||||
| 
 | ||||
| //@author: SliverHorn
 | ||||
| //@function: Gorm
 | ||||
| //@description: 初始化数据库并产生数据库全局变量
 | ||||
| //@return: *gorm.DB
 | ||||
| 
 | ||||
| func Gorm() *gorm.DB { | ||||
| 	switch global.GLOBAL_CONFIG.System.DbType { | ||||
| 	case "mysql": | ||||
| 		return GormMysql() | ||||
| 	default: | ||||
| 		return GormMysql() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //@author: SliverHorn
 | ||||
| //@function: GormMysql
 | ||||
| //@description: 初始化Mysql数据库
 | ||||
| //@return: *gorm.DB
 | ||||
| 
 | ||||
| func GormMysql() *gorm.DB { | ||||
| 	m := global.GLOBAL_CONFIG.Mysql | ||||
| 	if m.Dbname == "" { | ||||
| 		return nil | ||||
| 	} | ||||
| 	dsn := m.Username + ":" + m.Password + "@tcp(" + m.Path + ")/" + m.Dbname + "?" + m.Config | ||||
| 	mysqlConfig := mysql.Config{ | ||||
| 		DSN:                       dsn,   // DSN data source name
 | ||||
| 		DefaultStringSize:         191,   // string 类型字段的默认长度
 | ||||
| 		DisableDatetimePrecision:  true,  // 禁用 datetime 精度,MySQL 5.6 之前的数据库不支持
 | ||||
| 		DontSupportRenameIndex:    true,  // 重命名索引时采用删除并新建的方式,MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
 | ||||
| 		DontSupportRenameColumn:   true,  // 用 `change` 重命名列,MySQL 8 之前的数据库和 MariaDB 不支持重命名列
 | ||||
| 		SkipInitializeWithVersion: false, // 根据版本自动配置
 | ||||
| 	} | ||||
| 	if db, err := gorm.Open(mysql.New(mysqlConfig), gormConfig()); err != nil { | ||||
| 		//global.GLOBAL_LOG.Error("MySQL启动异常", zap.Any("err", err))
 | ||||
| 		//os.Exit(0)
 | ||||
| 		//return nil
 | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		sqlDB, _ := db.DB() | ||||
| 		sqlDB.SetMaxIdleConns(m.MaxIdleConns) | ||||
| 		sqlDB.SetMaxOpenConns(m.MaxOpenConns) | ||||
| 		return db | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //@author: SliverHorn
 | ||||
| //@function: gormConfig
 | ||||
| //@description: 根据配置决定是否开启日志
 | ||||
| //@param: mod bool
 | ||||
| //@return: *gorm.Config
 | ||||
| 
 | ||||
| func gormConfig() *gorm.Config { | ||||
| 	config := &gorm.Config{DisableForeignKeyConstraintWhenMigrating: true} | ||||
| 	switch global.GLOBAL_CONFIG.Mysql.LogMode { | ||||
| 	case "silent", "Silent": | ||||
| 		config.Logger = internal.Default.LogMode(logger.Silent) | ||||
| 	case "error", "Error": | ||||
| 		config.Logger = internal.Default.LogMode(logger.Error) | ||||
| 	case "warn", "Warn": | ||||
| 		config.Logger = internal.Default.LogMode(logger.Warn) | ||||
| 	case "info", "Info": | ||||
| 		config.Logger = internal.Default.LogMode(logger.Info) | ||||
| 	default: | ||||
| 		config.Logger = internal.Default.LogMode(logger.Info) | ||||
| 	} | ||||
| 	return config | ||||
| } | ||||
|  | @ -0,0 +1,153 @@ | |||
| package internal | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"log" | ||||
| 	"os" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"gorm.io/gorm/logger" | ||||
| 	"gorm.io/gorm/utils" | ||||
| ) | ||||
| 
 | ||||
| type config struct { | ||||
| 	SlowThreshold time.Duration | ||||
| 	Colorful      bool | ||||
| 	LogLevel      logger.LogLevel | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	Discard = New(log.New(ioutil.Discard, "", log.LstdFlags), config{}) | ||||
| 	Default = New(log.New(os.Stdout, "\r\n", log.LstdFlags), config{ | ||||
| 		SlowThreshold: 200 * time.Millisecond, | ||||
| 		LogLevel:      logger.Warn, | ||||
| 		Colorful:      true, | ||||
| 	}) | ||||
| 	Recorder = traceRecorder{Interface: Default, BeginAt: time.Now()} | ||||
| ) | ||||
| 
 | ||||
| func New(writer logger.Writer, config config) logger.Interface { | ||||
| 	var ( | ||||
| 		infoStr      = "%s\n[info] " | ||||
| 		warnStr      = "%s\n[warn] " | ||||
| 		errStr       = "%s\n[error] " | ||||
| 		traceStr     = "%s\n[%.3fms] [rows:%v] %s\n" | ||||
| 		traceWarnStr = "%s %s\n[%.3fms] [rows:%v] %s\n" | ||||
| 		traceErrStr  = "%s %s\n[%.3fms] [rows:%v] %s\n" | ||||
| 	) | ||||
| 
 | ||||
| 	if config.Colorful { | ||||
| 		infoStr = logger.Green + "%s\n" + logger.Reset + logger.Green + "[info] " + logger.Reset | ||||
| 		warnStr = logger.BlueBold + "%s\n" + logger.Reset + logger.Magenta + "[warn] " + logger.Reset | ||||
| 		errStr = logger.Magenta + "%s\n" + logger.Reset + logger.Red + "[error] " + logger.Reset | ||||
| 		traceStr = logger.Green + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n" | ||||
| 		traceWarnStr = logger.Green + "%s " + logger.Yellow + "%s\n" + logger.Reset + logger.RedBold + "[%.3fms] " + logger.Yellow + "[rows:%v]" + logger.Magenta + " %s\n" + logger.Reset | ||||
| 		traceErrStr = logger.RedBold + "%s " + logger.MagentaBold + "%s\n" + logger.Reset + logger.Yellow + "[%.3fms] " + logger.BlueBold + "[rows:%v]" + logger.Reset + " %s\n" | ||||
| 	} | ||||
| 
 | ||||
| 	return &_logger{ | ||||
| 		Writer:       writer, | ||||
| 		config:       config, | ||||
| 		infoStr:      infoStr, | ||||
| 		warnStr:      warnStr, | ||||
| 		errStr:       errStr, | ||||
| 		traceStr:     traceStr, | ||||
| 		traceWarnStr: traceWarnStr, | ||||
| 		traceErrStr:  traceErrStr, | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type _logger struct { | ||||
| 	config | ||||
| 	logger.Writer | ||||
| 	infoStr, warnStr, errStr            string | ||||
| 	traceStr, traceErrStr, traceWarnStr string | ||||
| } | ||||
| 
 | ||||
| // LogMode log mode
 | ||||
| func (c *_logger) LogMode(level logger.LogLevel) logger.Interface { | ||||
| 	newLogger := *c | ||||
| 	newLogger.LogLevel = level | ||||
| 	return &newLogger | ||||
| } | ||||
| 
 | ||||
| // Info print info
 | ||||
| func (c *_logger) Info(ctx context.Context, message string, data ...interface{}) { | ||||
| 	if c.LogLevel >= logger.Info { | ||||
| 		c.Printf(c.infoStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Warn print warn messages
 | ||||
| func (c *_logger) Warn(ctx context.Context, message string, data ...interface{}) { | ||||
| 	if c.LogLevel >= logger.Warn { | ||||
| 		c.Printf(c.warnStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Error print error messages
 | ||||
| func (c *_logger) Error(ctx context.Context, message string, data ...interface{}) { | ||||
| 	if c.LogLevel >= logger.Error { | ||||
| 		c.Printf(c.errStr+message, append([]interface{}{utils.FileWithLineNum()}, data...)...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Trace print sql message
 | ||||
| func (c *_logger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { | ||||
| 	if c.LogLevel > 0 { | ||||
| 		elapsed := time.Since(begin) | ||||
| 		switch { | ||||
| 		case err != nil && c.LogLevel >= logger.Error: | ||||
| 			sql, rows := fc() | ||||
| 			if rows == -1 { | ||||
| 				c.Printf(c.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, "-", sql) | ||||
| 			} else { | ||||
| 				c.Printf(c.traceErrStr, utils.FileWithLineNum(), err, float64(elapsed.Nanoseconds())/1e6, rows, sql) | ||||
| 			} | ||||
| 		case elapsed > c.SlowThreshold && c.SlowThreshold != 0 && c.LogLevel >= logger.Warn: | ||||
| 			sql, rows := fc() | ||||
| 			slowLog := fmt.Sprintf("SLOW SQL >= %v", c.SlowThreshold) | ||||
| 			if rows == -1 { | ||||
| 				c.Printf(c.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, "-", sql) | ||||
| 			} else { | ||||
| 				c.Printf(c.traceWarnStr, utils.FileWithLineNum(), slowLog, float64(elapsed.Nanoseconds())/1e6, rows, sql) | ||||
| 			} | ||||
| 		case c.LogLevel >= logger.Info: | ||||
| 			sql, rows := fc() | ||||
| 			if rows == -1 { | ||||
| 				c.Printf(c.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, "-", sql) | ||||
| 			} else { | ||||
| 				c.Printf(c.traceStr, utils.FileWithLineNum(), float64(elapsed.Nanoseconds())/1e6, rows, sql) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func (c *_logger) Printf(message string, data ...interface{}) { | ||||
| 	if global.GLOBAL_CONFIG.Mysql.LogZap { | ||||
| 		global.GLOBAL_LOG.Info(fmt.Sprintf(message, data...)) | ||||
| 	} else { | ||||
| 		c.Writer.Printf(message, data...) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| type traceRecorder struct { | ||||
| 	logger.Interface | ||||
| 	BeginAt      time.Time | ||||
| 	SQL          string | ||||
| 	RowsAffected int64 | ||||
| 	Err          error | ||||
| } | ||||
| 
 | ||||
| func (t traceRecorder) New() *traceRecorder { | ||||
| 	return &traceRecorder{Interface: t.Interface, BeginAt: time.Now()} | ||||
| } | ||||
| 
 | ||||
| func (t *traceRecorder) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { | ||||
| 	t.BeginAt = begin | ||||
| 	t.SQL, t.RowsAffected = fc() | ||||
| 	t.Err = err | ||||
| } | ||||
|  | @ -0,0 +1,46 @@ | |||
| package initialize | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/swaggo/gin-swagger" | ||||
| 	"github.com/swaggo/gin-swagger/swaggerFiles" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/middleware" | ||||
| 	"github.com/ymm135/goweb-gin-demo/router" | ||||
| ) | ||||
| 
 | ||||
| func Routers() *gin.Engine { | ||||
| 	var Router = gin.Default() | ||||
| 
 | ||||
| 	// 为用户头像和文件提供静态地址
 | ||||
| 	//Router.StaticFS()
 | ||||
| 
 | ||||
| 	//排除Swagger路径
 | ||||
| 	Router.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) | ||||
| 
 | ||||
| 	RouterGroup := router.RouterGroupApp | ||||
| 
 | ||||
| 	PublicGroup := Router.Group("") | ||||
| 	{ | ||||
| 		// 健康监测
 | ||||
| 		PublicGroup.GET("/health", func(c *gin.Context) { | ||||
| 			c.JSON(200, "ok") | ||||
| 		}) | ||||
| 	} | ||||
| 	{ | ||||
| 		RouterGroup.InitBaseRouter(PublicGroup) | ||||
| 	} | ||||
| 
 | ||||
| 	PrivateGroup := Router.Group("") | ||||
| 	//路由要经过jwt和cas校验
 | ||||
| 	PrivateGroup.Use(middleware.JWTAuth()).Use(middleware.CasbinHandler()) | ||||
| 	{ | ||||
| 		RouterGroup.InitJwtRouter(PrivateGroup) | ||||
| 		RouterGroup.InitUserRouter(PrivateGroup) | ||||
| 		RouterGroup.InitMenuRouter(PrivateGroup) | ||||
| 	} | ||||
| 
 | ||||
| 	global.GLOBAL_LOG.Info("router register success") | ||||
| 
 | ||||
| 	return Router | ||||
| } | ||||
|  | @ -0,0 +1 @@ | |||
| log/2021-10-29.log | ||||
|  | @ -0,0 +1,18 @@ | |||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:16:39.843	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:41	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:16:39.845	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:46:41.209	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:46:50.466	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:41	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:46:50.467	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:54:51.771	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:54:56.370	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:41	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 15:54:56.371	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 16:06:32.084	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 16:06:38.104	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:41	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 16:06:38.106	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 17:59:22.081	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:00:16.251	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:42	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:00:16.253	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:01:02.383	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/api/web/user.go:36	登陆失败! 用户名不存在或者密码错误!	{"err": "sql: Scan error on column index 1, name \"created_at\": unsupported Scan, storing driver.Value type []uint8 into type *time.Time"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:07:16.567	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:10:08.680	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:42	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/28 - 18:10:08.682	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
|  | @ -0,0 +1,44 @@ | |||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:12:13.261	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:12:20.858	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:42	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:12:20.859	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:14:17.594	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:14:20.877	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:14:20.878	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:17:06.301	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:17:22.808	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:17:22.809	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:21:09.128	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:22:00.541	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:22:00.542	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:24:23.321	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:24:23.322	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:27:49.768	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:27:52.871	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 09:27:52.872	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:22:58.395	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:22:58.396	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:24:32.438	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:24:39.538	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:24:39.539	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:36:36.866	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:36:36.868	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:37:52.738	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:37:55.725	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:37:55.726	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 10:38:48.587	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/service/casbin.go:107	./resource/rbac_model.conf | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:08:33.915	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:08:39.791	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:08:39.792	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:34:01.181	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:34:06.092	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:34:06.093	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:35:57.123	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:36:02.757	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:36:02.760	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:37:00.250	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:37:00.252	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:44:47.224	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 11:44:47.227	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 14:24:04.838	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/initialize/router.go:43	router register success | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 14:24:04.839	[34minfo[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:31	server run success on 	{"address": ":8888"} | ||||
| [github.com/flipped-aurora/gin-vue-admin/server]2021/10/29 - 14:33:20.005	[31merror[0m	/Users/zero/work/mygithub/goweb-gin-demo/core/server.go:38	accept tcp [::]:8888: use of closed network connection | ||||
|  | @ -0,0 +1,16 @@ | |||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/core" | ||||
| 	_ "github.com/ymm135/goweb-gin-demo/docs" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/initialize" | ||||
| ) | ||||
| 
 | ||||
| func main() { | ||||
| 	global.GLOBAL_VP = core.Viper()      // 初始化Viper
 | ||||
| 	global.GLOBAL_LOG = core.Zap()       // 初始化zap日志库
 | ||||
| 	global.GLOBAL_DB = initialize.Gorm() // gorm连接数据库
 | ||||
| 
 | ||||
| 	core.RunServer() | ||||
| } | ||||
|  | @ -0,0 +1,35 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/service" | ||||
| ) | ||||
| 
 | ||||
| var casbinService = service.ServiceGroupApp.CasbinService | ||||
| 
 | ||||
| // 拦截器
 | ||||
| func CasbinHandler() gin.HandlerFunc { | ||||
| 	return func(c *gin.Context) { | ||||
| 		claims, _ := c.Get("claims") | ||||
| 		waitUse := claims.(*request.CustomClaims) | ||||
| 		// 获取请求的URI
 | ||||
| 		obj := c.Request.URL.RequestURI() | ||||
| 		// 获取请求方法
 | ||||
| 		act := c.Request.Method | ||||
| 		// 获取用户的角色
 | ||||
| 		sub := waitUse.AuthorityId | ||||
| 		e := casbinService.Casbin() | ||||
| 		// 判断策略中是否存在
 | ||||
| 		success, _ := e.Enforce(sub, obj, act) | ||||
| 		if global.GLOBAL_CONFIG.System.Env == "develop" || success { | ||||
| 			c.Next() | ||||
| 		} else { | ||||
| 			response.FailWithDetailed(gin.H{}, "权限不足", c) | ||||
| 			c.Abort() | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,72 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/response" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/ymm135/goweb-gin-demo/service" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
| 
 | ||||
| var jwtService = service.ServiceGroupApp.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 | ||||
| 		} | ||||
| 		// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
 | ||||
| 		//if err, _ = userService.FindUserByUuid(claims.UUID.String()); err != nil {
 | ||||
| 		//	_ = jwtService.JsonInBlacklist(web.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.GLOBAL_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.GLOBAL_CONFIG.System.UseMultipoint { | ||||
| 				err, RedisJwtToken := jwtService.GetRedisJWT(newClaims.Username) | ||||
| 				if err != nil { | ||||
| 					global.GLOBAL_LOG.Error("get redis jwt failed", zap.Any("err", err)) | ||||
| 				} else { // 当之前的取成功时才进行拉黑操作
 | ||||
| 					_ = jwtService.JsonInBlacklist(web.JwtBlacklist{Jwt: RedisJwtToken}) | ||||
| 				} | ||||
| 				// 无论如何都要记录当前的活跃状态
 | ||||
| 				_ = jwtService.SetRedisJWT(newToken, newClaims.Username) | ||||
| 			} | ||||
| 		} | ||||
| 		c.Set("claims", claims) | ||||
| 		c.Next() | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,85 @@ | |||
| package middleware | ||||
| 
 | ||||
| import ( | ||||
| 	"bytes" | ||||
| 	"io/ioutil" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/service" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
| 
 | ||||
| var operationRecordService = service.ServiceGroupApp.OperationRecordService | ||||
| 
 | ||||
| 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.GLOBAL_LOG.Error("read body from request error:", zap.Any("err", err)) | ||||
| 			} else { | ||||
| 				c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) | ||||
| 			} | ||||
| 		} | ||||
| 		if claims, ok := c.Get("claims"); ok { | ||||
| 			waitUse := claims.(*request.CustomClaims) | ||||
| 			userId = int(waitUse.ID) | ||||
| 		} else { | ||||
| 			id, err := strconv.Atoi(c.Request.Header.Get("x-user-id")) | ||||
| 			if err != nil { | ||||
| 				userId = 0 | ||||
| 			} | ||||
| 			userId = id | ||||
| 		} | ||||
| 		record := web.SysOperationRecord{ | ||||
| 			Ip:     c.ClientIP(), | ||||
| 			Method: c.Request.Method, | ||||
| 			Path:   c.Request.URL.Path, | ||||
| 			Agent:  c.Request.UserAgent(), | ||||
| 			Body:   string(body), | ||||
| 			UserID: userId, | ||||
| 		} | ||||
| 		// 存在某些未知错误 TODO
 | ||||
| 		//values := c.Request.Header.Values("content-type")
 | ||||
| 		//if len(values) >0 && strings.Contains(values[0], "boundary") {
 | ||||
| 		//	record.Body = "file"
 | ||||
| 		//}
 | ||||
| 		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 err := operationRecordService.CreateSysOperationRecord(record); err != nil { | ||||
| 			global.GLOBAL_LOG.Error("create operation record error:", zap.Any("err", 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) | ||||
| } | ||||
|  | @ -0,0 +1,23 @@ | |||
| package request | ||||
| 
 | ||||
| // Paging common input parameter structure
 | ||||
| type PageInfo struct { | ||||
| 	Page     int `json:"page" form:"page"`         // 页码
 | ||||
| 	PageSize int `json:"pageSize" form:"pageSize"` // 每页大小
 | ||||
| } | ||||
| 
 | ||||
| // Find by id structure
 | ||||
| type GetById struct { | ||||
| 	ID float64 `json:"id" form:"id"` // 主键ID
 | ||||
| } | ||||
| 
 | ||||
| type IdsReq struct { | ||||
| 	Ids []int `json:"ids" form:"ids"` | ||||
| } | ||||
| 
 | ||||
| // Get role by id structure
 | ||||
| type GetAuthorityId struct { | ||||
| 	AuthorityId string `json:"authorityId" form:"authorityId"` // 角色ID
 | ||||
| } | ||||
| 
 | ||||
| type Empty struct{} | ||||
|  | @ -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"` | ||||
| } | ||||
|  | @ -0,0 +1,54 @@ | |||
| package response | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"net/http" | ||||
| ) | ||||
| 
 | ||||
| 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) | ||||
| } | ||||
|  | @ -0,0 +1,18 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| ) | ||||
| 
 | ||||
| type SysAuthority struct { | ||||
| 	CreatedAt       time.Time      // 创建时间
 | ||||
| 	UpdatedAt       time.Time      // 更新时间
 | ||||
| 	DeletedAt       *time.Time     `sql:"index"` | ||||
| 	AuthorityId     string         `json:"authorityId" gorm:"not null;unique;primary_key;comment:角色ID;size:90"` // 角色ID
 | ||||
| 	AuthorityName   string         `json:"authorityName" gorm:"comment:角色名"`                                    // 角色名
 | ||||
| 	ParentId        string         `json:"parentId" gorm:"comment:父角色ID"`                                       // 父角色ID
 | ||||
| 	DataAuthorityId []SysAuthority `json:"dataAuthorityId" gorm:"many2many:sys_data_authority_id"` | ||||
| 	Children        []SysAuthority `json:"children" gorm:"-"` | ||||
| 	SysBaseMenus    []SysBaseMenu  `json:"menus" gorm:"many2many:sys_authority_menus;"` | ||||
| 	DefaultRouter   string         `json:"defaultRouter" gorm:"comment:默认菜单;default:dashboard"` // 默认菜单(默认dashboard)
 | ||||
| } | ||||
|  | @ -0,0 +1,13 @@ | |||
| package web | ||||
| 
 | ||||
| type SysMenu struct { | ||||
| 	SysBaseMenu | ||||
| 	MenuId      string                 `json:"menuId" gorm:"comment:菜单ID"` | ||||
| 	AuthorityId string                 `json:"-" gorm:"comment:角色ID"` | ||||
| 	Children    []SysMenu              `json:"children" gorm:"-"` | ||||
| 	Parameters  []SysBaseMenuParameter `json:"parameters" gorm:"foreignKey:SysBaseMenuID;references:MenuId"` | ||||
| } | ||||
| 
 | ||||
| func (s SysMenu) TableName() string { | ||||
| 	return "authority_menu" | ||||
| } | ||||
|  | @ -0,0 +1,36 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| ) | ||||
| 
 | ||||
| type SysBaseMenu struct { | ||||
| 	global.GLOBAL_MODEL | ||||
| 	MenuLevel     uint                              `json:"-"` | ||||
| 	ParentId      string                            `json:"parentId" gorm:"comment:父菜单ID"`     // 父菜单ID
 | ||||
| 	Path          string                            `json:"path" gorm:"comment:路由path"`        // 路由path
 | ||||
| 	Name          string                            `json:"name" gorm:"comment:路由name"`        // 路由name
 | ||||
| 	Hidden        bool                              `json:"hidden" gorm:"comment:是否在列表隐藏"`     // 是否在列表隐藏
 | ||||
| 	Component     string                            `json:"component" gorm:"comment:对应前端文件路径"` // 对应前端文件路径
 | ||||
| 	Sort          int                               `json:"sort" gorm:"comment:排序标记"`          // 排序标记
 | ||||
| 	Meta          `json:"meta" gorm:"comment:附加属性"` // 附加属性
 | ||||
| 	SysAuthoritys []SysAuthority                    `json:"authoritys" gorm:"many2many:sys_authority_menus;"` | ||||
| 	Children      []SysBaseMenu                     `json:"children" gorm:"-"` | ||||
| 	Parameters    []SysBaseMenuParameter            `json:"parameters"` | ||||
| } | ||||
| 
 | ||||
| type Meta struct { | ||||
| 	KeepAlive   bool   `json:"keepAlive" gorm:"comment:是否缓存"`           // 是否缓存
 | ||||
| 	DefaultMenu bool   `json:"defaultMenu" gorm:"comment:是否是基础路由(开发中)"` // 是否是基础路由(开发中)
 | ||||
| 	Title       string `json:"title" gorm:"comment:菜单名"`                // 菜单名
 | ||||
| 	Icon        string `json:"icon" gorm:"comment:菜单图标"`                // 菜单图标
 | ||||
| 	CloseTab    bool   `json:"closeTab" gorm:"comment:自动关闭tab"`         // 自动关闭tab
 | ||||
| } | ||||
| 
 | ||||
| type SysBaseMenuParameter struct { | ||||
| 	global.GLOBAL_MODEL | ||||
| 	SysBaseMenuID uint | ||||
| 	Type          string `json:"type" gorm:"comment:地址栏携带参数为params还是query"` // 地址栏携带参数为params还是query
 | ||||
| 	Key           string `json:"key" gorm:"comment:地址栏携带参数的key"`            // 地址栏携带参数的key
 | ||||
| 	Value         string `json:"value" gorm:"comment:地址栏携带参数的值"`            // 地址栏携带参数的值
 | ||||
| } | ||||
|  | @ -0,0 +1,8 @@ | |||
| package web | ||||
| 
 | ||||
| type CasbinModel struct { | ||||
| 	Ptype       string `json:"ptype" gorm:"column:ptype"` | ||||
| 	AuthorityId string `json:"rolename" gorm:"column:v0"` | ||||
| 	Path        string `json:"path" gorm:"column:v1"` | ||||
| 	Method      string `json:"method" gorm:"column:v2"` | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| ) | ||||
| 
 | ||||
| type JwtBlacklist struct { | ||||
| 	global.GLOBAL_MODEL | ||||
| 	Jwt string `gorm:"type:text;comment:jwt"` | ||||
| } | ||||
|  | @ -0,0 +1,24 @@ | |||
| // 自动生成模板SysOperationRecord
 | ||||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| ) | ||||
| 
 | ||||
| // 如果含有time.Time 请自行import time包
 | ||||
| type SysOperationRecord struct { | ||||
| 	global.GLOBAL_MODEL | ||||
| 	Ip           string        `json:"ip" form:"ip" gorm:"column:ip;comment:请求ip"`                                   // 请求ip
 | ||||
| 	Method       string        `json:"method" form:"method" gorm:"column:method;comment:请求方法"`                       // 请求方法
 | ||||
| 	Path         string        `json:"path" form:"path" gorm:"column:path;comment:请求路径"`                             // 请求路径
 | ||||
| 	Status       int           `json:"status" form:"status" gorm:"column:status;comment:请求状态"`                       // 请求状态
 | ||||
| 	Latency      time.Duration `json:"latency" form:"latency" gorm:"column:latency;comment:延迟" swaggertype:"string"` // 延迟
 | ||||
| 	Agent        string        `json:"agent" form:"agent" gorm:"column:agent;comment:代理"`                            // 代理
 | ||||
| 	ErrorMessage string        `json:"error_message" form:"error_message" gorm:"column:error_message;comment:错误信息"`  // 错误信息
 | ||||
| 	Body         string        `json:"body" form:"body" gorm:"type:longtext;column:body;comment:请求Body"`             // 请求Body
 | ||||
| 	Resp         string        `json:"resp" form:"resp" gorm:"type:longtext;column:resp;comment:响应Body"`             // 响应Body
 | ||||
| 	UserID       int           `json:"user_id" form:"user_id" gorm:"column:user_id;comment:用户id"`                    // 用户id
 | ||||
| 	User         SysUser       `json:"user"` | ||||
| } | ||||
|  | @ -0,0 +1,26 @@ | |||
| package request | ||||
| 
 | ||||
| // Casbin info structure
 | ||||
| type CasbinInfo struct { | ||||
| 	Path   string `json:"path"`   // 路径
 | ||||
| 	Method string `json:"method"` // 方法
 | ||||
| } | ||||
| 
 | ||||
| // Casbin structure for input parameters
 | ||||
| type CasbinInReceive struct { | ||||
| 	AuthorityId string       `json:"authorityId"` // 权限id
 | ||||
| 	CasbinInfos []CasbinInfo `json:"casbinInfos"` | ||||
| } | ||||
| 
 | ||||
| func DefaultCasbin() []CasbinInfo { | ||||
| 	return []CasbinInfo{ | ||||
| 		{Path: "/menu/getMenu", Method: "POST"}, | ||||
| 		{Path: "/jwt/jsonInBlacklist", Method: "POST"}, | ||||
| 		{Path: "/base/login", Method: "POST"}, | ||||
| 		{Path: "/user/register", Method: "POST"}, | ||||
| 		{Path: "/user/changePassword", Method: "POST"}, | ||||
| 		{Path: "/user/setUserAuthority", Method: "POST"}, | ||||
| 		{Path: "/user/setUserInfo", Method: "PUT"}, | ||||
| 		{Path: "/user/getUserInfo", Method: "GET"}, | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| package request | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	uuid "github.com/satori/go.uuid" | ||||
| ) | ||||
| 
 | ||||
| // Custom claims structure
 | ||||
| type CustomClaims struct { | ||||
| 	UUID        uuid.UUID | ||||
| 	ID          uint | ||||
| 	Username    string | ||||
| 	NickName    string | ||||
| 	AuthorityId string | ||||
| 	BufferTime  int64 | ||||
| 	jwt.StandardClaims | ||||
| } | ||||
|  | @ -0,0 +1,27 @@ | |||
| package request | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| ) | ||||
| 
 | ||||
| // Add menu authority info structure
 | ||||
| type AddMenuAuthorityInfo struct { | ||||
| 	Menus       []web.SysBaseMenu `json:"menus"` | ||||
| 	AuthorityId string               `json:"authorityId"` // 角色ID
 | ||||
| } | ||||
| 
 | ||||
| func DefaultMenu() []web.SysBaseMenu { | ||||
| 	return []web.SysBaseMenu{{ | ||||
| 		GLOBAL_MODEL: global.GLOBAL_MODEL{ID: 1}, | ||||
| 		ParentId:  "0", | ||||
| 		Path:      "dashboard", | ||||
| 		Name:      "dashboard", | ||||
| 		Component: "view/dashboard/index.vue", | ||||
| 		Sort:      1, | ||||
| 		Meta: web.Meta{ | ||||
| 			Title: "仪表盘", | ||||
| 			Icon:  "setting", | ||||
| 		}, | ||||
| 	}} | ||||
| } | ||||
|  | @ -0,0 +1,11 @@ | |||
| package request | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| ) | ||||
| 
 | ||||
| type SysOperationRecordSearch struct { | ||||
| 	web.SysOperationRecord | ||||
| 	request.PageInfo | ||||
| } | ||||
|  | @ -0,0 +1,37 @@ | |||
| package request | ||||
| 
 | ||||
| // User register structure
 | ||||
| type Register struct { | ||||
| 	Username     string   `json:"userName"` | ||||
| 	Password     string   `json:"passWord"` | ||||
| 	NickName     string   `json:"nickName" gorm:"default:'QMPlusUser'"` | ||||
| 	HeaderImg    string   `json:"headerImg" gorm:"default:'https://qmplusimg.henrongyi.top/gva_header.jpg'"` | ||||
| 	AuthorityId  string   `json:"authorityId" gorm:"default:888"` | ||||
| 	AuthorityIds []string `json:"authorityIds"` | ||||
| } | ||||
| 
 | ||||
| // User login structure
 | ||||
| type Login struct { | ||||
| 	Username  string `json:"username"`  // 用户名
 | ||||
| 	Password  string `json:"password"`  // 密码
 | ||||
| 	Captcha   string `json:"captcha"`   // 验证码
 | ||||
| 	CaptchaId string `json:"captchaId"` // 验证码ID
 | ||||
| } | ||||
| 
 | ||||
| // Modify password structure
 | ||||
| type ChangePasswordStruct struct { | ||||
| 	Username    string `json:"username"`    // 用户名
 | ||||
| 	Password    string `json:"password"`    // 密码
 | ||||
| 	NewPassword string `json:"newPassword"` // 新密码
 | ||||
| } | ||||
| 
 | ||||
| // Modify  user's auth structure
 | ||||
| type SetUserAuth struct { | ||||
| 	AuthorityId string `json:"authorityId"` // 角色ID
 | ||||
| } | ||||
| 
 | ||||
| // Modify  user's auth structure
 | ||||
| type SetUserAuthorities struct { | ||||
| 	ID           uint | ||||
| 	AuthorityIds []string `json:"authorityIds"` // 角色ID
 | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| package response | ||||
| 
 | ||||
| import "github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 
 | ||||
| type SysAuthorityResponse struct { | ||||
| 	Authority web.SysAuthority `json:"authority"` | ||||
| } | ||||
| 
 | ||||
| type SysAuthorityCopyResponse struct { | ||||
| 	Authority      web.SysAuthority `json:"authority"` | ||||
| 	OldAuthorityId string              `json:"oldAuthorityId"` // 旧角色ID
 | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| package response | ||||
| 
 | ||||
| type SysCaptchaResponse struct { | ||||
| 	CaptchaId string `json:"captchaId"` | ||||
| 	PicPath   string `json:"picPath"` | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| package response | ||||
| 
 | ||||
| import "github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 
 | ||||
| type SysMenusResponse struct { | ||||
| 	Menus []web.SysMenu `json:"menus"` | ||||
| } | ||||
| 
 | ||||
| type SysBaseMenusResponse struct { | ||||
| 	Menus []web.SysBaseMenu `json:"menus"` | ||||
| } | ||||
| 
 | ||||
| type SysBaseMenuResponse struct { | ||||
| 	Menu web.SysBaseMenu `json:"menu"` | ||||
| } | ||||
|  | @ -0,0 +1,15 @@ | |||
| package response | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| ) | ||||
| 
 | ||||
| type SysUserResponse struct { | ||||
| 	User web.SysUser `json:"user"` | ||||
| } | ||||
| 
 | ||||
| type LoginResponse struct { | ||||
| 	User      web.SysUser `json:"user"` | ||||
| 	Token     string         `json:"token"` | ||||
| 	ExpiresAt int64          `json:"expiresAt"` | ||||
| } | ||||
|  | @ -0,0 +1,21 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/satori/go.uuid" | ||||
| ) | ||||
| 
 | ||||
| type SysUser struct { | ||||
| 	global.GLOBAL_MODEL | ||||
| 	UUID        uuid.UUID      `json:"uuid" gorm:"comment:用户UUID"`                                                           // 用户UUID
 | ||||
| 	Username    string         `json:"userName" gorm:"comment:用户登录名"`                                                        // 用户登录名
 | ||||
| 	Password    string         `json:"-"  gorm:"comment:用户登录密码"`                                                             // 用户登录密码
 | ||||
| 	NickName    string         `json:"nickName" gorm:"default:系统用户;comment:用户昵称"`                                            // 用户昵称
 | ||||
| 	SideMode    string         `json:"sideMode" gorm:"default:dark;comment:用户侧边主题"`                                          // 用户侧边主题
 | ||||
| 	HeaderImg   string         `json:"headerImg" gorm:"default:https://qmplusimg.henrongyi.top/gva_header.jpg;comment:用户头像"` // 用户头像
 | ||||
| 	BaseColor   string         `json:"baseColor" gorm:"default:#fff;comment:基础颜色"`                                           // 基础颜色
 | ||||
| 	ActiveColor string         `json:"activeColor" gorm:"default:#1890ff;comment:活跃颜色"`                                      // 活跃颜色
 | ||||
| 	AuthorityId string         `json:"authorityId" gorm:"default:888;comment:用户角色ID"`                                        // 用户角色ID
 | ||||
| 	Authority   SysAuthority   `json:"authority" gorm:"foreignKey:AuthorityId;references:AuthorityId;comment:用户角色"` | ||||
| 	Authorities []SysAuthority `json:"authorities" gorm:"many2many:sys_user_authority;"` | ||||
| } | ||||
|  | @ -0,0 +1,10 @@ | |||
| package web | ||||
| 
 | ||||
| type SysUseAuthority struct { | ||||
| 	SysUserId               uint   `gorm:"column:sys_user_id"` | ||||
| 	SysAuthorityAuthorityId string `gorm:"column:sys_authority_authority_id"` | ||||
| } | ||||
| 
 | ||||
| func (s *SysUseAuthority) TableName() string { | ||||
| 	return "sys_user_authority" | ||||
| } | ||||
|  | @ -0,0 +1,4 @@ | |||
| //go:build !packfile
 | ||||
| // +build !packfile
 | ||||
| 
 | ||||
| package packfile | ||||
|  | @ -0,0 +1,46 @@ | |||
| //go:build packfile
 | ||||
| // +build packfile
 | ||||
| 
 | ||||
| package packfile | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"io/ioutil" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| //go:generate go-bindata -o=staticFile.go -pkg=packfile -tags=packfile ../resource/... ../config.yaml
 | ||||
| 
 | ||||
| func writeFile(path string, data []byte) { | ||||
| 	// 如果文件夹不存在,预先创建文件夹
 | ||||
| 	if lastSeparator := strings.LastIndex(path, "/"); lastSeparator != -1 { | ||||
| 		dirPath := path[:lastSeparator] | ||||
| 		if _, err := os.Stat(dirPath); err != nil && os.IsNotExist(err) { | ||||
| 			os.MkdirAll(dirPath, os.ModePerm) | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	// 已存在的文件,不应该覆盖重写,可能在前端更改了配置文件等
 | ||||
| 	if _, err := os.Stat(path); os.IsNotExist(err) { | ||||
| 		if err2 := ioutil.WriteFile(path, data, os.ModePerm); err2 != nil { | ||||
| 			fmt.Printf("Write file failed: %s\n", path) | ||||
| 		} | ||||
| 	} else { | ||||
| 		fmt.Printf("File exist, skip: %s\n", path) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func init() { | ||||
| 	for key := range _bindata { | ||||
| 		filePath, _ := filepath.Abs(strings.TrimPrefix(key, ".")) | ||||
| 		data, err := Asset(key) | ||||
| 		if err != nil { | ||||
| 			// Asset was not found.
 | ||||
| 			fmt.Printf("Fail to find: %s\n", filePath) | ||||
| 		} else { | ||||
| 			writeFile(filePath, data) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 182 KiB | 
|  | @ -0,0 +1,14 @@ | |||
| [request_definition] | ||||
| r = sub, obj, act | ||||
| 
 | ||||
| [policy_definition] | ||||
| p = sub, obj, act | ||||
| 
 | ||||
| [role_definition] | ||||
| g = _, _ | ||||
| 
 | ||||
| [policy_effect] | ||||
| e = some(where (p.eft == allow)) | ||||
| 
 | ||||
| [matchers] | ||||
| m = r.sub == p.sub && ParamsMatch(r.obj,p.obj) && r.act == p.act | ||||
|  | @ -0,0 +1,12 @@ | |||
| package router | ||||
| 
 | ||||
| import "github.com/ymm135/goweb-gin-demo/router/web" | ||||
| 
 | ||||
| type RouterGroup struct { | ||||
| 	web.BaseRouter | ||||
| 	web.JwtRouter | ||||
| 	web.UserRouter | ||||
| 	web.MenuRouter | ||||
| } | ||||
| 
 | ||||
| var RouterGroupApp = new(RouterGroup) | ||||
|  | @ -0,0 +1,19 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"github.com/ymm135/goweb-gin-demo/api" | ||||
| ) | ||||
| 
 | ||||
| type BaseRouter struct { | ||||
| } | ||||
| 
 | ||||
| func (s *BaseRouter) InitBaseRouter(Router *gin.RouterGroup) (R gin.IRoutes) { | ||||
| 	baseRouter := Router.Group("base") | ||||
| 	var baseApi = api.ApiGroupApp.ApiGroup | ||||
| 	{ | ||||
| 		baseRouter.POST("login", baseApi.Login) | ||||
| 		baseRouter.POST("captcha", baseApi.Captcha) | ||||
| 	} | ||||
| 	return baseRouter | ||||
| } | ||||
|  | @ -0,0 +1,17 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	v1 "github.com/ymm135/goweb-gin-demo/api" | ||||
| ) | ||||
| 
 | ||||
| type JwtRouter struct { | ||||
| } | ||||
| 
 | ||||
| func (s *JwtRouter) InitJwtRouter(Router *gin.RouterGroup) { | ||||
| 	jwtRouter := Router.Group("jwt") | ||||
| 	var jwtApi = v1.ApiGroupApp.ApiGroup.JwtApi | ||||
| 	{ | ||||
| 		jwtRouter.POST("jsonInBlacklist", jwtApi.JsonInBlacklist) // jwt加入黑名单
 | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,30 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	v1 "github.com/ymm135/goweb-gin-demo/api" | ||||
| 	"github.com/ymm135/goweb-gin-demo/middleware" | ||||
| ) | ||||
| 
 | ||||
| type MenuRouter struct { | ||||
| } | ||||
| 
 | ||||
| func (s *MenuRouter) InitMenuRouter(Router *gin.RouterGroup) (R gin.IRoutes) { | ||||
| 	menuRouter := Router.Group("menu").Use(middleware.OperationRecord()) | ||||
| 	menuRouterWithoutRecord := Router.Group("menu") | ||||
| 	var authorityMenuApi = v1.ApiGroupApp.ApiGroup.AuthorityMenuApi | ||||
| 	{ | ||||
| 		menuRouter.POST("addBaseMenu", authorityMenuApi.AddBaseMenu)           // 新增菜单
 | ||||
| 		menuRouter.POST("addMenuAuthority", authorityMenuApi.AddMenuAuthority) //	增加menu和角色关联关系
 | ||||
| 		menuRouter.POST("deleteBaseMenu", authorityMenuApi.DeleteBaseMenu)     // 删除菜单
 | ||||
| 		menuRouter.POST("updateBaseMenu", authorityMenuApi.UpdateBaseMenu)     // 更新菜单
 | ||||
| 	} | ||||
| 	{ | ||||
| 		menuRouterWithoutRecord.POST("getMenu", authorityMenuApi.GetMenu)                   // 获取菜单树
 | ||||
| 		menuRouterWithoutRecord.POST("getMenuList", authorityMenuApi.GetMenuList)           // 分页获取基础menu列表
 | ||||
| 		menuRouterWithoutRecord.POST("getBaseMenuTree", authorityMenuApi.GetBaseMenuTree)   // 获取用户动态路由
 | ||||
| 		menuRouterWithoutRecord.POST("getMenuAuthority", authorityMenuApi.GetMenuAuthority) // 获取指定角色menu
 | ||||
| 		menuRouterWithoutRecord.POST("getBaseMenuById", authorityMenuApi.GetBaseMenuById)   // 根据id获取菜单
 | ||||
| 	} | ||||
| 	return menuRouter | ||||
| } | ||||
|  | @ -0,0 +1,28 @@ | |||
| package web | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	v1 "github.com/ymm135/goweb-gin-demo/api" | ||||
| 	"github.com/ymm135/goweb-gin-demo/middleware" | ||||
| ) | ||||
| 
 | ||||
| type UserRouter struct { | ||||
| } | ||||
| 
 | ||||
| func (s *UserRouter) InitUserRouter(Router *gin.RouterGroup) { | ||||
| 	userRouter := Router.Group("user").Use(middleware.OperationRecord()) | ||||
| 	userRouterWithoutRecord := Router.Group("user") | ||||
| 	var baseApi = v1.ApiGroupApp.ApiGroup.BaseApi | ||||
| 	{ | ||||
| 		userRouter.POST("register", baseApi.Register)                     // 用户注册账号
 | ||||
| 		userRouter.POST("changePassword", baseApi.ChangePassword)         // 用户修改密码
 | ||||
| 		userRouter.POST("setUserAuthority", baseApi.SetUserAuthority)     // 设置用户权限
 | ||||
| 		userRouter.DELETE("deleteUser", baseApi.DeleteUser)               // 删除用户
 | ||||
| 		userRouter.PUT("setUserInfo", baseApi.SetUserInfo)                // 设置用户信息
 | ||||
| 		userRouter.POST("setUserAuthorities", baseApi.SetUserAuthorities) // 设置用户权限组
 | ||||
| 	} | ||||
| 	{ | ||||
| 		userRouterWithoutRecord.POST("getUserList", baseApi.GetUserList) // 分页获取用户列表
 | ||||
| 		userRouterWithoutRecord.GET("getUserInfo", baseApi.GetUserInfo)  // 获取自身信息
 | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,187 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web/response" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: CreateAuthority
 | ||||
| //@description: 创建一个角色
 | ||||
| //@param: auth model.SysAuthority
 | ||||
| //@return: err error, authority model.SysAuthority
 | ||||
| 
 | ||||
| type AuthorityService struct { | ||||
| } | ||||
| 
 | ||||
| var AuthorityServiceApp = new(AuthorityService) | ||||
| 
 | ||||
| func (authorityService *AuthorityService) CreateAuthority(auth web.SysAuthority) (err error, authority web.SysAuthority) { | ||||
| 	var authorityBox web.SysAuthority | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("authority_id = ?", auth.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("存在相同角色id"), auth | ||||
| 	} | ||||
| 	err = global.GLOBAL_DB.Create(&auth).Error | ||||
| 	return err, auth | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: CopyAuthority
 | ||||
| //@description: 复制一个角色
 | ||||
| //@param: copyInfo response.SysAuthorityCopyResponse
 | ||||
| //@return: err error, authority model.SysAuthority
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) CopyAuthority(copyInfo response.SysAuthorityCopyResponse) (err error, authority web.SysAuthority) { | ||||
| 	var authorityBox web.SysAuthority | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("authority_id = ?", copyInfo.Authority.AuthorityId).First(&authorityBox).Error, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("存在相同角色id"), authority | ||||
| 	} | ||||
| 	copyInfo.Authority.Children = []web.SysAuthority{} | ||||
| 	err, menus := MenuServiceApp.GetMenuAuthority(&request.GetAuthorityId{AuthorityId: copyInfo.OldAuthorityId}) | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	var baseMenu []web.SysBaseMenu | ||||
| 	for _, v := range menus { | ||||
| 		intNum, _ := strconv.Atoi(v.MenuId) | ||||
| 		v.SysBaseMenu.ID = uint(intNum) | ||||
| 		baseMenu = append(baseMenu, v.SysBaseMenu) | ||||
| 	} | ||||
| 	copyInfo.Authority.SysBaseMenus = baseMenu | ||||
| 	err = global.GLOBAL_DB.Create(©Info.Authority).Error | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	paths := CasbinServiceApp.GetPolicyPathByAuthorityId(copyInfo.OldAuthorityId) | ||||
| 	err = CasbinServiceApp.UpdateCasbin(copyInfo.Authority.AuthorityId, paths) | ||||
| 	if err != nil { | ||||
| 		_ = authorityService.DeleteAuthority(©Info.Authority) | ||||
| 	} | ||||
| 	return err, copyInfo.Authority | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: UpdateAuthority
 | ||||
| //@description: 更改一个角色
 | ||||
| //@param: auth model.SysAuthority
 | ||||
| //@return: err error, authority model.SysAuthority
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) UpdateAuthority(auth web.SysAuthority) (err error, authority web.SysAuthority) { | ||||
| 	err = global.GLOBAL_DB.Where("authority_id = ?", auth.AuthorityId).First(&web.SysAuthority{}).Updates(&auth).Error | ||||
| 	return err, auth | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: DeleteAuthority
 | ||||
| //@description: 删除角色
 | ||||
| //@param: auth *model.SysAuthority
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) DeleteAuthority(auth *web.SysAuthority) (err error) { | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("authority_id = ?", auth.AuthorityId).First(&web.SysUser{}).Error, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("此角色有用户正在使用禁止删除") | ||||
| 	} | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("parent_id = ?", auth.AuthorityId).First(&web.SysAuthority{}).Error, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("此角色存在子角色不允许删除") | ||||
| 	} | ||||
| 	db := global.GLOBAL_DB.Preload("SysBaseMenus").Where("authority_id = ?", auth.AuthorityId).First(auth) | ||||
| 	err = db.Unscoped().Delete(auth).Error | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	if len(auth.SysBaseMenus) > 0 { | ||||
| 		err = global.GLOBAL_DB.Model(auth).Association("SysBaseMenus").Delete(auth.SysBaseMenus) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		//err = db.Association("SysBaseMenus").Delete(&auth)
 | ||||
| 	} else { | ||||
| 		err = db.Error | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 	} | ||||
| 	err = global.GLOBAL_DB.Delete(&[]web.SysUseAuthority{}, "sys_authority_authority_id = ?", auth.AuthorityId).Error | ||||
| 	CasbinServiceApp.ClearCasbin(0, auth.AuthorityId) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetAuthorityInfoList
 | ||||
| //@description: 分页获取数据
 | ||||
| //@param: info request.PageInfo
 | ||||
| //@return: err error, list interface{}, total int64
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) GetAuthorityInfoList(info request.PageInfo) (err error, list interface{}, total int64) { | ||||
| 	limit := info.PageSize | ||||
| 	offset := info.PageSize * (info.Page - 1) | ||||
| 	db := global.GLOBAL_DB.Model(&web.SysAuthority{}) | ||||
| 	err = db.Where("parent_id = 0").Count(&total).Error | ||||
| 	var authority []web.SysAuthority | ||||
| 	err = db.Limit(limit).Offset(offset).Preload("DataAuthorityId").Where("parent_id = 0").Find(&authority).Error | ||||
| 	if len(authority) > 0 { | ||||
| 		for k := range authority { | ||||
| 			err = authorityService.findChildrenAuthority(&authority[k]) | ||||
| 		} | ||||
| 	} | ||||
| 	return err, authority, total | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetAuthorityInfo
 | ||||
| //@description: 获取所有角色信息
 | ||||
| //@param: auth model.SysAuthority
 | ||||
| //@return: err error, sa model.SysAuthority
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) GetAuthorityInfo(auth web.SysAuthority) (err error, sa web.SysAuthority) { | ||||
| 	err = global.GLOBAL_DB.Preload("DataAuthorityId").Where("authority_id = ?", auth.AuthorityId).First(&sa).Error | ||||
| 	return err, sa | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetDataAuthority
 | ||||
| //@description: 设置角色资源权限
 | ||||
| //@param: auth model.SysAuthority
 | ||||
| //@return: error
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) SetDataAuthority(auth web.SysAuthority) error { | ||||
| 	var s web.SysAuthority | ||||
| 	global.GLOBAL_DB.Preload("DataAuthorityId").First(&s, "authority_id = ?", auth.AuthorityId) | ||||
| 	err := global.GLOBAL_DB.Model(&s).Association("DataAuthorityId").Replace(&auth.DataAuthorityId) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetMenuAuthority
 | ||||
| //@description: 菜单与角色绑定
 | ||||
| //@param: auth *model.SysAuthority
 | ||||
| //@return: error
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) SetMenuAuthority(auth *web.SysAuthority) error { | ||||
| 	var s web.SysAuthority | ||||
| 	global.GLOBAL_DB.Preload("SysBaseMenus").First(&s, "authority_id = ?", auth.AuthorityId) | ||||
| 	err := global.GLOBAL_DB.Model(&s).Association("SysBaseMenus").Replace(&auth.SysBaseMenus) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: findChildrenAuthority
 | ||||
| //@description: 查询子角色
 | ||||
| //@param: authority *model.SysAuthority
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (authorityService *AuthorityService) findChildrenAuthority(authority *web.SysAuthority) (err error) { | ||||
| 	err = global.GLOBAL_DB.Preload("DataAuthorityId").Where("parent_id = ?", authority.AuthorityId).Find(&authority.Children).Error | ||||
| 	if len(authority.Children) > 0 { | ||||
| 		for k := range authority.Children { | ||||
| 			err = authorityService.findChildrenAuthority(&authority.Children[k]) | ||||
| 		} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | @ -0,0 +1,107 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| type BaseMenuService struct { | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: DeleteBaseMenu
 | ||||
| //@description: 删除基础路由
 | ||||
| //@param: id float64
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (baseMenuService *BaseMenuService) DeleteBaseMenu(id float64) (err error) { | ||||
| 	err = global.GLOBAL_DB.Preload("Parameters").Where("parent_id = ?", id).First(&web.SysBaseMenu{}).Error | ||||
| 	if err != nil { | ||||
| 		var menu web.SysBaseMenu | ||||
| 		db := global.GLOBAL_DB.Preload("SysAuthoritys").Where("id = ?", id).First(&menu).Delete(&menu) | ||||
| 		err = global.GLOBAL_DB.Delete(&web.SysBaseMenuParameter{}, "sys_base_menu_id = ?", id).Error | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if len(menu.SysAuthoritys) > 0 { | ||||
| 			err = global.GLOBAL_DB.Model(&menu).Association("SysAuthoritys").Delete(&menu.SysAuthoritys) | ||||
| 		} else { | ||||
| 			err = db.Error | ||||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		return errors.New("此菜单存在子菜单不可删除") | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: UpdateBaseMenu
 | ||||
| //@description: 更新路由
 | ||||
| //@param: menu model.SysBaseMenu
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (baseMenuService *BaseMenuService) UpdateBaseMenu(menu web.SysBaseMenu) (err error) { | ||||
| 	var oldMenu web.SysBaseMenu | ||||
| 	upDateMap := make(map[string]interface{}) | ||||
| 	upDateMap["keep_alive"] = menu.KeepAlive | ||||
| 	upDateMap["close_tab"] = menu.CloseTab | ||||
| 	upDateMap["default_menu"] = menu.DefaultMenu | ||||
| 	upDateMap["parent_id"] = menu.ParentId | ||||
| 	upDateMap["path"] = menu.Path | ||||
| 	upDateMap["name"] = menu.Name | ||||
| 	upDateMap["hidden"] = menu.Hidden | ||||
| 	upDateMap["component"] = menu.Component | ||||
| 	upDateMap["title"] = menu.Title | ||||
| 	upDateMap["icon"] = menu.Icon | ||||
| 	upDateMap["sort"] = menu.Sort | ||||
| 
 | ||||
| 	err = global.GLOBAL_DB.Transaction(func(tx *gorm.DB) error { | ||||
| 		db := tx.Where("id = ?", menu.ID).Find(&oldMenu) | ||||
| 		if oldMenu.Name != menu.Name { | ||||
| 			if !errors.Is(tx.Where("id <> ? AND name = ?", menu.ID, menu.Name).First(&web.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { | ||||
| 				global.GLOBAL_LOG.Debug("存在相同name修改失败") | ||||
| 				return errors.New("存在相同name修改失败") | ||||
| 			} | ||||
| 		} | ||||
| 		txErr := tx.Unscoped().Delete(&web.SysBaseMenuParameter{}, "sys_base_menu_id = ?", menu.ID).Error | ||||
| 		if txErr != nil { | ||||
| 			global.GLOBAL_LOG.Debug(txErr.Error()) | ||||
| 			return txErr | ||||
| 		} | ||||
| 		if len(menu.Parameters) > 0 { | ||||
| 			for k := range menu.Parameters { | ||||
| 				menu.Parameters[k].SysBaseMenuID = menu.ID | ||||
| 			} | ||||
| 			txErr = tx.Create(&menu.Parameters).Error | ||||
| 			if txErr != nil { | ||||
| 				global.GLOBAL_LOG.Debug(txErr.Error()) | ||||
| 				return txErr | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		txErr = db.Updates(upDateMap).Error | ||||
| 		if txErr != nil { | ||||
| 			global.GLOBAL_LOG.Debug(txErr.Error()) | ||||
| 			return txErr | ||||
| 		} | ||||
| 		return nil | ||||
| 	}) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetBaseMenuById
 | ||||
| //@description: 返回当前选中menu
 | ||||
| //@param: id float64
 | ||||
| //@return: err error, menu model.SysBaseMenu
 | ||||
| 
 | ||||
| func (baseMenuService *BaseMenuService) GetBaseMenuById(id float64) (err error, menu web.SysBaseMenu) { | ||||
| 	err = global.GLOBAL_DB.Preload("Parameters").Where("id = ?", id).First(&menu).Error | ||||
| 	return | ||||
| } | ||||
|  | @ -0,0 +1,136 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/casbin/casbin/v2" | ||||
| 	"github.com/casbin/casbin/v2/util" | ||||
| 	gormadapter "github.com/casbin/gorm-adapter/v3" | ||||
| 	_ "github.com/go-sql-driver/mysql" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: UpdateCasbin
 | ||||
| //@description: 更新casbin权限
 | ||||
| //@param: authorityId string, casbinInfos []request.CasbinInfo
 | ||||
| //@return: error
 | ||||
| 
 | ||||
| type CasbinService struct { | ||||
| } | ||||
| 
 | ||||
| var CasbinServiceApp = new(CasbinService) | ||||
| 
 | ||||
| func (casbinService *CasbinService) UpdateCasbin(authorityId string, casbinInfos []request.CasbinInfo) error { | ||||
| 	casbinService.ClearCasbin(0, authorityId) | ||||
| 	rules := [][]string{} | ||||
| 	for _, v := range casbinInfos { | ||||
| 		cm := web.CasbinModel{ | ||||
| 			Ptype:       "p", | ||||
| 			AuthorityId: authorityId, | ||||
| 			Path:        v.Path, | ||||
| 			Method:      v.Method, | ||||
| 		} | ||||
| 		rules = append(rules, []string{cm.AuthorityId, cm.Path, cm.Method}) | ||||
| 	} | ||||
| 	e := casbinService.Casbin() | ||||
| 	success, _ := e.AddPolicies(rules) | ||||
| 	if !success { | ||||
| 		return errors.New("存在相同api,添加失败,请联系管理员") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: UpdateCasbinApi
 | ||||
| //@description: API更新随动
 | ||||
| //@param: oldPath string, newPath string, oldMethod string, newMethod string
 | ||||
| //@return: error
 | ||||
| 
 | ||||
| func (casbinService *CasbinService) UpdateCasbinApi(oldPath string, newPath string, oldMethod string, newMethod string) error { | ||||
| 	err := global.GLOBAL_DB.Table("casbin_rule").Model(&web.CasbinModel{}).Where("v1 = ? AND v2 = ?", oldPath, oldMethod).Updates(map[string]interface{}{ | ||||
| 		"v1": newPath, | ||||
| 		"v2": newMethod, | ||||
| 	}).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetPolicyPathByAuthorityId
 | ||||
| //@description: 获取权限列表
 | ||||
| //@param: authorityId string
 | ||||
| //@return: pathMaps []request.CasbinInfo
 | ||||
| 
 | ||||
| func (casbinService *CasbinService) GetPolicyPathByAuthorityId(authorityId string) (pathMaps []request.CasbinInfo) { | ||||
| 	e := casbinService.Casbin() | ||||
| 	list := e.GetFilteredPolicy(0, authorityId) | ||||
| 	for _, v := range list { | ||||
| 		pathMaps = append(pathMaps, request.CasbinInfo{ | ||||
| 			Path:   v[1], | ||||
| 			Method: v[2], | ||||
| 		}) | ||||
| 	} | ||||
| 	return pathMaps | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: ClearCasbin
 | ||||
| //@description: 清除匹配的权限
 | ||||
| //@param: v int, p ...string
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func (casbinService *CasbinService) ClearCasbin(v int, p ...string) bool { | ||||
| 	e := casbinService.Casbin() | ||||
| 	success, _ := e.RemoveFilteredPolicy(v, p...) | ||||
| 	return success | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Casbin
 | ||||
| //@description: 持久化到数据库  引入自定义规则
 | ||||
| //@return: *casbin.Enforcer
 | ||||
| 
 | ||||
| var ( | ||||
| 	syncedEnforcer *casbin.SyncedEnforcer | ||||
| 	once           sync.Once | ||||
| ) | ||||
| 
 | ||||
| func (casbinService *CasbinService) Casbin() *casbin.SyncedEnforcer { | ||||
| 	once.Do(func() { | ||||
| 		a, _ := gormadapter.NewAdapterByDB(global.GLOBAL_DB) | ||||
| 		syncedEnforcer, _ = casbin.NewSyncedEnforcer(global.GLOBAL_CONFIG.Casbin.ModelPath, a) | ||||
| 		syncedEnforcer.AddFunction("ParamsMatch", casbinService.ParamsMatchFunc) | ||||
| 	}) | ||||
| 	_ = syncedEnforcer.LoadPolicy() | ||||
| 	return syncedEnforcer | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: ParamsMatch
 | ||||
| //@description: 自定义规则函数
 | ||||
| //@param: fullNameKey1 string, key2 string
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func (casbinService *CasbinService) ParamsMatch(fullNameKey1 string, key2 string) bool { | ||||
| 	key1 := strings.Split(fullNameKey1, "?")[0] | ||||
| 	// 剥离路径后再使用casbin的keyMatch2
 | ||||
| 	return util.KeyMatch2(key1, key2) | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: ParamsMatchFunc
 | ||||
| //@description: 自定义规则函数
 | ||||
| //@param: args ...interface{}
 | ||||
| //@return: interface{}, error
 | ||||
| 
 | ||||
| func (casbinService *CasbinService) ParamsMatchFunc(args ...interface{}) (interface{}, error) { | ||||
| 	name1 := args[0].(string) | ||||
| 	name2 := args[1].(string) | ||||
| 
 | ||||
| 	return casbinService.ParamsMatch(name1, name2), nil | ||||
| } | ||||
|  | @ -0,0 +1,12 @@ | |||
| package service | ||||
| 
 | ||||
| type ServiceGroup struct { | ||||
| 	UserService | ||||
| 	JwtService | ||||
| 	CasbinService | ||||
| 	OperationRecordService | ||||
| 	MenuService | ||||
| 	BaseMenuService | ||||
| } | ||||
| 
 | ||||
| var ServiceGroupApp = new(ServiceGroup) | ||||
|  | @ -0,0 +1,78 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"context" | ||||
| 	"go.uber.org/zap" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| ) | ||||
| 
 | ||||
| type JwtService struct { | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: JsonInBlacklist
 | ||||
| //@description: 拉黑jwt
 | ||||
| //@param: jwtList model.JwtBlacklist
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (jwtService *JwtService) JsonInBlacklist(jwtList web.JwtBlacklist) (err error) { | ||||
| 	err = global.GLOBAL_DB.Create(&jwtList).Error | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	global.BlackCache.SetDefault(jwtList.Jwt, struct{}{}) | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: IsBlacklist
 | ||||
| //@description: 判断JWT是否在黑名单内部
 | ||||
| //@param: jwt string
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func (jwtService *JwtService) IsBlacklist(jwt string) bool { | ||||
| 	_, ok := global.BlackCache.Get(jwt) | ||||
| 	return ok | ||||
| 	//err := global.GLOBAL_DB.Where("jwt = ?", jwt).First(&web.JwtBlacklist{}).Error
 | ||||
| 	//isNotFound := errors.Is(err, gorm.ErrRecordNotFound)
 | ||||
| 	//return !isNotFound
 | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetRedisJWT
 | ||||
| //@description: 从redis取jwt
 | ||||
| //@param: userName string
 | ||||
| //@return: err error, redisJWT string
 | ||||
| 
 | ||||
| func (jwtService *JwtService) GetRedisJWT(userName string) (err error, redisJWT string) { | ||||
| 	redisJWT, err = global.GLOBAL_REDIS.Get(context.Background(), userName).Result() | ||||
| 	return err, redisJWT | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetRedisJWT
 | ||||
| //@description: jwt存入redis并设置过期时间
 | ||||
| //@param: jwt string, userName string
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (jwtService *JwtService) SetRedisJWT(jwt string, userName string) (err error) { | ||||
| 	// 此处过期时间等于jwt过期时间
 | ||||
| 	timer := time.Duration(global.GLOBAL_CONFIG.JWT.ExpiresTime) * time.Second | ||||
| 	err = global.GLOBAL_REDIS.Set(context.Background(), userName, jwt, timer).Err() | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| func LoadAll() { | ||||
| 	var data []string | ||||
| 	err := global.GLOBAL_DB.Model(&web.JwtBlacklist{}).Select("jwt").Find(&data).Error | ||||
| 	if err != nil { | ||||
| 		global.GLOBAL_LOG.Error("加载数据库jwt黑名单失败!", zap.Error(err)) | ||||
| 		return | ||||
| 	} | ||||
| 	for i := 0; i < len(data); i++ { | ||||
| 		global.BlackCache.SetDefault(data[i], struct{}{}) | ||||
| 	} // jwt黑名单 加入 BlackCache 中
 | ||||
| } | ||||
|  | @ -0,0 +1,159 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: getMenuTreeMap
 | ||||
| //@description: 获取路由总树map
 | ||||
| //@param: authorityId string
 | ||||
| //@return: err error, treeMap map[string][]model.SysMenu
 | ||||
| 
 | ||||
| type MenuService struct { | ||||
| } | ||||
| 
 | ||||
| var MenuServiceApp = new(MenuService) | ||||
| 
 | ||||
| func (menuService *MenuService) getMenuTreeMap(authorityId string) (err error, treeMap map[string][]web.SysMenu) { | ||||
| 	var allMenus []web.SysMenu | ||||
| 	treeMap = make(map[string][]web.SysMenu) | ||||
| 	err = global.GLOBAL_DB.Where("authority_id = ?", authorityId).Order("sort").Preload("Parameters").Find(&allMenus).Error | ||||
| 	for _, v := range allMenus { | ||||
| 		treeMap[v.ParentId] = append(treeMap[v.ParentId], v) | ||||
| 	} | ||||
| 	return err, treeMap | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetMenuTree
 | ||||
| //@description: 获取动态菜单树
 | ||||
| //@param: authorityId string
 | ||||
| //@return: err error, menus []model.SysMenu
 | ||||
| 
 | ||||
| func (menuService *MenuService) GetMenuTree(authorityId string) (err error, menus []web.SysMenu) { | ||||
| 	err, menuTree := menuService.getMenuTreeMap(authorityId) | ||||
| 	menus = menuTree["0"] | ||||
| 	for i := 0; i < len(menus); i++ { | ||||
| 		err = menuService.getChildrenList(&menus[i], menuTree) | ||||
| 	} | ||||
| 	return err, menus | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: getChildrenList
 | ||||
| //@description: 获取子菜单
 | ||||
| //@param: menu *model.SysMenu, treeMap map[string][]model.SysMenu
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (menuService *MenuService) getChildrenList(menu *web.SysMenu, treeMap map[string][]web.SysMenu) (err error) { | ||||
| 	menu.Children = treeMap[menu.MenuId] | ||||
| 	for i := 0; i < len(menu.Children); i++ { | ||||
| 		err = menuService.getChildrenList(&menu.Children[i], treeMap) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetInfoList
 | ||||
| //@description: 获取路由分页
 | ||||
| //@return: err error, list interface{}, total int64
 | ||||
| 
 | ||||
| func (menuService *MenuService) GetInfoList() (err error, list interface{}, total int64) { | ||||
| 	var menuList []web.SysBaseMenu | ||||
| 	err, treeMap := menuService.getBaseMenuTreeMap() | ||||
| 	menuList = treeMap["0"] | ||||
| 	for i := 0; i < len(menuList); i++ { | ||||
| 		err = menuService.getBaseChildrenList(&menuList[i], treeMap) | ||||
| 	} | ||||
| 	return err, menuList, total | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: getBaseChildrenList
 | ||||
| //@description: 获取菜单的子菜单
 | ||||
| //@param: menu *model.SysBaseMenu, treeMap map[string][]model.SysBaseMenu
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (menuService *MenuService) getBaseChildrenList(menu *web.SysBaseMenu, treeMap map[string][]web.SysBaseMenu) (err error) { | ||||
| 	menu.Children = treeMap[strconv.Itoa(int(menu.ID))] | ||||
| 	for i := 0; i < len(menu.Children); i++ { | ||||
| 		err = menuService.getBaseChildrenList(&menu.Children[i], treeMap) | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: AddBaseMenu
 | ||||
| //@description: 添加基础路由
 | ||||
| //@param: menu model.SysBaseMenu
 | ||||
| //@return: error
 | ||||
| 
 | ||||
| func (menuService *MenuService) AddBaseMenu(menu web.SysBaseMenu) error { | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("name = ?", menu.Name).First(&web.SysBaseMenu{}).Error, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("存在重复name,请修改name") | ||||
| 	} | ||||
| 	return global.GLOBAL_DB.Create(&menu).Error | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: getBaseMenuTreeMap
 | ||||
| //@description: 获取路由总树map
 | ||||
| //@return: err error, treeMap map[string][]model.SysBaseMenu
 | ||||
| 
 | ||||
| func (menuService *MenuService) getBaseMenuTreeMap() (err error, treeMap map[string][]web.SysBaseMenu) { | ||||
| 	var allMenus []web.SysBaseMenu | ||||
| 	treeMap = make(map[string][]web.SysBaseMenu) | ||||
| 	err = global.GLOBAL_DB.Order("sort").Preload("Parameters").Find(&allMenus).Error | ||||
| 	for _, v := range allMenus { | ||||
| 		treeMap[v.ParentId] = append(treeMap[v.ParentId], v) | ||||
| 	} | ||||
| 	return err, treeMap | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetBaseMenuTree
 | ||||
| //@description: 获取基础路由树
 | ||||
| //@return: err error, menus []model.SysBaseMenu
 | ||||
| 
 | ||||
| func (menuService *MenuService) GetBaseMenuTree() (err error, menus []web.SysBaseMenu) { | ||||
| 	err, treeMap := menuService.getBaseMenuTreeMap() | ||||
| 	menus = treeMap["0"] | ||||
| 	for i := 0; i < len(menus); i++ { | ||||
| 		err = menuService.getBaseChildrenList(&menus[i], treeMap) | ||||
| 	} | ||||
| 	return err, menus | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: AddMenuAuthority
 | ||||
| //@description: 为角色增加menu树
 | ||||
| //@param: menus []model.SysBaseMenu, authorityId string
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (menuService *MenuService) AddMenuAuthority(menus []web.SysBaseMenu, authorityId string) (err error) { | ||||
| 	var auth web.SysAuthority | ||||
| 	auth.AuthorityId = authorityId | ||||
| 	auth.SysBaseMenus = menus | ||||
| 	err = AuthorityServiceApp.SetMenuAuthority(&auth) | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetMenuAuthority
 | ||||
| //@description: 查看当前角色树
 | ||||
| //@param: info *request.GetAuthorityId
 | ||||
| //@return: err error, menus []model.SysMenu
 | ||||
| 
 | ||||
| func (menuService *MenuService) GetMenuAuthority(info *request.GetAuthorityId) (err error, menus []web.SysMenu) { | ||||
| 	err = global.GLOBAL_DB.Where("authority_id = ? ", info.AuthorityId).Order("sort").Find(&menus).Error | ||||
| 	//sql := "SELECT authority_menu.keep_alive,authority_menu.default_menu,authority_menu.created_at,authority_menu.updated_at,authority_menu.deleted_at,authority_menu.menu_level,authority_menu.parent_id,authority_menu.path,authority_menu.`name`,authority_menu.hidden,authority_menu.component,authority_menu.title,authority_menu.icon,authority_menu.sort,authority_menu.menu_id,authority_menu.authority_id FROM authority_menu WHERE authority_menu.authority_id = ? ORDER BY authority_menu.sort ASC"
 | ||||
| 	//err = global.GLOBAL_DB.Raw(sql, authorityId).Scan(&menus).Error
 | ||||
| 	return err, menus | ||||
| } | ||||
|  | @ -0,0 +1,87 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	systemReq "github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| ) | ||||
| 
 | ||||
| //@author: [granty1](https://github.com/granty1)
 | ||||
| //@function: CreateSysOperationRecord
 | ||||
| //@description: 创建记录
 | ||||
| //@param: sysOperationRecord model.SysOperationRecord
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| type OperationRecordService struct { | ||||
| } | ||||
| 
 | ||||
| func (operationRecordService *OperationRecordService) CreateSysOperationRecord(sysOperationRecord web.SysOperationRecord) (err error) { | ||||
| 	err = global.GLOBAL_DB.Create(&sysOperationRecord).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [granty1](https://github.com/granty1)
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: DeleteSysOperationRecordByIds
 | ||||
| //@description: 批量删除记录
 | ||||
| //@param: ids request.IdsReq
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (operationRecordService *OperationRecordService) DeleteSysOperationRecordByIds(ids request.IdsReq) (err error) { | ||||
| 	err = global.GLOBAL_DB.Delete(&[]web.SysOperationRecord{}, "id in (?)", ids.Ids).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [granty1](https://github.com/granty1)
 | ||||
| //@function: DeleteSysOperationRecord
 | ||||
| //@description: 删除操作记录
 | ||||
| //@param: sysOperationRecord model.SysOperationRecord
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (operationRecordService *OperationRecordService) DeleteSysOperationRecord(sysOperationRecord web.SysOperationRecord) (err error) { | ||||
| 	err = global.GLOBAL_DB.Delete(&sysOperationRecord).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [granty1](https://github.com/granty1)
 | ||||
| //@function: DeleteSysOperationRecord
 | ||||
| //@description: 根据id获取单条操作记录
 | ||||
| //@param: id uint
 | ||||
| //@return: err error, sysOperationRecord model.SysOperationRecord
 | ||||
| 
 | ||||
| func (operationRecordService *OperationRecordService) GetSysOperationRecord(id uint) (err error, sysOperationRecord web.SysOperationRecord) { | ||||
| 	err = global.GLOBAL_DB.Where("id = ?", id).First(&sysOperationRecord).Error | ||||
| 	return | ||||
| } | ||||
| 
 | ||||
| //@author: [granty1](https://github.com/granty1)
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetSysOperationRecordInfoList
 | ||||
| //@description: 分页获取操作记录列表
 | ||||
| //@param: info systemReq.SysOperationRecordSearch
 | ||||
| //@return: err error, list interface{}, total int64
 | ||||
| 
 | ||||
| func (operationRecordService *OperationRecordService) GetSysOperationRecordInfoList(info systemReq.SysOperationRecordSearch) (err error, list interface{}, total int64) { | ||||
| 	limit := info.PageSize | ||||
| 	offset := info.PageSize * (info.Page - 1) | ||||
| 	// 创建db
 | ||||
| 	db := global.GLOBAL_DB.Model(&web.SysOperationRecord{}) | ||||
| 	var sysOperationRecords []web.SysOperationRecord | ||||
| 	// 如果有条件搜索 下方会自动创建搜索语句
 | ||||
| 	if info.Method != "" { | ||||
| 		db = db.Where("method = ?", info.Method) | ||||
| 	} | ||||
| 	if info.Path != "" { | ||||
| 		db = db.Where("path LIKE ?", "%"+info.Path+"%") | ||||
| 	} | ||||
| 	if info.Status != 0 { | ||||
| 		db = db.Where("status = ?", info.Status) | ||||
| 	} | ||||
| 	err = db.Count(&total).Error | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	err = db.Order("id desc").Limit(limit).Offset(offset).Preload("User").Find(&sysOperationRecords).Error | ||||
| 	return err, sysOperationRecords, total | ||||
| } | ||||
|  | @ -0,0 +1,188 @@ | |||
| package service | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	uuid "github.com/satori/go.uuid" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/common/request" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web" | ||||
| 	"github.com/ymm135/goweb-gin-demo/utils" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Register
 | ||||
| //@description: 用户注册
 | ||||
| //@param: u model.SysUser
 | ||||
| //@return: err error, userInter model.SysUser
 | ||||
| 
 | ||||
| type UserService struct { | ||||
| } | ||||
| 
 | ||||
| func (userService *UserService) Register(u web.SysUser) (err error, userInter web.SysUser) { | ||||
| 	var user web.SysUser | ||||
| 	if !errors.Is(global.GLOBAL_DB.Where("username = ?", u.Username).First(&user).Error, gorm.ErrRecordNotFound) { // 判断用户名是否注册
 | ||||
| 		return errors.New("用户名已注册"), userInter | ||||
| 	} | ||||
| 	// 否则 附加uuid 密码md5简单加密 注册
 | ||||
| 	u.Password = utils.MD5V([]byte(u.Password)) | ||||
| 	u.UUID = uuid.NewV4() | ||||
| 	err = global.GLOBAL_DB.Create(&u).Error | ||||
| 	return err, u | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Login
 | ||||
| //@description: 用户登录
 | ||||
| //@param: u *model.SysUser
 | ||||
| //@return: err error, userInter *model.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) Login(u *web.SysUser) (err error, userInter *web.SysUser) { | ||||
| 	var user web.SysUser | ||||
| 	u.Password = utils.MD5V([]byte(u.Password)) | ||||
| 	err = global.GLOBAL_DB.Where("username = ? AND password = ?", u.Username, u.Password).Preload("Authorities").Preload("Authority").First(&user).Error | ||||
| 	return err, &user | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: ChangePassword
 | ||||
| //@description: 修改用户密码
 | ||||
| //@param: u *model.SysUser, newPassword string
 | ||||
| //@return: err error, userInter *model.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) ChangePassword(u *web.SysUser, newPassword string) (err error, userInter *web.SysUser) { | ||||
| 	var user web.SysUser | ||||
| 	u.Password = utils.MD5V([]byte(u.Password)) | ||||
| 	err = global.GLOBAL_DB.Where("username = ? AND password = ?", u.Username, u.Password).First(&user).Update("password", utils.MD5V([]byte(newPassword))).Error | ||||
| 	return err, u | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetUserInfoList
 | ||||
| //@description: 分页获取数据
 | ||||
| //@param: info request.PageInfo
 | ||||
| //@return: err error, list interface{}, total int64
 | ||||
| 
 | ||||
| func (userService *UserService) GetUserInfoList(info request.PageInfo) (err error, list interface{}, total int64) { | ||||
| 	limit := info.PageSize | ||||
| 	offset := info.PageSize * (info.Page - 1) | ||||
| 	db := global.GLOBAL_DB.Model(&web.SysUser{}) | ||||
| 	var userList []web.SysUser | ||||
| 	err = db.Count(&total).Error | ||||
| 	if err != nil { | ||||
| 		return | ||||
| 	} | ||||
| 	err = db.Limit(limit).Offset(offset).Preload("Authorities").Preload("Authority").Find(&userList).Error | ||||
| 	return err, userList, total | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetUserAuthority
 | ||||
| //@description: 设置一个用户的权限
 | ||||
| //@param: uuid uuid.UUID, authorityId string
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (userService *UserService) SetUserAuthority(id uint, uuid uuid.UUID, authorityId string) (err error) { | ||||
| 	assignErr := global.GLOBAL_DB.Where("sys_user_id = ? AND sys_authority_authority_id = ?", id, authorityId).First(&web.SysUseAuthority{}).Error | ||||
| 	if errors.Is(assignErr, gorm.ErrRecordNotFound) { | ||||
| 		return errors.New("该用户无此角色") | ||||
| 	} | ||||
| 	err = global.GLOBAL_DB.Where("uuid = ?", uuid).First(&web.SysUser{}).Update("authority_id", authorityId).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetUserAuthorities
 | ||||
| //@description: 设置一个用户的权限
 | ||||
| //@param: id uint, authorityIds []string
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (userService *UserService) SetUserAuthorities(id uint, authorityIds []string) (err error) { | ||||
| 	return global.GLOBAL_DB.Transaction(func(tx *gorm.DB) error { | ||||
| 		TxErr := tx.Delete(&[]web.SysUseAuthority{}, "sys_user_id = ?", id).Error | ||||
| 		if TxErr != nil { | ||||
| 			return TxErr | ||||
| 		} | ||||
| 		useAuthority := []web.SysUseAuthority{} | ||||
| 		for _, v := range authorityIds { | ||||
| 			useAuthority = append(useAuthority, web.SysUseAuthority{ | ||||
| 				id, v, | ||||
| 			}) | ||||
| 		} | ||||
| 		TxErr = tx.Create(&useAuthority).Error | ||||
| 		if TxErr != nil { | ||||
| 			return TxErr | ||||
| 		} | ||||
| 		TxErr = tx.Where("id = ?", id).First(&web.SysUser{}).Update("authority_id", authorityIds[0]).Error | ||||
| 		if TxErr != nil { | ||||
| 			return TxErr | ||||
| 		} | ||||
| 		// 返回 nil 提交事务
 | ||||
| 		return nil | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: DeleteUser
 | ||||
| //@description: 删除用户
 | ||||
| //@param: id float64
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func (userService *UserService) DeleteUser(id float64) (err error) { | ||||
| 	var user web.SysUser | ||||
| 	err = global.GLOBAL_DB.Where("id = ?", id).Delete(&user).Error | ||||
| 	if err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	err = global.GLOBAL_DB.Delete(&[]web.SysUseAuthority{}, "sys_user_id = ?", id).Error | ||||
| 	return err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: SetUserInfo
 | ||||
| //@description: 设置用户信息
 | ||||
| //@param: reqUser model.SysUser
 | ||||
| //@return: err error, user model.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) SetUserInfo(reqUser web.SysUser) (err error, user web.SysUser) { | ||||
| 	err = global.GLOBAL_DB.Updates(&reqUser).Error | ||||
| 	return err, reqUser | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: GetUserInfo
 | ||||
| //@description: 获取用户信息
 | ||||
| //@param: uuid uuid.UUID
 | ||||
| //@return: err error, user web.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) GetUserInfo(uuid uuid.UUID) (err error, user web.SysUser) { | ||||
| 	var reqUser web.SysUser | ||||
| 	err = global.GLOBAL_DB.Preload("Authorities").Preload("Authority").First(&reqUser, "uuid = ?", uuid).Error | ||||
| 	return err, reqUser | ||||
| } | ||||
| 
 | ||||
| //@author: [SliverHorn](https://github.com/SliverHorn)
 | ||||
| //@function: FindUserById
 | ||||
| //@description: 通过id获取用户信息
 | ||||
| //@param: id int
 | ||||
| //@return: err error, user *model.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) FindUserById(id int) (err error, user *web.SysUser) { | ||||
| 	var u web.SysUser | ||||
| 	err = global.GLOBAL_DB.Where("`id` = ?", id).First(&u).Error | ||||
| 	return err, &u | ||||
| } | ||||
| 
 | ||||
| //@author: [SliverHorn](https://github.com/SliverHorn)
 | ||||
| //@function: FindUserByUuid
 | ||||
| //@description: 通过uuid获取用户信息
 | ||||
| //@param: uuid string
 | ||||
| //@return: err error, user *model.SysUser
 | ||||
| 
 | ||||
| func (userService *UserService) FindUserByUuid(uuid string) (err error, user *web.SysUser) { | ||||
| 	var u web.SysUser | ||||
| 	if err = global.GLOBAL_DB.Where("`uuid` = ?", uuid).First(&u).Error; err != nil { | ||||
| 		return errors.New("用户不存在"), &u | ||||
| 	} | ||||
| 	return nil, &u | ||||
| } | ||||
|  | @ -0,0 +1,52 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	uuid "github.com/satori/go.uuid" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	systemReq "github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| ) | ||||
| 
 | ||||
| // 从Gin的Context中获取从jwt解析出来的用户ID
 | ||||
| func GetUserID(c *gin.Context) uint { | ||||
| 	if claims, exists := c.Get("claims"); !exists { | ||||
| 		global.GLOBAL_LOG.Error("从Gin的Context中获取从jwt解析出来的用户ID失败, 请检查路由是否使用jwt中间件!") | ||||
| 		return 0 | ||||
| 	} else { | ||||
| 		waitUse := claims.(*systemReq.CustomClaims) | ||||
| 		return waitUse.ID | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 从Gin的Context中获取从jwt解析出来的用户UUID
 | ||||
| func GetUserUuid(c *gin.Context) uuid.UUID { | ||||
| 	if claims, exists := c.Get("claims"); !exists { | ||||
| 		global.GLOBAL_LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") | ||||
| 		return uuid.UUID{} | ||||
| 	} else { | ||||
| 		waitUse := claims.(*systemReq.CustomClaims) | ||||
| 		return waitUse.UUID | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 从Gin的Context中获取从jwt解析出来的用户角色id
 | ||||
| func GetUserAuthorityId(c *gin.Context) string { | ||||
| 	if claims, exists := c.Get("claims"); !exists { | ||||
| 		global.GLOBAL_LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") | ||||
| 		return "" | ||||
| 	} else { | ||||
| 		waitUse := claims.(*systemReq.CustomClaims) | ||||
| 		return waitUse.AuthorityId | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 从Gin的Context中获取从jwt解析出来的用户角色id
 | ||||
| func GetUserInfo(c *gin.Context) *systemReq.CustomClaims { | ||||
| 	if claims, exists := c.Get("claims"); !exists { | ||||
| 		global.GLOBAL_LOG.Error("从Gin的Context中获取从jwt解析出来的用户UUID失败, 请检查路由是否使用jwt中间件!") | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		waitUse := claims.(*systemReq.CustomClaims) | ||||
| 		return waitUse | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,6 @@ | |||
| package utils | ||||
| 
 | ||||
| const ( | ||||
| 	ConfigEnv  = "GVA_CONFIG" | ||||
| 	ConfigFile = "config.yaml" | ||||
| ) | ||||
|  | @ -0,0 +1,48 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"go.uber.org/zap" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: PathExists
 | ||||
| //@description: 文件目录是否存在
 | ||||
| //@param: path string
 | ||||
| //@return: bool, error
 | ||||
| 
 | ||||
| func PathExists(path string) (bool, error) { | ||||
| 	_, err := os.Stat(path) | ||||
| 	if err == nil { | ||||
| 		return true, nil | ||||
| 	} | ||||
| 	if os.IsNotExist(err) { | ||||
| 		return false, nil | ||||
| 	} | ||||
| 	return false, err | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: CreateDir
 | ||||
| //@description: 批量创建文件夹
 | ||||
| //@param: dirs ...string
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func CreateDir(dirs ...string) (err error) { | ||||
| 	for _, v := range dirs { | ||||
| 		exist, err := PathExists(v) | ||||
| 		if err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 		if !exist { | ||||
| 			global.GLOBAL_LOG.Debug("create directory" + v) | ||||
| 			if err := os.MkdirAll(v, os.ModePerm); err != nil { | ||||
| 				global.GLOBAL_LOG.Error("create directory"+v, zap.Any(" error:", err)) | ||||
| 				return err | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return err | ||||
| } | ||||
|  | @ -0,0 +1,71 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"github.com/dgrijalva/jwt-go" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"github.com/ymm135/goweb-gin-demo/model/web/request" | ||||
| ) | ||||
| 
 | ||||
| type JWT struct { | ||||
| 	SigningKey []byte | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	TokenExpired     = errors.New("Token is expired") | ||||
| 	TokenNotValidYet = errors.New("Token not active yet") | ||||
| 	TokenMalformed   = errors.New("That's not even a token") | ||||
| 	TokenInvalid     = errors.New("Couldn't handle this token:") | ||||
| ) | ||||
| 
 | ||||
| func NewJWT() *JWT { | ||||
| 	return &JWT{ | ||||
| 		[]byte(global.GLOBAL_CONFIG.JWT.SigningKey), | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // 创建一个token
 | ||||
| func (j *JWT) CreateToken(claims request.CustomClaims) (string, error) { | ||||
| 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) | ||||
| 	return token.SignedString(j.SigningKey) | ||||
| } | ||||
| 
 | ||||
| // CreateTokenByOldToken 旧token 换新token 使用归并回源避免并发问题
 | ||||
| func (j *JWT) CreateTokenByOldToken(oldToken string, claims request.CustomClaims) (string, error) { | ||||
| 	v, err, _ := global.GLOBAL_Concurrency_Control.Do("JWT:"+oldToken, func() (interface{}, error) { | ||||
| 		return j.CreateToken(claims) | ||||
| 	}) | ||||
| 	return v.(string), err | ||||
| } | ||||
| 
 | ||||
| // 解析 token
 | ||||
| func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) { | ||||
| 	token, err := jwt.ParseWithClaims(tokenString, &request.CustomClaims{}, func(token *jwt.Token) (i interface{}, e error) { | ||||
| 		return j.SigningKey, nil | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		if ve, ok := err.(*jwt.ValidationError); ok { | ||||
| 			if ve.Errors&jwt.ValidationErrorMalformed != 0 { | ||||
| 				return nil, TokenMalformed | ||||
| 			} else if ve.Errors&jwt.ValidationErrorExpired != 0 { | ||||
| 				// Token is expired
 | ||||
| 				return nil, TokenExpired | ||||
| 			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 { | ||||
| 				return nil, TokenNotValidYet | ||||
| 			} else { | ||||
| 				return nil, TokenInvalid | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if token != nil { | ||||
| 		if claims, ok := token.Claims.(*request.CustomClaims); ok && token.Valid { | ||||
| 			return claims, nil | ||||
| 		} | ||||
| 		return nil, TokenInvalid | ||||
| 
 | ||||
| 	} else { | ||||
| 		return nil, TokenInvalid | ||||
| 
 | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,18 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"crypto/md5" | ||||
| 	"encoding/hex" | ||||
| ) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: MD5V
 | ||||
| //@description: md5加密
 | ||||
| //@param: str []byte
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func MD5V(str []byte, b ...byte) string { | ||||
| 	h := md5.New() | ||||
| 	h.Write(str) | ||||
| 	return hex.EncodeToString(h.Sum(b)) | ||||
| } | ||||
|  | @ -0,0 +1,32 @@ | |||
| //go:build !windows
 | ||||
| // +build !windows
 | ||||
| 
 | ||||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"time" | ||||
| 
 | ||||
| 	zaprotatelogs "github.com/lestrrat-go/file-rotatelogs" | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	"go.uber.org/zap/zapcore" | ||||
| ) | ||||
| 
 | ||||
| //@author: [SliverHorn](https://github.com/SliverHorn)
 | ||||
| //@function: GetWriteSyncer
 | ||||
| //@description: zap logger中加入file-rotatelogs
 | ||||
| //@return: zapcore.WriteSyncer, error
 | ||||
| 
 | ||||
| func GetWriteSyncer() (zapcore.WriteSyncer, error) { | ||||
| 	fileWriter, err := zaprotatelogs.New( | ||||
| 		path.Join(global.GLOBAL_CONFIG.Zap.Director, "%Y-%m-%d.log"), | ||||
| 		zaprotatelogs.WithLinkName(global.GLOBAL_CONFIG.Zap.LinkName), | ||||
| 		zaprotatelogs.WithMaxAge(7*24*time.Hour), | ||||
| 		zaprotatelogs.WithRotationTime(24*time.Hour), | ||||
| 	) | ||||
| 	if global.GLOBAL_CONFIG.Zap.LogInConsole { | ||||
| 		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err | ||||
| 	} | ||||
| 	return zapcore.AddSync(fileWriter), err | ||||
| } | ||||
|  | @ -0,0 +1,28 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/ymm135/goweb-gin-demo/global" | ||||
| 	zaprotatelogs "github.com/lestrrat-go/file-rotatelogs" | ||||
| 	"go.uber.org/zap/zapcore" | ||||
| ) | ||||
| 
 | ||||
| //@author: [SliverHorn](https://github.com/SliverHorn)
 | ||||
| //@function: GetWriteSyncer
 | ||||
| //@description: zap logger中加入file-rotatelogs
 | ||||
| //@return: zapcore.WriteSyncer, error
 | ||||
| 
 | ||||
| func GetWriteSyncer() (zapcore.WriteSyncer, error) { | ||||
| 	fileWriter, err := zaprotatelogs.New( | ||||
| 		path.Join(global.GVA_CONFIG.Zap.Director, "%Y-%m-%d.log"), | ||||
| 		zaprotatelogs.WithMaxAge(7*24*time.Hour), | ||||
| 		zaprotatelogs.WithRotationTime(24*time.Hour), | ||||
| 	) | ||||
| 	if global.GLOBAL_CONFIG.Zap.LogInConsole { | ||||
| 		return zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(fileWriter)), err | ||||
| 	} | ||||
| 	return zapcore.AddSync(fileWriter), err | ||||
| } | ||||
|  | @ -0,0 +1,106 @@ | |||
| package timer | ||||
| 
 | ||||
| import ( | ||||
| 	"sync" | ||||
| 
 | ||||
| 	"github.com/robfig/cron/v3" | ||||
| ) | ||||
| 
 | ||||
| type Timer interface { | ||||
| 	AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error) | ||||
| 	AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error) | ||||
| 	FindCron(taskName string) (*cron.Cron, bool) | ||||
| 	StartTask(taskName string) | ||||
| 	StopTask(taskName string) | ||||
| 	Remove(taskName string, id int) | ||||
| 	Clear(taskName string) | ||||
| 	Close() | ||||
| } | ||||
| 
 | ||||
| // timer 定时任务管理
 | ||||
| type timer struct { | ||||
| 	taskList map[string]*cron.Cron | ||||
| 	sync.Mutex | ||||
| } | ||||
| 
 | ||||
| // AddTaskByFunc 通过函数的方法添加任务
 | ||||
| func (t *timer) AddTaskByFunc(taskName string, spec string, task func()) (cron.EntryID, error) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if _, ok := t.taskList[taskName]; !ok { | ||||
| 		t.taskList[taskName] = cron.New() | ||||
| 	} | ||||
| 	id, err := t.taskList[taskName].AddFunc(spec, task) | ||||
| 	t.taskList[taskName].Start() | ||||
| 	return id, err | ||||
| } | ||||
| 
 | ||||
| // AddTaskByJob 通过接口的方法添加任务
 | ||||
| func (t *timer) AddTaskByJob(taskName string, spec string, job interface{ Run() }) (cron.EntryID, error) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if _, ok := t.taskList[taskName]; !ok { | ||||
| 		t.taskList[taskName] = cron.New() | ||||
| 	} | ||||
| 	id, err := t.taskList[taskName].AddJob(spec, job) | ||||
| 	t.taskList[taskName].Start() | ||||
| 	return id, err | ||||
| } | ||||
| 
 | ||||
| // FindCron 获取对应taskName的cron 可能会为空
 | ||||
| func (t *timer) FindCron(taskName string) (*cron.Cron, bool) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	v, ok := t.taskList[taskName] | ||||
| 	return v, ok | ||||
| } | ||||
| 
 | ||||
| // StartTask 开始任务
 | ||||
| func (t *timer) StartTask(taskName string) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if v, ok := t.taskList[taskName]; ok { | ||||
| 		v.Start() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // StopTask 停止任务
 | ||||
| func (t *timer) StopTask(taskName string) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if v, ok := t.taskList[taskName]; ok { | ||||
| 		v.Stop() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Remove 从taskName 删除指定任务
 | ||||
| func (t *timer) Remove(taskName string, id int) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if v, ok := t.taskList[taskName]; ok { | ||||
| 		v.Remove(cron.EntryID(id)) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Clear 清除任务
 | ||||
| func (t *timer) Clear(taskName string) { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	if v, ok := t.taskList[taskName]; ok { | ||||
| 		v.Stop() | ||||
| 		delete(t.taskList, taskName) | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| // Close 释放资源
 | ||||
| func (t *timer) Close() { | ||||
| 	t.Lock() | ||||
| 	defer t.Unlock() | ||||
| 	for _, v := range t.taskList { | ||||
| 		v.Stop() | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func NewTimerTask() Timer { | ||||
| 	return &timer{taskList: make(map[string]*cron.Cron)} | ||||
| } | ||||
|  | @ -0,0 +1,65 @@ | |||
| package timer | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| ) | ||||
| 
 | ||||
| var job = mockJob{} | ||||
| 
 | ||||
| type mockJob struct{} | ||||
| 
 | ||||
| func (job mockJob) Run() { | ||||
| 	mockFunc() | ||||
| } | ||||
| func mockFunc() { | ||||
| 	time.Sleep(time.Second) | ||||
| 	fmt.Println("1s...") | ||||
| } | ||||
| func TestNewTimerTask(t *testing.T) { | ||||
| 	tm := NewTimerTask() | ||||
| 	_tm := tm.(*timer) | ||||
| 
 | ||||
| 	{ | ||||
| 		_, err := tm.AddTaskByFunc("func", "@every 1s", mockFunc) | ||||
| 		assert.Nil(t, err) | ||||
| 		_, ok := _tm.taskList["func"] | ||||
| 		if !ok { | ||||
| 			t.Error("no find func") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		_, err := tm.AddTaskByJob("job", "@every 1s", job) | ||||
| 		assert.Nil(t, err) | ||||
| 		_, ok := _tm.taskList["job"] | ||||
| 		if !ok { | ||||
| 			t.Error("no find job") | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	{ | ||||
| 		_, ok := tm.FindCron("func") | ||||
| 		if !ok { | ||||
| 			t.Error("no find func") | ||||
| 		} | ||||
| 		_, ok = tm.FindCron("job") | ||||
| 		if !ok { | ||||
| 			t.Error("no find job") | ||||
| 		} | ||||
| 		_, ok = tm.FindCron("none") | ||||
| 		if ok { | ||||
| 			t.Error("find none") | ||||
| 		} | ||||
| 	} | ||||
| 	{ | ||||
| 		tm.Clear("func") | ||||
| 		_, ok := tm.FindCron("func") | ||||
| 		if ok { | ||||
| 			t.Error("find func") | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -0,0 +1,286 @@ | |||
| package utils | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"reflect" | ||||
| 	"regexp" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| ) | ||||
| 
 | ||||
| type Rules map[string][]string | ||||
| 
 | ||||
| type RulesMap map[string]Rules | ||||
| 
 | ||||
| var CustomizeMap = make(map[string]Rules) | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: RegisterRule
 | ||||
| //@description: 注册自定义规则方案建议在路由初始化层即注册
 | ||||
| //@param: key string, rule Rules
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func RegisterRule(key string, rule Rules) (err error) { | ||||
| 	if CustomizeMap[key] != nil { | ||||
| 		return errors.New(key + "已注册,无法重复注册") | ||||
| 	} else { | ||||
| 		CustomizeMap[key] = rule | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: NotEmpty
 | ||||
| //@description: 非空 不能为其对应类型的0值
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func NotEmpty() string { | ||||
| 	return "notEmpty" | ||||
| } | ||||
| 
 | ||||
| //@author: [zooqkl](https://github.com/zooqkl)
 | ||||
| //@function: RegexpMatch
 | ||||
| //@description: 正则校验 校验输入项是否满足正则表达式
 | ||||
| //@param:  rule string
 | ||||
| //@return: string
 | ||||
| func RegexpMatch(rule string) string { | ||||
| 	return "regexp=" + rule | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Lt
 | ||||
| //@description: 小于入参(<) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Lt(mark string) string { | ||||
| 	return "lt=" + mark | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Le
 | ||||
| //@description: 小于等于入参(<=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Le(mark string) string { | ||||
| 	return "le=" + mark | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Eq
 | ||||
| //@description: 等于入参(==) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Eq(mark string) string { | ||||
| 	return "eq=" + mark | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Ne
 | ||||
| //@description: 不等于入参(!=)  如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Ne(mark string) string { | ||||
| 	return "ne=" + mark | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Ge
 | ||||
| //@description: 大于等于入参(>=) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Ge(mark string) string { | ||||
| 	return "ge=" + mark | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Gt
 | ||||
| //@description: 大于入参(>) 如果为string array Slice则为长度比较 如果是 int uint float 则为数值比较
 | ||||
| //@param: mark string
 | ||||
| //@return: string
 | ||||
| 
 | ||||
| func Gt(mark string) string { | ||||
| 	return "gt=" + mark | ||||
| } | ||||
| 
 | ||||
| //
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: Verify
 | ||||
| //@description: 校验方法
 | ||||
| //@param: st interface{}, roleMap Rules(入参实例,规则map)
 | ||||
| //@return: err error
 | ||||
| 
 | ||||
| func Verify(st interface{}, roleMap Rules) (err error) { | ||||
| 	compareMap := map[string]bool{ | ||||
| 		"lt": true, | ||||
| 		"le": true, | ||||
| 		"eq": true, | ||||
| 		"ne": true, | ||||
| 		"ge": true, | ||||
| 		"gt": true, | ||||
| 	} | ||||
| 
 | ||||
| 	typ := reflect.TypeOf(st) | ||||
| 	val := reflect.ValueOf(st) // 获取reflect.Type类型
 | ||||
| 
 | ||||
| 	kd := val.Kind() // 获取到st对应的类别
 | ||||
| 	if kd != reflect.Struct { | ||||
| 		return errors.New("expect struct") | ||||
| 	} | ||||
| 	num := val.NumField() | ||||
| 	// 遍历结构体的所有字段
 | ||||
| 	for i := 0; i < num; i++ { | ||||
| 		tagVal := typ.Field(i) | ||||
| 		val := val.Field(i) | ||||
| 		if len(roleMap[tagVal.Name]) > 0 { | ||||
| 			for _, v := range roleMap[tagVal.Name] { | ||||
| 				switch { | ||||
| 				case v == "notEmpty": | ||||
| 					if isBlank(val) { | ||||
| 						return errors.New(tagVal.Name + "值不能为空") | ||||
| 					} | ||||
| 				case strings.Split(v, "=")[0] == "regexp": | ||||
| 					if !regexpMatch(strings.Split(v, "=")[1], val.String()) { | ||||
| 						return errors.New(tagVal.Name + "格式校验不通过") | ||||
| 					} | ||||
| 				case compareMap[strings.Split(v, "=")[0]]: | ||||
| 					if !compareVerify(val, v) { | ||||
| 						return errors.New(tagVal.Name + "长度或值不在合法范围," + v) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: compareVerify
 | ||||
| //@description: 长度和数字的校验方法 根据类型自动校验
 | ||||
| //@param: value reflect.Value, VerifyStr string
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func compareVerify(value reflect.Value, VerifyStr string) bool { | ||||
| 	switch value.Kind() { | ||||
| 	case reflect.String, reflect.Slice, reflect.Array: | ||||
| 		return compare(value.Len(), VerifyStr) | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||||
| 		return compare(value.Uint(), VerifyStr) | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		return compare(value.Float(), VerifyStr) | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		return compare(value.Int(), VerifyStr) | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: isBlank
 | ||||
| //@description: 非空校验
 | ||||
| //@param: value reflect.Value
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func isBlank(value reflect.Value) bool { | ||||
| 	switch value.Kind() { | ||||
| 	case reflect.String: | ||||
| 		return value.Len() == 0 | ||||
| 	case reflect.Bool: | ||||
| 		return !value.Bool() | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		return value.Int() == 0 | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||||
| 		return value.Uint() == 0 | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		return value.Float() == 0 | ||||
| 	case reflect.Interface, reflect.Ptr: | ||||
| 		return value.IsNil() | ||||
| 	} | ||||
| 	return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface()) | ||||
| } | ||||
| 
 | ||||
| //@author: [piexlmax](https://github.com/piexlmax)
 | ||||
| //@function: compare
 | ||||
| //@description: 比较函数
 | ||||
| //@param: value interface{}, VerifyStr string
 | ||||
| //@return: bool
 | ||||
| 
 | ||||
| func compare(value interface{}, VerifyStr string) bool { | ||||
| 	VerifyStrArr := strings.Split(VerifyStr, "=") | ||||
| 	val := reflect.ValueOf(value) | ||||
| 	switch val.Kind() { | ||||
| 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: | ||||
| 		VInt, VErr := strconv.ParseInt(VerifyStrArr[1], 10, 64) | ||||
| 		if VErr != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		switch { | ||||
| 		case VerifyStrArr[0] == "lt": | ||||
| 			return val.Int() < VInt | ||||
| 		case VerifyStrArr[0] == "le": | ||||
| 			return val.Int() <= VInt | ||||
| 		case VerifyStrArr[0] == "eq": | ||||
| 			return val.Int() == VInt | ||||
| 		case VerifyStrArr[0] == "ne": | ||||
| 			return val.Int() != VInt | ||||
| 		case VerifyStrArr[0] == "ge": | ||||
| 			return val.Int() >= VInt | ||||
| 		case VerifyStrArr[0] == "gt": | ||||
| 			return val.Int() > VInt | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: | ||||
| 		VInt, VErr := strconv.Atoi(VerifyStrArr[1]) | ||||
| 		if VErr != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		switch { | ||||
| 		case VerifyStrArr[0] == "lt": | ||||
| 			return val.Uint() < uint64(VInt) | ||||
| 		case VerifyStrArr[0] == "le": | ||||
| 			return val.Uint() <= uint64(VInt) | ||||
| 		case VerifyStrArr[0] == "eq": | ||||
| 			return val.Uint() == uint64(VInt) | ||||
| 		case VerifyStrArr[0] == "ne": | ||||
| 			return val.Uint() != uint64(VInt) | ||||
| 		case VerifyStrArr[0] == "ge": | ||||
| 			return val.Uint() >= uint64(VInt) | ||||
| 		case VerifyStrArr[0] == "gt": | ||||
| 			return val.Uint() > uint64(VInt) | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	case reflect.Float32, reflect.Float64: | ||||
| 		VFloat, VErr := strconv.ParseFloat(VerifyStrArr[1], 64) | ||||
| 		if VErr != nil { | ||||
| 			return false | ||||
| 		} | ||||
| 		switch { | ||||
| 		case VerifyStrArr[0] == "lt": | ||||
| 			return val.Float() < VFloat | ||||
| 		case VerifyStrArr[0] == "le": | ||||
| 			return val.Float() <= VFloat | ||||
| 		case VerifyStrArr[0] == "eq": | ||||
| 			return val.Float() == VFloat | ||||
| 		case VerifyStrArr[0] == "ne": | ||||
| 			return val.Float() != VFloat | ||||
| 		case VerifyStrArr[0] == "ge": | ||||
| 			return val.Float() >= VFloat | ||||
| 		case VerifyStrArr[0] == "gt": | ||||
| 			return val.Float() > VFloat | ||||
| 		default: | ||||
| 			return false | ||||
| 		} | ||||
| 	default: | ||||
| 		return false | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func regexpMatch(rule, matchStr string) bool { | ||||
| 	return regexp.MustCompile(rule).MatchString(matchStr) | ||||
| } | ||||
|  | @ -0,0 +1,18 @@ | |||
| package utils | ||||
| 
 | ||||
| var ( | ||||
| 	IdVerify               = Rules{"ID": {NotEmpty()}} | ||||
| 	ApiVerify              = Rules{"Path": {NotEmpty()}, "Description": {NotEmpty()}, "ApiGroup": {NotEmpty()}, "Method": {NotEmpty()}} | ||||
| 	MenuVerify             = Rules{"Path": {NotEmpty()}, "ParentId": {NotEmpty()}, "Name": {NotEmpty()}, "Component": {NotEmpty()}, "Sort": {Ge("0")}} | ||||
| 	MenuMetaVerify         = Rules{"Title": {NotEmpty()}} | ||||
| 	LoginVerify            = Rules{"CaptchaId": {NotEmpty()}, "Captcha": {NotEmpty()}, "Username": {NotEmpty()}, "Password": {NotEmpty()}} | ||||
| 	RegisterVerify         = Rules{"Username": {NotEmpty()}, "NickName": {NotEmpty()}, "Password": {NotEmpty()}, "AuthorityId": {NotEmpty()}} | ||||
| 	PageInfoVerify         = Rules{"Page": {NotEmpty()}, "PageSize": {NotEmpty()}} | ||||
| 	CustomerVerify         = Rules{"CustomerName": {NotEmpty()}, "CustomerPhoneData": {NotEmpty()}} | ||||
| 	AutoCodeVerify         = Rules{"Abbreviation": {NotEmpty()}, "StructName": {NotEmpty()}, "PackageName": {NotEmpty()}, "Fields": {NotEmpty()}} | ||||
| 	AuthorityVerify        = Rules{"AuthorityId": {NotEmpty()}, "AuthorityName": {NotEmpty()}, "ParentId": {NotEmpty()}} | ||||
| 	AuthorityIdVerify      = Rules{"AuthorityId": {NotEmpty()}} | ||||
| 	OldAuthorityVerify     = Rules{"OldAuthorityId": {NotEmpty()}} | ||||
| 	ChangePasswordVerify   = Rules{"Username": {NotEmpty()}, "Password": {NotEmpty()}, "NewPassword": {NotEmpty()}} | ||||
| 	SetUserAuthorityVerify = Rules{"AuthorityId": {NotEmpty()}} | ||||
| ) | ||||
		Loading…
	
		Reference in New Issue