zhou.xiaoning il y a 3 ans
Parent
commit
f945d705a5

+ 38 - 0
api/v1/account/login.go

@@ -1 +1,39 @@
 package account
+
+import (
+	"mtp20access/global"
+	"mtp20access/model/account/request"
+	accountRsp "mtp20access/model/account/response"
+	"mtp20access/model/common/response"
+	accountService "mtp20access/service/account"
+	"mtp20access/utils"
+
+	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+)
+
+// InsertNewFuncmenu 账户登录
+// @Summary 账户登录
+// @accept application/json
+// @Produce application/json
+// @Param data body request.LoginReq true "登录入参"
+// @Success 200 {object} response.Response{data=accountRsp.LoginRsp,msg=string} "返回包括用户信息,token,过期时间"
+// @Router /Account/Login [post]
+// @Tags 账户服务
+func Login(c *gin.Context) {
+	g := utils.GinUtils{C: c}
+	r := request.LoginReq{}
+	g.BindJsonReq(&r)
+
+	if loginaccount, token, expiresAt, err := accountService.Login(r, g.C.ClientIP()); err == nil {
+		response.OkWithDetailed(accountRsp.LoginRsp{
+			LoginID:   int(loginaccount.LOGINID),
+			UserID:    int(loginaccount.USERID),
+			Token:     token,
+			ExpiresAt: expiresAt,
+		}, "登录成功", g.C)
+	} else {
+		global.M2A_LOG.Error(err.Error(), zap.Error(err))
+		response.FailWithMessage(err.Error(), c)
+	}
+}

+ 2 - 5
core/server.go

@@ -15,11 +15,8 @@ type server interface {
 	ListenAndServe() error
 }
 
