增加用户/权限/菜单功能
							parent
							
								
									3d2f15ef13
								
							
						
					
					
						commit
						da4b26cdf6
					
				|  | @ -13,3 +13,6 @@ | ||||||
| 
 | 
 | ||||||
| # Dependency directories (remove the comment below to include it) | # Dependency directories (remove the comment below to include it) | ||||||
| # vendor/ | # vendor/ | ||||||
|  | .idea | ||||||
|  | .DS_Store | ||||||
|  | 
 | ||||||
|  |  | ||||||
							
								
								
									
										333
									
								
								README.md
								
								
								
								
							
							
						
						
									
										333
									
								
								README.md
								
								
								
								
							|  | @ -1,2 +1,335 @@ | ||||||
| # goweb-gin-demo | # goweb-gin-demo | ||||||
| go web脚手架 | 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