-func RunServer() {
-	// 初始化redis服务
-	initialize.Redis()
-	// 从redis加载jwt数据
-
+// RunApiServer 启动Http API 服务
+func RunApiServer() {
 	// 启动服务
 	Router := initialize.Routers()
 	address := fmt.Sprintf(":%d", global.M2A_CONFIG.System.Addr)

+ 189 - 0
docs/docs.go

@@ -0,0 +1,189 @@
+// GENERATED BY THE COMMAND ABOVE; DO NOT EDIT
+// This file was generated by swaggo/swag
+
+package docs
+
+import (
+	"bytes"
+	"encoding/json"
+	"strings"
+
+	"github.com/alecthomas/template"
+	"github.com/swaggo/swag"
+)
+
+var doc = `{
+    "schemes": {{ marshal .Schemes }},
+    "swagger": "2.0",
+    "info": {
+        "description": "{{.Description}}",
+        "title": "{{.Title}}",
+        "contact": {},
+        "version": "{{.Version}}"
+    },
+    "host": "{{.Host}}",
+    "basePath": "{{.BasePath}}",
+    "paths": {
+        "/Account/Login": {
+            "post": {
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "账户登录",
+                "parameters": [
+                    {
+                        "description": "登录入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.LoginReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "返回包括用户信息,token,过期时间",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/response.LoginRsp"
+                                        },
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    },
+    "definitions": {
+        "request.LoginReq": {
+            "type": "object",
+            "required": [
+                "clientType",
+                "password",
+                "userName"
+            ],
+            "properties": {
+                "clientType": {
+                    "description": "客户端类型,2-PC交易端 3-手机客户端_安卓 4-网页客户端 5-微信客户端 6-手机客户端_苹果",
+                    "type": "integer"
+                },
+                "password": {
+                    "description": "密码",
+                    "type": "string"
+                },
+                "userName": {
+                    "description": "用户名,可以是LoginID/LoginCode/手机号码",
+                    "type": "string"
+                }
+            }
+        },
+        "response.LoginRsp": {
+            "type": "object",
+            "properties": {
+                "expiresAt": {
+                    "description": "过期时间",
+                    "type": "integer"
+                },
+                "loginId": {
+                    "description": "登录ID",
+                    "type": "integer"
+                },
+                "token": {
+                    "description": "新服务Token",
+                    "type": "string"
+                },
+                "userId": {
+                    "description": "用户ID",
+                    "type": "integer"
+                }
+            }
+        },
+        "response.Response": {
+            "type": "object",
+            "properties": {
+                "code": {
+                    "type": "integer"
+                },
+                "data": {
+                    "type": "object"
+                },
+                "msg": {
+                    "type": "string"
+                }
+            }
+        }
+    },
+    "securityDefinitions": {
+        "ApiKeyAuth": {
+            "type": "apiKey",
+            "name": "x-token",
+            "in": "header"
+        }
+    }
+}`
+
+type swaggerInfo struct {
+	Version     string
+	Host        string
+	BasePath    string
+	Schemes     []string
+	Title       string
+	Description string
+}
+
+// SwaggerInfo holds exported Swagger Info so clients can modify it
+var SwaggerInfo = swaggerInfo{
+	Version:     "0.0.1",
+	Host:        "",
+	BasePath:    "/",
+	Schemes:     []string{},
+	Title:       "Swagger Example API",
+	Description: "新接入服务",
+}
+
+type s struct{}
+
+func (s *s) ReadDoc() string {
+	sInfo := SwaggerInfo
+	sInfo.Description = strings.Replace(sInfo.Description, "\n", "\\n", -1)
+
+	t, err := template.New("swagger_info").Funcs(template.FuncMap{
+		"marshal": func(v interface{}) string {
+			a, _ := json.Marshal(v)
+			return string(a)
+		},
+	}).Parse(doc)
+	if err != nil {
+		return doc
+	}
+
+	var tpl bytes.Buffer
+	if err := t.Execute(&tpl, sInfo); err != nil {
+		return doc
+	}
+
+	return tpl.String()
+}
+
+func init() {
+	swag.Register(swag.Name, &s{})
+}

+ 126 - 0
docs/swagger.json

@@ -0,0 +1,126 @@
+{
+    "swagger": "2.0",
+    "info": {
+        "description": "新接入服务",
+        "title": "Swagger Example API",
+        "contact": {},
+        "version": "0.0.1"
+    },
+    "basePath": "/",
+    "paths": {
+        "/Account/Login": {
+            "post": {
+                "consumes": [
+                    "application/json"
+                ],
+                "produces": [
+                    "application/json"
+                ],
+                "tags": [
+                    "账户服务"
+                ],
+                "summary": "账户登录",
+                "parameters": [
+                    {
+                        "description": "登录入参",
+                        "name": "data",
+                        "in": "body",
+                        "required": true,
+                        "schema": {
+                            "$ref": "#/definitions/request.LoginReq"
+                        }
+                    }
+                ],
+                "responses": {
+                    "200": {
+                        "description": "返回包括用户信息,token,过期时间",
+                        "schema": {
+                            "allOf": [
+                                {
+                                    "$ref": "#/definitions/response.Response"
+                                },
+                                {
+                                    "type": "object",
+                                    "properties": {
+                                        "data": {
+                                            "$ref": "#/definitions/response.LoginRsp"
+                                        },
+                                        "msg": {
+                                            "type": "string"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                }
+            }
+        }
+    },
+    "definitions": {
+        "request.LoginReq": {
+            "type": "object",
+            "required": [
+                "clientType",
+                "password",
+                "userName"
+            ],
+            "properties": {
+                "clientType": {
+                    "description": "客户端类型,2-PC交易端 3-手机客户端_安卓 4-网页客户端 5-微信客户端 6-手机客户端_苹果",
+                    "type": "integer"
+                },
+                "password": {
+                    "description": "密码",
+                    "type": "string"
+                },
+                "userName": {
+                    "description": "用户名,可以是LoginID/LoginCode/手机号码",
+                    "type": "string"
+                }
+            }
+        },
+        "response.LoginRsp": {
+            "type": "object",
+            "properties": {
+                "expiresAt": {
+                    "description": "过期时间",
+                    "type": "integer"
+                },
+                "loginId": {
+                    "description": "登录ID",
+                    "type": "integer"
+                },
+                "token": {
+                    "description": "新服务Token",
+                    "type": "string"
+                },
+                "userId": {
+                    "description": "用户ID",
+                    "type": "integer"
+                }
+            }
+        },
+        "response.Response": {
+            "type": "object",
+            "properties": {
+                "code": {
+                    "type": "integer"
+                },
+                "data": {
+                    "type": "object"
+                },
+                "msg": {
+                    "type": "string"
+                }
+            }
+        }
+    },
+    "securityDefinitions": {
+        "ApiKeyAuth": {
+            "type": "apiKey",
+            "name": "x-token",
+            "in": "header"
+        }
+    }
+}

+ 82 - 0
docs/swagger.yaml

@@ -0,0 +1,82 @@
+basePath: /
+definitions:
+  request.LoginReq:
+    properties:
+      clientType:
+        description: 客户端类型,2-PC交易端 3-手机客户端_安卓 4-网页客户端 5-微信客户端 6-手机客户端_苹果
+        type: integer
+      password:
+        description: 密码
+        type: string
+      userName:
+        description: 用户名,可以是LoginID/LoginCode/手机号码
+        type: string
+    required:
+    - clientType
+    - password
+    - userName
+    type: object
+  response.LoginRsp:
+    properties:
+      expiresAt:
+        description: 过期时间
+        type: integer
+      loginId:
+        description: 登录ID
+        type: integer
+      token:
+        description: 新服务Token
+        type: string
+      userId:
+        description: 用户ID
+        type: integer
+    type: object
+  response.Response:
+    properties:
+      code:
+        type: integer
+      data:
+        type: object
+      msg:
+        type: string
+    type: object
+info:
+  contact: {}
+  description: 新接入服务
+  title: Swagger Example API
+  version: 0.0.1
+paths:
+  /Account/Login:
+    post:
+      consumes:
+      - application/json
+      parameters:
+      - description: 登录入参
+        in: body
+        name: data
+        required: true
+        schema:
+          $ref: '#/definitions/request.LoginReq'
+      produces:
+      - application/json
+      responses:
+        "200":
+          description: 返回包括用户信息,token,过期时间
+          schema:
+            allOf:
+            - $ref: '#/definitions/response.Response'
+            - properties:
+                data:
+                  $ref: '#/definitions/response.LoginRsp'
+                msg:
+                  type: string
+              type: object
+      summary: 账户登录
+      tags:
+      - 账户服务
+securityDefinitions:
+  ApiKeyAuth:
+    in: header
+    name: x-token
+    type: apiKey
+swagger: "2.0"

+ 53 - 0
global/client.go

@@ -0,0 +1,53 @@
+package global
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"sync"
+
+	"github.com/gorilla/websocket"
+	"github.com/mitchellh/mapstructure"
+)
+
+type LoginRedis struct {
+	LoginID   string `json:"loginId" redis:"loginId"`     // 登陆账号
+	UserID    string `json:"userId" redis:"userId"`       // 用户ID
+	SessionID string `json:"sessionId" redis:"sessionId"` // 终端sid
+	Token     string `json:"token" redis:"token"`         // 令牌
+	Group     string `json:"group" redis:"group"`         // 终端分组
+	Addr      string `json:"addr" redis:"addr"`           // 客户端地址信息 // FIXME: 由于本服务改用短连,所以每次提交请交请求可能会不一样,后期可判断是否在中间件中进行拦截
+}
+
+// FromMap Map to Struct
+func (r *LoginRedis) FromMap(val map[string]interface{}) error {
+	return mapstructure.Decode(val, r)
+}
+
+// ToMap Struct to Map
+func (r *LoginRedis) ToMap() (val map[string]interface{}, err error) {
+	if marshalContent, err := json.Marshal(r); err != nil {
+		return nil, err
+	} else {
+		d := json.NewDecoder(bytes.NewReader(marshalContent))
+		d.UseNumber() // 设置将float64转为一个number
+		if err := d.Decode(&val); err != nil {
+			fmt.Println(err)
+		} else {
+			for k, v := range val {
+				val[k] = v
+			}
+		}
+	}
+
+	return
+}
+
+type Client struct {
+	LoginRedis
+
+	wsConn    *websocket.Conn // websocket 连接
+	writeChan chan []byte     // 推送队列
+	closeChan chan struct{}   // 关闭信号
+	doClose   sync.Once       // 仅关闭通道一次
+}

+ 3 - 2
global/global.go

@@ -4,7 +4,6 @@ import (
 	"mtp20access/config"
 
 	"github.com/go-redis/redis/v8"
-	"github.com/songzhibin97/gkit/cache/local_cache"
 	"github.com/spf13/viper"
 	"github.com/streadway/amqp"
 	"go.uber.org/zap"
@@ -25,5 +24,7 @@ var (
 	M2A_LOG                 *zap.Logger
 	M2A_RABBITMQ            *RabbitMQ
 	M2A_Concurrency_Control = &singleflight.Group{}
-	BlackCache              local_cache.Cache
+	M2A_Clients             map[int]*Client
+
+	// BlackCache              local_cache.Cache
 )

+ 15 - 9
go.mod

@@ -3,45 +3,49 @@ module mtp20access
 go 1.18
 
 require (
+	github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
 	github.com/fsnotify/fsnotify v1.5.4
 	github.com/gin-gonic/gin v1.8.1
 	github.com/go-redis/redis/v8 v8.11.5
 	github.com/golang-jwt/jwt/v4 v4.4.2
+	github.com/gorilla/websocket v1.4.2
 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible
 	github.com/mattn/go-oci8 v0.1.1
-	github.com/songzhibin97/gkit v1.2.5
+	github.com/mitchellh/mapstructure v1.5.0
 	github.com/spf13/viper v1.12.0
 	github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271
 	github.com/swaggo/gin-swagger v1.3.0
+	github.com/swaggo/swag v1.7.0
 	go.uber.org/zap v1.21.0
 	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
 	xorm.io/xorm v1.3.1
 )
 
 require (
-	github.com/PuerkitoBio/purell v1.1.0 // indirect
+	github.com/KyleBanks/depth v1.2.1 // indirect
+	github.com/PuerkitoBio/purell v1.1.1 // indirect
 	github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
 	github.com/gin-contrib/sse v0.1.0 // indirect
-	github.com/go-openapi/jsonpointer v0.17.0 // indirect
-	github.com/go-openapi/jsonreference v0.19.0 // indirect
-	github.com/go-openapi/spec v0.19.0 // indirect
-	github.com/go-openapi/swag v0.17.0 // indirect
+	github.com/go-openapi/jsonpointer v0.19.5 // indirect
+	github.com/go-openapi/jsonreference v0.19.6 // indirect
+	github.com/go-openapi/spec v0.20.3 // indirect
+	github.com/go-openapi/swag v0.19.15 // indirect
 	github.com/go-playground/locales v0.14.0 // indirect
 	github.com/go-playground/universal-translator v0.18.0 // indirect
 	github.com/go-playground/validator/v10 v10.10.0 // indirect
 	github.com/goccy/go-json v0.9.7 // indirect
 	github.com/golang/snappy v0.0.4 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
+	github.com/josharian/intern v1.0.0 // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/leodido/go-urn v1.2.1 // indirect
 	github.com/lestrrat-go/strftime v1.0.6 // indirect
 	github.com/magiconair/properties v1.8.6 // indirect
-	github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 // indirect
+	github.com/mailru/easyjson v0.7.7 // indirect
 	github.com/mattn/go-isatty v0.0.14 // indirect
 	github.com/mattn/go-sqlite3 v1.14.14 // indirect
-	github.com/mitchellh/mapstructure v1.5.0 // indirect
 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/pelletier/go-toml v1.9.5 // indirect
@@ -53,7 +57,6 @@ require (
 	github.com/spf13/pflag v1.0.5 // indirect
 	github.com/stretchr/testify v1.8.0 // indirect
 	github.com/subosito/gotenv v1.3.0 // indirect
-	github.com/swaggo/swag v1.5.1 // indirect
 	github.com/syndtr/goleveldb v1.0.0 // indirect
 	github.com/ugorji/go/codec v1.2.7 // indirect
 	go.uber.org/atomic v1.7.0 // indirect
@@ -67,5 +70,8 @@ require (
 	gopkg.in/ini.v1 v1.66.4 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
+	modernc.org/ccgo/v3 v3.16.2 // indirect
+	modernc.org/libc v1.15.1 // indirect
+	modernc.org/sqlite v1.16.0 // indirect
 	xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978 // indirect
 )

+ 90 - 14
go.sum

@@ -42,9 +42,12 @@ gitee.com/travelliu/dm v1.8.11192/go.mod h1:DHTzyhCrM843x9VdKVbZ+GKXGRbKM2sJ4Lxi
 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/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
+github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
 github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/PuerkitoBio/purell v1.1.0 h1:rmGxhojJlM0tuKtfdvliR84CFHljx9ag64t2xmVkjK4=
 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/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -143,15 +146,26 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
 github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-openapi/jsonpointer v0.17.0 h1:nH6xp8XdXHx8dqveo0ZuJBluCO2qGrPbDNZ0dwoRHP0=
 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 h1:BqWKpV1dFd+AuiKlgtddwVIFQsuMpxfBDBHGfM2yNpk=
 github.com/go-openapi/jsonreference v0.19.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
-github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
+github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
+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/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
+github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
+github.com/go-openapi/spec v0.20.3 h1:uH9RQ6vdyPSs2pSy9fL8QPspDF2AMIMPtmK5coSSjtQ=
+github.com/go-openapi/spec v0.20.3/go.mod h1:gG4F8wdEDN+YPBMVnzE85Rbhf+Th2DTvA9nFPQ5AYEg=
 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.11/go.mod h1:Uc0gKkdR+ojzsEpjh39QChyu92vPgIr72POcgHMAgSY=
+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.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU=
@@ -250,6 +264,8 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
+github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
@@ -331,6 +347,8 @@ github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv
 github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -376,8 +394,12 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U
 github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
 github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
 github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
-github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329 h1:2gxZ0XQIU/5z3Z3bUBu+FXuk2pFbkN6tcwi/pjyaDic=
 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=
@@ -395,6 +417,7 @@ github.com/mattn/go-oci8 v0.1.1 h1:aEUDxNAyDG0tv8CA3TArnDQNyc4EhnWlsfxRgDHABHM=
 github.com/mattn/go-oci8 v0.1.1/go.mod h1:wjDx6Xm9q7dFtHJvIlrI99JytznLw5wQ4R+9mNXJwGI=
 github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
+github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw=
 github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -423,6 +446,7 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE
 github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w=
 github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
 github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs=
 github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
@@ -503,8 +527,6 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 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.2.5 h1:uLbqSTUNdFKtp8AjdkzuV3qvltQ494o7FAzZnziIs9c=
-github.com/songzhibin97/gkit v1.2.5/go.mod h1:fqP08z5X5p1rdYCoxTtovvDQ91wcWsablOuMPVqQHEc=
 github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
 github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
 github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
@@ -540,8 +562,9 @@ github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t
 github.com/swaggo/files v0.0.0-20190704085106-630677cd5c14/go.mod h1:gxQT6pBGRuIGunNf/+tSOB5OHvguWi8Tbt82WOkf35E=
 github.com/swaggo/gin-swagger v1.3.0 h1:eOmp7r57oUgZPw2dJOjcGNMse9cvXcI4tTqBcnZtPsI=
 github.com/swaggo/gin-swagger v1.3.0/go.mod h1:oy1BRA6WvgtCp848lhxce7BnWH4C8Bxa0m5SkWx+cS0=
-github.com/swaggo/swag v1.5.1 h1:2Agm8I4K5qb00620mHq0VJ05/KT4FtmALPIcQR9lEZM=
 github.com/swaggo/swag v1.5.1/go.mod h1:1Bl9F/ZBpVWh22nY0zmYyASPO1lI/zIwRDrpZU+tv8Y=
+github.com/swaggo/swag v1.7.0 h1:5bCA/MTLQoIqDXXyHfOpMeDvL9j68OY/udlK4pQoo4E=
+github.com/swaggo/swag v1.7.0/go.mod h1:BdPIL73gvS9NBsdi7M1JOxLvlbfvNRaBP8m6WT6Aajo=
 github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
 github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@@ -554,6 +577,7 @@ github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0
 github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
 github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
+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.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -669,6 +693,7 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/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-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
@@ -685,10 +710,13 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
+golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2 h1:NWy5+hlRbC7HK+PmcXVUmW1IMyFce7to56IUvhUFm7Y=
 golang.org/x/net v0.0.0-20220520000938-2e3eb7b945c2/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -763,6 +791,7 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -781,6 +810,7 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.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=
@@ -840,6 +870,7 @@ golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc
 golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
 golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
 golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
+golang.org/x/tools v0.0.0-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -957,6 +988,7 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
@@ -975,10 +1007,13 @@ gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRN
 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.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
 gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
 gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -1005,8 +1040,13 @@ modernc.org/cc/v3 v3.35.10/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g
 modernc.org/cc/v3 v3.35.15/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
 modernc.org/cc/v3 v3.35.16/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
 modernc.org/cc/v3 v3.35.17/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
-modernc.org/cc/v3 v3.35.18 h1:rMZhRcWrba0y3nVmdiQ7kxAgOOSq2m2f2VzjHLgEs6U=
 modernc.org/cc/v3 v3.35.18/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.20/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.22/go.mod h1:iPJg1pkwXqAV16SNgFBVYmggfMg6xhs+2oiO0vclK3g=
+modernc.org/cc/v3 v3.35.24/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
+modernc.org/cc/v3 v3.35.25/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
+modernc.org/cc/v3 v3.35.26 h1:S4B+fg6/9krLtfZ9lr7pfKiESopiv+Sm6lUUI3oc0fY=
+modernc.org/cc/v3 v3.35.26/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
 modernc.org/ccgo/v3 v3.9.5/go.mod h1:umuo2EP2oDSBnD3ckjaVUXMrmeAw8C8OSICVa0iFf60=
 modernc.org/ccgo/v3 v3.10.0/go.mod h1:c0yBmkRFi7uW4J7fwx/JiijwOjeAeR2NoSaRVFPmjMw=
 modernc.org/ccgo/v3 v3.11.0/go.mod h1:dGNposbDp9TOZ/1KBxghxtUp/bzErD0/0QW4hhSaBMI=
@@ -1041,9 +1081,25 @@ modernc.org/ccgo/v3 v3.12.66/go.mod h1:jUuxlCFZTUZLMV08s7B1ekHX5+LIAurKTTaugUr/E
 modernc.org/ccgo/v3 v3.12.67/go.mod h1:Bll3KwKvGROizP2Xj17GEGOTrlvB1XcVaBrC90ORO84=
 modernc.org/ccgo/v3 v3.12.73/go.mod h1:hngkB+nUUqzOf3iqsM48Gf1FZhY599qzVg1iX+BT3cQ=
 modernc.org/ccgo/v3 v3.12.81/go.mod h1:p2A1duHoBBg1mFtYvnhAnQyI6vL0uw5PGYLSIgF6rYY=
-modernc.org/ccgo/v3 v3.12.82 h1:wudcnJyjLj1aQQCXF3IM9Gz2X6UNjw+afIghzdtn0v8=
 modernc.org/ccgo/v3 v3.12.82/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
+modernc.org/ccgo/v3 v3.12.84/go.mod h1:ApbflUfa5BKadjHynCficldU1ghjen84tuM5jRynB7w=
+modernc.org/ccgo/v3 v3.12.86/go.mod h1:dN7S26DLTgVSni1PVA3KxxHTcykyDurf3OgUzNqTSrU=
+modernc.org/ccgo/v3 v3.12.90/go.mod h1:obhSc3CdivCRpYZmrvO88TXlW0NvoSVvdh/ccRjJYko=
+modernc.org/ccgo/v3 v3.12.92/go.mod h1:5yDdN7ti9KWPi5bRVWPl8UNhpEAtCjuEE7ayQnzzqHA=
+modernc.org/ccgo/v3 v3.13.1/go.mod h1:aBYVOUfIlcSnrsRVU8VRS35y2DIfpgkmVkYZ0tpIXi4=
+modernc.org/ccgo/v3 v3.15.9/go.mod h1:md59wBwDT2LznX/OTCPoVS6KIsdRgY8xqQwBV+hkTH0=
+modernc.org/ccgo/v3 v3.15.10/go.mod h1:wQKxoFn0ynxMuCLfFD09c8XPUCc8obfchoVR9Cn0fI8=
+modernc.org/ccgo/v3 v3.15.12/go.mod h1:VFePOWoCd8uDGRJpq/zfJ29D0EVzMSyID8LCMWYbX6I=
+modernc.org/ccgo/v3 v3.15.14/go.mod h1:144Sz2iBCKogb9OKwsu7hQEub3EVgOlyI8wMUPGKUXQ=
+modernc.org/ccgo/v3 v3.15.15/go.mod h1:z5qltXjU4PJl0pE5nhYQCvA9DhPHiWsl5GWl89+NSYE=
+modernc.org/ccgo/v3 v3.15.16/go.mod h1:XbKRMeMWMdq712Tr5ECgATYMrzJ+g9zAZEj2ktzBe24=
+modernc.org/ccgo/v3 v3.15.17/go.mod h1:bofnFkpRFf5gLY+mBZIyTW6FEcp26xi2lgOFk2Rlvs0=
+modernc.org/ccgo/v3 v3.15.18/go.mod h1:/2lv3WjHyanEr2sAPdGKRC38n6f0werut9BRXUjjX+A=
+modernc.org/ccgo/v3 v3.15.19/go.mod h1:TDJj+DxR26pkDteH2E5WQDj/xlmtsX7JdzkJkaZhOVU=
+modernc.org/ccgo/v3 v3.16.2 h1:FUklsEMps3Y2heuTOmn/l6mv83nQgCjW3nsU+1JXzuQ=
+modernc.org/ccgo/v3 v3.16.2/go.mod h1:w55kPTAqvRMAYS3Lwij6qhqIuBEYS3Z8QtDkjD8cnik=
 modernc.org/ccorpus v1.11.1/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
+modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ=
 modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
 modernc.org/libc v1.9.8/go.mod h1:U1eq8YWr/Kc1RWCMFUWEdkTg8OTcfLw2kY8EDwl039w=
 modernc.org/libc v1.9.11/go.mod h1:NyF3tsA5ArIjJ83XB0JlqhjTabTCHm9aX4XMPHyQn0Q=
@@ -1079,26 +1135,46 @@ modernc.org/libc v1.11.71/go.mod h1:DUOmMYe+IvKi9n6Mycyx3DbjfzSKrdr/0Vgt3j7P5gw=
 modernc.org/libc v1.11.75/go.mod h1:dGRVugT6edz361wmD9gk6ax1AbDSe0x5vji0dGJiPT0=
 modernc.org/libc v1.11.82/go.mod h1:NF+Ek1BOl2jeC7lw3a7Jj5PWyHPwWD4aq3wVKxqV1fI=
 modernc.org/libc v1.11.86/go.mod h1:ePuYgoQLmvxdNT06RpGnaDKJmDNEkV7ZPKI2jnsvZoE=
-modernc.org/libc v1.11.87 h1:PzIzOqtlzMDDcCzJ5cUP6h/Ku6Fa9iyflP2ccTY64aE=
 modernc.org/libc v1.11.87/go.mod h1:Qvd5iXTeLhI5PS0XSyqMY99282y+3euapQFxM7jYnpY=
+modernc.org/libc v1.11.88/go.mod h1:h3oIVe8dxmTcchcFuCcJ4nAWaoiwzKCdv82MM0oiIdQ=
+modernc.org/libc v1.11.98/go.mod h1:ynK5sbjsU77AP+nn61+k+wxUGRx9rOFcIqWYYMaDZ4c=
+modernc.org/libc v1.11.101/go.mod h1:wLLYgEiY2D17NbBOEp+mIJJJBGSiy7fLL4ZrGGZ+8jI=
+modernc.org/libc v1.12.0/go.mod h1:2MH3DaF/gCU8i/UBiVE1VFRos4o523M7zipmwH8SIgQ=
+modernc.org/libc v1.14.1/go.mod h1:npFeGWjmZTjFeWALQLrvklVmAxv4m80jnG3+xI8FdJk=
+modernc.org/libc v1.14.2/go.mod h1:MX1GBLnRLNdvmK9azU9LCxZ5lMyhrbEMK8rG3X/Fe34=
+modernc.org/libc v1.14.3/go.mod h1:GPIvQVOVPizzlqyRX3l756/3ppsAgg1QgPxjr5Q4agQ=
+modernc.org/libc v1.14.6/go.mod h1:2PJHINagVxO4QW/5OQdRrvMYo+bm5ClpUFfyXCYl9ak=
+modernc.org/libc v1.14.7/go.mod h1:f8xfWXW8LW41qb4X5+huVQo5dcfPlq7Cbny2TDheMv0=
+modernc.org/libc v1.14.8/go.mod h1:9+JCLb1MWSY23smyOpIPbd5ED+rSS/ieiDWUpdyO3mo=
+modernc.org/libc v1.14.10/go.mod h1:y1MtIWhwpJFpLYm6grAThtuXJKEsY6xkdZmXbRngIdo=
+modernc.org/libc v1.14.11/go.mod h1:l5/Mz/GrZwOqzwRHA3abgSCnSeJzzTl+Ify0bAwKbAw=
+modernc.org/libc v1.14.12/go.mod h1:fJdoe23MHu2ruPQkFPPqCpToDi5cckzsbmkI6Ez0LqQ=
+modernc.org/libc v1.15.0/go.mod h1:H1OKCu+NYa9+uQG8WsP7DndMBP61I4PWH8ivWhbdoWQ=
+modernc.org/libc v1.15.1 h1:q4wjNNEdw9eceGHEUxklZyPVTkOPHjywI7qKQBj1f/A=
+modernc.org/libc v1.15.1/go.mod h1:CoZ2riUhSNTAP4bADwpxkLCyJK9SbbMvle0YRzkRT/I=
 modernc.org/mathutil v1.1.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 modernc.org/mathutil v1.2.2/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 modernc.org/mathutil v1.4.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
 modernc.org/mathutil v1.4.1/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
 modernc.org/memory v1.0.4/go.mod h1:nV2OApxradM3/OVbs2/0OsP6nPfakXpi50C7dcoHXlc=
-modernc.org/memory v1.0.5 h1:XRch8trV7GgvTec2i7jc33YlUI0RKVDBvZ5eZ5m8y14=
 modernc.org/memory v1.0.5/go.mod h1:B7OYswTRnfGg+4tDH1t1OeUNnsy2viGTdME4tzd+IjM=
+modernc.org/memory v1.0.6/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
+modernc.org/memory v1.0.7 h1:UE3cxTRFa5tfUibAV7Jqq8P7zRY0OlJg+yWVIIaluEE=
+modernc.org/memory v1.0.7/go.mod h1:/0wo5ibyrQiaoUoH7f9D8dnglAmILJ5/cxZlRECf+Nw=
 modernc.org/opt v0.1.1 h1:/0RX92k9vwVeDXj+Xn23DKp2VJubL7k8qNffND6qn3A=
 modernc.org/opt v0.1.1/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
-modernc.org/sqlite v1.14.2 h1:ohsW2+e+Qe2To1W6GNezzKGwjXwSax6R+CrhRxVaFbE=
 modernc.org/sqlite v1.14.2/go.mod h1:yqfn85u8wVOE6ub5UT8VI9JjhrwBUUCNyTACN0h6Sx8=
+modernc.org/sqlite v1.16.0 h1:DdvOGaWN0y+X7t2L7RUD63gcwbVjYZjcBZnA68g44EI=
+modernc.org/sqlite v1.16.0/go.mod h1:Jwe13ItpESZ+78K5WS6+AjXsUg+JvirsjN3iIDO4C8k=
 modernc.org/strutil v1.1.1 h1:xv+J1BXY3Opl2ALrBwyfEikFAj8pmqcpnfmuwUwcozs=
 modernc.org/strutil v1.1.1/go.mod h1:DE+MQQ/hjKBZS2zNInV5hhcipt5rLPWkmpbGeW5mmdw=
 modernc.org/tcl v1.8.13/go.mod h1:V+q/Ef0IJaNUSECieLU4o+8IScapxnMyFV6i/7uQlAY=
+modernc.org/tcl v1.11.2/go.mod h1:BRzgpajcGdS2qTxniOx9c/dcxjlbA7p12eJNmiriQYo=
 modernc.org/token v1.0.0 h1:a0jaWiNMDhDUtqOj09wvjWWAqd3q7WpBulmL9H2egsk=
 modernc.org/token v1.0.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
 modernc.org/z v1.2.19/go.mod h1:+ZpP0pc4zz97eukOzW3xagV/lS82IpPN9NGG5pNF9vY=
+modernc.org/z v1.3.2/go.mod h1:PEU2oK2OEA1CfzDTd+8E908qEXhC9s0MfyKp5LZsd+k=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
 rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=

+ 10 - 3
initialize/router.go

@@ -3,6 +3,9 @@ package initialize
 import (
 	"mtp20access/global"
 	"mtp20access/middleware"
+	"mtp20access/router"
+
+	_ "mtp20access/docs"
 
 	"github.com/gin-gonic/gin"
 	ginSwagger "github.com/swaggo/gin-swagger"
@@ -24,10 +27,14 @@ func Routers() *gin.Engine {
 	}
 
 	// 非鉴权组
-	// PublicGroup := Router.Group("")
+	publicGroup := Router.Group("")
+	{
+		router.InitAccountPublicRouter(publicGroup)
+	}
+
 	// 鉴权组
-	PrivateGroup := Router.Group("")
-	PrivateGroup.Use(middleware.JWTAuth())
+	privateGroup := Router.Group("")
+	privateGroup.Use(middleware.JWTAuth())
 	{
 
 	}

+ 13 - 2
main.go

@@ -8,6 +8,13 @@ import (
 	"go.uber.org/zap"
 )
 
+// @title Swagger Example API
+// @version 0.0.1
+// @description 新接入服务
+// @securityDefinitions.apikey ApiKeyAuth
+// @in header
+// @name x-token
+// @BasePath /
 func main() {
 
 	// 初始化Viper
@@ -17,6 +24,10 @@ func main() {
 	global.M2A_LOG = core.Zap()
 	zap.ReplaceGlobals(global.M2A_LOG)
 
+	// 初始化redis服务
+	initialize.Redis()
+	// 从redis加载jwt数据
+
 	// xorm连接数据库
 	global.M2A_DB = initialize.XormOracle()
 	if global.M2A_DB != nil {
@@ -34,7 +45,7 @@ func main() {
 		return
 	}
 
-	// 启动服务
-	core.RunServer()
+	// 启动Http API 服务
+	core.RunApiServer()
 	defer global.M2A_REDIS.Close()
 }

+ 43 - 56
middleware/jwt.go

@@ -1,72 +1,59 @@
 package middleware
 
 import (
-	"mtp20access/global"
-	"mtp20access/model/common/response"
-	"mtp20access/utils"
-	"strconv"
-	"time"
-
 	"github.com/gin-gonic/gin"
-	"github.com/golang-jwt/jwt/v4"
-	"go.uber.org/zap"
 )
 
 // FIXME - Token校验机制不适用,应重新设计
 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
-		}
-
-		j := utils.NewJWT()
-		if j.IsBlacklist(token) {
-			response.FailWithDetailed(gin.H{"reload": true}, "您的帐户异地登陆或令牌失效", c)
-			c.Abort()
-			return
-		}
-		// parseToken 解析token包含的信息
-		claims, err := j.ParseToken(token)
-		if err != nil {
-			if err == utils.ErrTokenExpired {
-				response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
-				c.Abort()
-				return
-			}
-			response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
-			c.Abort()
-			return
-		}
+		// token := c.Request.Header.Get("x-token")
+		// if token == "" {
+		// 	response.FailWithDetailed(gin.H{"reload": true}, "未登录或非法访问", c)
+		// 	c.Abort()
+		// 	return
+		// }
 
-		// 已登录用户被管理员禁用 需要使该用户的jwt失效 此处比较消耗性能 如果需要 请自行打开
-		// 用户被删除的逻辑 需要优化 此处比较消耗性能 如果需要 请自行打开
+		// j := utils.NewJWT()
+		// // parseToken 解析token包含的信息
+		// claims, err := j.ParseToken(token)
+		// if err != nil {
+		// 	if err == utils.ErrTokenExpired {
+		// 		response.FailWithDetailed(gin.H{"reload": true}, "授权已过期", c)
+		// 		c.Abort()
+		// 		return
+		// 	}
+		// 	response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
+		// 	c.Abort()
+		// 	return
+		// }
+		// // 从Redis获取登录信息
+		// loginMap, err := j.GetRedisLogin(claims.LoginID)
+		// if err != nil {
+		// 	response.FailWithDetailed(gin.H{"reload": true}, "您的帐户异地登陆或令牌失效", c)
+		// 	c.Abort()
+		// 	return
+		// }
 
-		//if user, err := userService.FindUserByUuid(claims.UUID.String()); err != nil || user.Enable == 2 {
-		//	_ = jwtService.JsonInBlacklist(system.JwtBlacklist{Jwt: token})
-		//	response.FailWithDetailed(gin.H{"reload": true}, err.Error(), c)
-		//	c.Abort()
-		//}
-		if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
-			claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Duration(global.M2A_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.Unix(), 10))
+		// // 判断是否需要自动续约
+		// if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
+		// 	claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Duration(global.M2A_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.Unix(), 10))
 
-			RedisJwtToken, err := j.GetRedisJWT(newClaims.LoginID)
-			if err != nil {
-				global.M2A_LOG.Error("get redis jwt failed", zap.Error(err))
-			} else { // 当之前的取成功时才进行拉黑操作
-				_ = j.JsonInBlacklist(RedisJwtToken)
-			}
-			// 无论如何都要记录当前的活跃状态
-			_ = j.SetRedisJWT(newToken, newClaims.LoginID)
-		}
-		c.Set("claims", claims)
+		// 	// RedisJwtToken, err := j.GetRedisJWT(newClaims.LoginID)
+		// 	if err != nil {
+		// 		global.M2A_LOG.Error("get redis jwt failed", zap.Error(err))
+		// 	} else { // 当之前的取成功时才进行拉黑操作
+		// 		_ = j.JsonInBlacklist(RedisJwtToken)
+		// 	}
+		// 	// 无论如何都要记录当前的活跃状态
+		// 	_ = j.SetRedisJWT(newToken, newClaims.LoginID)
+		// }
+		// c.Set("claims", claims)
 		c.Next()
 	}
 }

+ 9 - 1
model/account/account.go

@@ -1,6 +1,9 @@
 package account
 
-import "time"
+import (
+	"mtp20access/global"
+	"time"
+)
 
 // Loginaccount 登录账户表
 type Loginaccount struct {
@@ -36,3 +39,8 @@ type Loginaccount struct {
 func (r *Loginaccount) TableName() string {
 	return "LOGINACCOUNT"
 }
+
+// Get 根据条件获取登录账户表信息
+func (r *Loginaccount) Get() (has bool, err error) {
+	return global.M2A_DB.Get(r)
+}

+ 3 - 3
model/account/request/login.go

@@ -2,7 +2,7 @@ package request
 
 // LoginReq 登录请求入参
 type LoginReq struct {
-	UserName string `from:"userName" binding:"required"` // 用户名,可以是LoginID/LoginCode/手机号码
-	Password string `from:"password" binding:"required"` // 密码
-	Captcha  string `from:"captcha"`                     // 验证码
+	UserName   string `from:"userName" binding:"required"`   // 用户名,可以是LoginID/LoginCode/手机号码
+	Password   string `from:"password" binding:"required"`   // 密码
+	ClientType int    `from:"clientType" binding:"required"` // 客户端类型,2-PC交易端 3-手机客户端_安卓 4-网页客户端 5-微信客户端 6-手机客户端_苹果
 }

+ 7 - 0
model/account/response/login.go

@@ -1 +1,8 @@
 package response
+
+type LoginRsp struct {
+	LoginID   int    `json:"loginId"`   // 登录ID
+	UserID    int    `json:"userId"`    // 用户ID
+	Token     string `json:"token"`     // 新服务Token
+	ExpiresAt int64  `json:"expiresAt"` // 过期时间
+}

+ 3 - 2
model/common/request/jwt.go

@@ -11,6 +11,7 @@ type CustomClaims struct {
 }
 
 type BaseClaims struct {
-	LoginID   string
-	SessionID uint
+	LoginID   int
+	SessionID int
+	// FIXME: 要不要这里存UserID?
 }

+ 14 - 0
router/account.go

@@ -0,0 +1,14 @@
+package router
+
+import (
+	"mtp20access/api/v1/account"
+
+	"github.com/gin-gonic/gin"
+)
+
+func InitAccountPublicRouter(r *gin.RouterGroup) {
+	accountR := r.Group("Account").Use()
+	{
+		accountR.POST("Login", account.Login)
+	}
+}

+ 143 - 0
service/account/login.go

@@ -1 +1,144 @@
 package account
+
+import (
+	"encoding/hex"
+	"errors"
+	"mtp20access/global"
+	accountModel "mtp20access/model/account"
+	"mtp20access/model/account/request"
+	jwtRequest "mtp20access/model/common/request"
+	"sync"
+
+	"mtp20access/utils"
+	"strconv"
+
+	"go.uber.org/zap"
+)
+
+var (
+	mtx          sync.RWMutex
+	curSessionID int = 90000 // 本服务SessionID从90000开始,以避免与旧登录服务重叠
+)
+
+// Login 用户登录
+func Login(req request.LoginReq, addr string) (loginaccount *accountModel.Loginaccount, token string, expiresAt int64, err error) {
+	// 分别尝试用LoginID、LoginCode和手机号码进行登录
+	loginaccount, err = getLoginAccount(req.UserName, req.Password)
+	if err != nil {
+		return
+	}
+
+	// 判断用户状态
+	if loginaccount.LOGINSTATUS == 2 {
+		err = errors.New("账户已冻结")
+		return
+	}
+	if loginaccount.LOGINSTATUS == 3 {
+		err = errors.New("账户已注销")
+		return
+	}
+
+	// 生成Token,并写入Redis
+	if token, expiresAt, err = buildRedisLoginInfo(*loginaccount, addr, req.ClientType); err != nil {
+		return
+	}
+
+	return
+}
+
+// getLoginAccount 分别尝试用LoginID、LoginCode和手机号码进行登录
+func getLoginAccount(userName string, password string) (loginaccount *accountModel.Loginaccount, err error) {
+	// LoginID
+	if loginID, _ := strconv.Atoi(userName); loginID != 0 {
+		loginaccount = &accountModel.Loginaccount{
+			LOGINID:  int64(loginID),
+			PASSWORD: password,
+		}
+		if has, _ := global.M2A_DB.Get(loginaccount); has {
+			return
+		}
+	}
+	// LoginCode
+	loginaccount = &accountModel.Loginaccount{
+		LOGINCODE: userName,
+		PASSWORD:  password,
+	}
+	if has, _ := global.M2A_DB.Get(loginaccount); has {
+		return
+	}
+	// 手机号码,需要AES加密
+	key, _ := hex.DecodeString(utils.AESSecretKey)
+	if mobileEncrypted, _ := utils.AESEncrypt([]byte(userName), key); mobileEncrypted != nil {
+		loginaccount = &accountModel.Loginaccount{
+			MOBILE:   string(mobileEncrypted),
+			PASSWORD: password,
+		}
+		if has, _ := global.M2A_DB.Get(loginaccount); has {
+			return
+		}
+	}
+
+	err = errors.New("错误的用户名或密码")
+	return
+}
+
+// newSessionID 获取
+func newSessionID() int {
+	mtx.Lock()
+	curSessionID += 1
+	mtx.Unlock()
+
+	return curSessionID
+}
+
+// buildRedisLoginInfo 生成Token,并写入Redis
+func buildRedisLoginInfo(loginaccount accountModel.Loginaccount, addr string, group int) (token string, expiresAt int64, err error) {
+	// 生成SessionID
+	sessionID := newSessionID()
+
+	// 生成本服务Token
+	j := &utils.JWT{SigningKey: []byte(global.M2A_CONFIG.JWT.SigningKey)} // 唯一签名
+	claims := j.CreateClaims(jwtRequest.BaseClaims{
+		LoginID:   int(loginaccount.LOGINID),
+		SessionID: sessionID,
+	})
+	token, err = j.CreateToken(claims)
+	if err != nil {
+		global.M2A_LOG.Error("生成本服token失败", zap.Error(err))
+		return
+	}
+	expiresAt = claims.RegisteredClaims.ExpiresAt.Unix()
+	loginLogin := global.LoginRedis{
+		LoginID:   strconv.Itoa(int(loginaccount.LOGINID)),
+		UserID:    strconv.Itoa(int(loginaccount.USERID)),
+		SessionID: strconv.Itoa(sessionID),
+		Token:     token,
+		Group:     strconv.Itoa(group),
+		Addr:      addr,
+	}
+	loginMap, err := loginLogin.ToMap()
+	// loginMap := map[string]interface{}{
+	// 	"LoginID":   strconv.Itoa(int(loginaccount.LOGINID)),
+	// 	"UserID":    strconv.Itoa(int(loginaccount.USERID)),
+	// 	"SessionID": strconv.Itoa(sessionID),
+	// 	"Token":     token,
+	// 	"Group":     strconv.Itoa(group),
+	// 	"Addr":      addr,
+	// }
+	if err != nil {
+		global.M2A_LOG.Error("生成登录信息MAP失败", zap.Error(err))
+		return
+	}
+	if err = j.SetRedisLogin(int(loginaccount.LOGINID), group, loginMap); err != nil {
+		global.M2A_LOG.Error("Token写入Redis失败", zap.Error(err))
+		return
+	}
+	// 生成旧登录服务Token
+	// if err = j.SetOriRedisToken(int(loginaccount.LOGINID), group); err != nil {
+	// 	// FIXME: 这里有类事务的回滚问题
+	// 	global.M2A_LOG.Error("生成旧登录服务Token失败", zap.Error(err))
+	// 	return
+	// }
+
+	return
+}

+ 115 - 0
utils/encrypt.go

@@ -0,0 +1,115 @@
+package utils
+
+import (
+	"bytes"
+	"crypto/aes"
+	"crypto/cipher"
+	"errors"
+)
+
+// AESSecretKey AES密钥
+const AESSecretKey = "0d299ce2d4105282f7471074cb0f9f9d"
+
+// AESEncrypt AES加密
+func AESEncrypt(plaintext, key []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, errors.New("错误的数据或密钥")
+	}
+	ecb := NewECBEncrypter(block)
+	content := plaintext[:]
+	content = PKCS5Padding(content, block.BlockSize())
+	crypted := make([]byte, len(content))
+	ecb.CryptBlocks(crypted, content)
+
+	return crypted, nil
+}
+
+// AESDecrypt AES
+func AESDecrypt(crypted, key []byte) ([]byte, error) {
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, errors.New("错误的数据或密钥")
+	}
+	blockMode := NewECBDecrypter(block)
+	origData := make([]byte, len(crypted))
+	blockMode.CryptBlocks(origData, crypted)
+	origData = PKCS5UnPadding(origData)
+
+	return origData, nil
+}
+
+// PKCS5Padding ECB PKCS5Padding
+func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
+	padding := blockSize - len(ciphertext)%blockSize
+	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
+	return append(ciphertext, padtext...)
+}
+
+// PKCS5UnPadding ECB PKCS5Unpadding
+func PKCS5UnPadding(origData []byte) []byte {
+	length := len(origData)
+	// 去掉最后一个字节 unpadding 次
+	unpadding := int(origData[length-1])
+	// 防止数据越界崩溃
+	if length-unpadding >= len(origData) {
+		return origData
+	}
+	return origData[:(length - unpadding)]
+}
+
+type ecb struct {
+	b         cipher.Block
+	blockSize int
+}
+
+func newECB(b cipher.Block) *ecb {
+	return &ecb{
+		b:         b,
+		blockSize: b.BlockSize(),
+	}
+}
+
+type ecbEncrypter ecb
+
+// NewECBEncrypter returns a BlockMode which encrypts in electronic code book
+// mode, using the given Block.
+func NewECBEncrypter(b cipher.Block) cipher.BlockMode {
+	return (*ecbEncrypter)(newECB(b))
+}
+func (x *ecbEncrypter) BlockSize() int { return x.blockSize }
+func (x *ecbEncrypter) CryptBlocks(dst, src []byte) {
+	if len(src)%x.blockSize != 0 {
+		panic("crypto/cipher: input not full blocks")
+	}
+	if len(dst) < len(src) {
+		panic("crypto/cipher: output smaller than input")
+	}
+	for len(src) > 0 {
+		x.b.Encrypt(dst, src[:x.blockSize])
+		src = src[x.blockSize:]
+		dst = dst[x.blockSize:]
+	}
+}
+
+type ecbDecrypter ecb
+
+// NewECBDecrypter returns a BlockMode which decrypts in electronic code book
+// mode, using the given Block.
+func NewECBDecrypter(b cipher.Block) cipher.BlockMode {
+	return (*ecbDecrypter)(newECB(b))
+}
+func (x *ecbDecrypter) BlockSize() int { return x.blockSize }
+func (x *ecbDecrypter) CryptBlocks(dst, src []byte) {
+	if len(src)%x.blockSize != 0 {
+		panic("crypto/cipher: input not full blocks")
+	}
+	if len(dst) < len(src) {
+		panic("crypto/cipher: output smaller than input")
+	}
+	for len(src) > 0 {
+		x.b.Decrypt(dst, src[:x.blockSize])
+		src = src[x.blockSize:]
+		dst = dst[x.blockSize:]
+	}
+}

+ 34 - 0
utils/gin.go

@@ -0,0 +1,34 @@
+package utils
+
+import (
+	"mtp20access/model/common/response"
+
+	"github.com/gin-gonic/gin"
+)
+
+type GinUtils struct {
+	C   *gin.Context
+	err error
+}
+
+// BindFormReq 绑定Form入参的方法
+func (r *GinUtils) BindFormReq(req interface{}) {
+	if r.err != nil {
+		return
+	}
+	if r.err = r.C.ShouldBind(&req); r.err != nil {
+		response.FailWithMessage("入参不正确", r.C)
+		return
+	}
+}
+
+// BindJsonReq 绑定Body入参的方法
+func (r *GinUtils) BindJsonReq(req interface{}) {
+	if r.err != nil {
+		return
+	}
+	if r.err = r.C.ShouldBindJSON(&req); r.err != nil {
+		response.FailWithMessage("入参不正确", r.C)
+		return
+	}
+}

+ 22 - 31
utils/jwt.go

@@ -86,42 +86,33 @@ func (j *JWT) ParseToken(tokenString string) (*request.CustomClaims, error) {
 	}
 }
 
-//@function: JsonInBlacklist
-//@description: 拉黑jwt
-//@param: jwt string
-//@return: err error
-func (j *JWT) JsonInBlacklist(jwt string) (err error) {
-	global.BlackCache.SetDefault(jwt, struct{}{})
-	return
-}
-
-//@function: IsBlacklist
-//@description: 判断JWT是否在黑名单内部
-//@param: jwt string
-//@return: bool
-func (j *JWT) IsBlacklist(jwt string) bool {
-	_, ok := global.BlackCache.Get(jwt)
-	return ok
-}
-
-//@function: GetRedisJWT
-//@description: 从redis取jwt
-//@param: userName string
+//@function: GetRedis
+//@description: 从redis取目标用户登录信息
+//@param: userName int
 //@return: redisJWT string, err error
-func (j *JWT) GetRedisJWT(loginID string) (redisJWT string, err error) {
-	key := fmt.Sprintf("m2a:jwt:%s", loginID)
-	redisJWT, err = global.M2A_REDIS.Get(context.Background(), key).Result()
-	return redisJWT, err
+func (j *JWT) GetRedisLogin(loginID int) (values map[string]string, err error) {
+	key := fmt.Sprintf("m2a:login:%d", loginID)
+	values, err = global.M2A_REDIS.HGetAll(context.Background(), key).Result()
+	return
 }
 
 //@function: SetRedisJWT
 //@description: jwt存入redis并设置过期时间
-//@param: jwt string, userName string
+//@param: loginID string, jwt string
 //@return: err error
-func (j *JWT) SetRedisJWT(jwt string, loginID string) (err error) {
-	key := fmt.Sprintf("m2a:jwt:%s", loginID)
+func (j *JWT) SetRedisLogin(loginID int, group int, values map[string]interface{}) (err error) {
+	key := fmt.Sprintf("m2a:login:%d:%d", loginID, group)
 	// 此处过期时间等于jwt过期时间
-	timer := time.Duration(global.M2A_CONFIG.JWT.ExpiresTime) * time.Second
-	err = global.M2A_REDIS.Set(context.Background(), key, jwt, timer).Err()
-	return err
+	// timer := time.Duration(global.M2A_CONFIG.JWT.ExpiresTime) * time.Second
+	err = global.M2A_REDIS.HMSet(context.Background(), key, values).Err()
+	return
+}
+
+// SetOriRedisToken 设置原登录服务Redis Token
+func (j *JWT) SetOriRedisToken(loginID int, group int) (err error) {
+	key := fmt.Sprintf("monitor:online_loginid:%d:%d", loginID, group)
+	// 生成原登录服务Token
+	token := fmt.Sprintf("%d_%d_%d", loginID, time.Now().Unix(), group)
+	err = global.M2A_REDIS.HSet(context.Background(), key, "Token", token).Err()
+	return
 }