This commit is contained in:
新亮
2021-01-01 10:51:55 +08:00
parent aa5dec6eeb
commit 458ebe10c6
51 changed files with 1018 additions and 2848 deletions
+33 -42
View File
@@ -5,38 +5,41 @@
██║ ██║██║ ██║╚════╝██║ ██║██║██║╚██╗██║╚════╝██╔══██║██╔═══╝ ██║
╚██████╔╝╚██████╔╝ ╚██████╔╝██║██║ ╚████║ ██║ ██║██║ ██║
╚═════╝ ╚═════╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═╝
* [register cors]
* [register rate]
* [register panic notify]
* [register pprof]
* [register swagger]
* [register prometheus]
```
## go-gin-api
基于 [Gin](https://github.com/gin-gonic/gin) 进行模块化设计的 API 框架,封装了常用的功能,使用简单,致力于进行快速的业务研发。
基于 [Gin](https://github.com/gin-gonic/gin) 进行模块化设计的 API 框架,封装了常用的功能,使用简单,致力于进行快速的业务研发,同时增加了更多限制,以约束项目组开发成员、规避混乱无序和自由随意
供参考学习,线上使用请谨慎!
**查看 Jaeger 链路追踪代码,请查看 [v1.0版](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0),文档 [jaeger.md](https://github.com/xinliangnote/go-gin-api/blob/master/docs/jaeger.md) 点这里**
持续更新...
## Features
- [x] 使用 go modules 初始化项目
- [x] 安装 Gin 框架
- [x] 性能分析工具(pprof
- [x] 支持优雅地重启或停止
- [x] 规划项目目录
- [x] 参数验证(validator.v9
- [x] 模型绑定和验
- [x] 自定义验证器
- [x] 路由中间件
- [x] 签名验证
- [x] MD5 组合加密
- [x] AES 对称加密
- [x] RSA 非对称加密
- [x] 日志记录
- [x] 异常捕获
- [x] 链路追踪(Jaeger
- [x] 限流
- [x] 自定义告警
- [x] 邮件(gomail
- [ ] 微信
- [ ] 短信
- [ ] 钉钉
- [x] 包管理工具 [Go Modules](https://github.com/golang/go/wiki/Modules)
- [x] [Gin](https://github.com/gin-gonic/gin) 支持优雅关闭
- [x] 配置文件解析库 [Viper](https://github.com/spf13/viper)
- [x] 文档使用 [Swagger](https://swagger.io/) 生成
- [x] 性能分析使用 [pprof](github.com/gin-contrib/pprof)
- [x] 集成
- [x] [JWT](https://jwt.io/) 身份认
- [x] [zap](go.uber.org/zap) 日志记录
- [x] [rate](golang.org/x/time/rate) 限流
- [x] 异常捕获并邮件告警
- [x] 每个请求具备链路ID
- [x] 统一定义错误码
- [x] 支持 FAT、UAT、PRO 环境
- [x] MD5、AES 对称加密、RSA 非对称加密
- [ ] 存储
- [ ] MySQL
- [ ] Redis
@@ -64,27 +67,15 @@ cd go-gin-api
go run main.go
```
#### HTTP Demo
#### swagger
```go
http://127.0.0.1:9999/swagger/index.html
// 生成文档
$ go get -u github.com/swaggo/swag/cmd/swag
$ swag init
```
curl -X POST http://127.0.0.1:9999/demo/user
```
#### Jaeger Demo
访问:
```
http://127.0.0.1:9999/jaeger_test
```
服务端测试代码:
- [https://github.com/xinliangnote/go-jaeger-demo](https://github.com/xinliangnote/go-jaeger-demo)
![](https://github.com/xinliangnote/Go/blob/master/03-go-gin-api%20%5B文档%5D/images/jaeger_demo_2.png)
![](https://github.com/xinliangnote/Go/blob/master/03-go-gin-api%20%5B文档%5D/images/jaeger_demo_3.png)
#### pprof
+9 -3
View File
@@ -1,6 +1,12 @@
[db]
host = '127.0.0.1'
port = 3306
[mail]
host = 'smtp.163.com'
port = 465
user = ""
pass = ""
to = ""
[jwt]
secret = 'i1ydX9RtHyuJTrw7frcu'
[aes]
key = 'IgkibX71IEf382PT'
+67 -2
View File
@@ -40,10 +40,17 @@ var doc = `{
"parameters": [
{
"type": "string",
"description": "用户名",
"description": "用户名(Tom)",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "签名",
"name": "Authorization",
"in": "header",
"required": true
}
],
"responses": {
@@ -68,6 +75,64 @@ var doc = `{
}
}
}
},
"/user/login": {
"post": {
"description": "登录获取 Authorization 码",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Demo"
],
"summary": "登录获取 Authorization 码",
"parameters": [
{
"description": "请求信息",
"name": "loginRequest",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/demo.loginRequest"
}
}
],
"responses": {
"200": {
"description": "签名信息",
"schema": {
"$ref": "#/definitions/demo.loginResponse"
}
}
}
}
}
},
"definitions": {
"demo.loginRequest": {
"type": "object",
"properties": {
"user_id": {
"description": "用户ID\u003e0",
"type": "integer"
},
"user_name": {
"description": "用户名",
"type": "string"
}
}
},
"demo.loginResponse": {
"type": "object",
"properties": {
"authorization": {
"description": "签名",
"type": "string"
}
}
}
}
}`
@@ -84,7 +149,7 @@ type swaggerInfo struct {
// SwaggerInfo holds exported Swagger Info so clients can modify it
var SwaggerInfo = swaggerInfo{
Version: "",
Host: "localhost:9999",
Host: "127.0.0.1:9999",
BasePath: "",
Schemes: []string{},
Title: "go-gin-api docs api",
+20
View File
@@ -0,0 +1,20 @@
## Jaeger Demo 代码
- 代码在 [v1.0](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0) 版本。
#### Jaeger Demo 文档
访问:
```
http://127.0.0.1:9999/jaeger_test
```
服务端测试代码:
- [https://github.com/xinliangnote/go-jaeger-demo](https://github.com/xinliangnote/go-jaeger-demo)
![](https://github.com/xinliangnote/Go/blob/master/03-go-gin-api%20%5B文档%5D/images/jaeger_demo_2.png)
![](https://github.com/xinliangnote/Go/blob/master/03-go-gin-api%20%5B文档%5D/images/jaeger_demo_3.png)
+67 -2
View File
@@ -4,7 +4,7 @@
"title": "go-gin-api docs api",
"contact": {}
},
"host": "localhost:9999",
"host": "127.0.0.1:9999",
"paths": {
"/demo/user/{name}": {
"get": {
@@ -22,10 +22,17 @@
"parameters": [
{
"type": "string",
"description": "用户名",
"description": "用户名(Tom)",
"name": "name",
"in": "path",
"required": true
},
{
"type": "string",
"description": "签名",
"name": "Authorization",
"in": "header",
"required": true
}
],
"responses": {
@@ -50,6 +57,64 @@
}
}
}
},
"/user/login": {
"post": {
"description": "登录获取 Authorization 码",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Demo"
],
"summary": "登录获取 Authorization 码",
"parameters": [
{
"description": "请求信息",
"name": "loginRequest",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/demo.loginRequest"
}
}
],
"responses": {
"200": {
"description": "签名信息",
"schema": {
"$ref": "#/definitions/demo.loginResponse"
}
}
}
}
}
},
"definitions": {
"demo.loginRequest": {
"type": "object",
"properties": {
"user_id": {
"description": "用户ID\u003e0",
"type": "integer"
},
"user_name": {
"description": "用户名",
"type": "string"
}
}
},
"demo.loginResponse": {
"type": "object",
"properties": {
"authorization": {
"description": "签名",
"type": "string"
}
}
}
}
}
+45 -2
View File
@@ -1,4 +1,20 @@
host: localhost:9999
definitions:
demo.loginRequest:
properties:
user_id:
description: 用户ID>0
type: integer
user_name:
description: 用户名
type: string
type: object
demo.loginResponse:
properties:
authorization:
description: 签名
type: string
type: object
host: 127.0.0.1:9999
info:
contact: {}
title: go-gin-api docs api
@@ -9,11 +25,16 @@ paths:
- application/json
description: 获取用户信息
parameters:
- description: 用户名
- description: 用户名(Tom)
in: path
name: name
required: true
type: string
- description: 签名
in: header
name: Authorization
required: true
type: string
produces:
- application/json
responses:
@@ -33,4 +54,26 @@ paths:
summary: 获取用户信息
tags:
- Demo
/user/login:
post:
consumes:
- application/json
description: 登录获取 Authorization 码
parameters:
- description: 请求信息
in: body
name: loginRequest
required: true
schema:
$ref: '#/definitions/demo.loginRequest'
produces:
- application/json
responses:
"200":
description: 签名信息
schema:
$ref: '#/definitions/demo.loginResponse'
summary: 登录获取 Authorization 码
tags:
- Demo
swagger: "2.0"
+4 -11
View File
@@ -4,13 +4,10 @@ go 1.12
require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/gin-contrib/pprof v1.2.1
github.com/gin-gonic/gin v1.4.0
github.com/gin-gonic/gin v1.6.3
github.com/go-openapi/spec v0.20.0 // indirect
github.com/golang/protobuf v1.3.2
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0
github.com/opentracing/opentracing-go v1.1.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v0.9.3
github.com/rs/cors v1.7.0
@@ -18,19 +15,15 @@ require (
github.com/swaggo/gin-swagger v1.3.0
github.com/swaggo/swag v1.7.0
github.com/tidwall/gjson v1.6.6
github.com/uber-go/atomic v1.4.0 // indirect
github.com/uber/jaeger-client-go v2.18.1+incompatible
github.com/uber/jaeger-lib v2.1.1+incompatible // indirect
github.com/xinliangnote/go-util v0.0.0-20191115235314-e7d1576d41db
go.uber.org/multierr v1.5.0
go.uber.org/zap v1.16.0
golang.org/x/mod v0.4.0 // indirect
golang.org/x/net v0.0.0-20201224014010-6772e930b67b // indirect
golang.org/x/sys v0.0.0-20201223074533-0d417f636930 // indirect
golang.org/x/time v0.0.0-20190921001708-c4c64cad1fd0
golang.org/x/tools v0.0.0-20201226215659-b1c90890d22a // indirect
google.golang.org/grpc v1.23.1
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df // indirect
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
gopkg.in/natefinch/lumberjack.v2 v2.0.0
)
+26 -53
View File
@@ -17,7 +17,6 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
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 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=
@@ -26,7 +25,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY=
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=
@@ -38,8 +36,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd h1:qMd81Ts1T2OTKmB4acZcyKaMtRnY5Y44NuXGX2GFJ1w=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
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=
@@ -50,55 +46,58 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
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/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-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
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/pprof v1.2.1 h1:p6mY/FsE3tDx2+Wp3ksrMe1QL5egQ7p+lsZ7WbQLT1U=
github.com/gin-contrib/pprof v1.2.1/go.mod h1:u2l4P4YNkLXYz+xBbrl7Pxu1Btng6VCD7j3O3pUPP2w=
github.com/gin-contrib/sse v0.0.0-20170109093832-22d885f9ecc7/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3 h1:t8FVkw33L+wilf2QiWkw0UV77qRpcH/JHPKGpKa2E8g=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.3.0/go.mod h1:7cKuhb5qV2ggCFctp2fJQ+ErvciLZrIeoOSOm6mUr7Y=
github.com/gin-gonic/gin v1.4.0 h1:3tMoCCfM7ppqsR0ptz/wi1impNpT7/9wQtMZ8lr1mCQ=
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
github.com/gin-gonic/gin v1.6.3 h1:ahKqKTFpO5KTPHxWZjEdPScmYaGtLo8Y4DMHoEsnp14=
github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M=
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-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/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/jsonreference v0.19.5 h1:1WJP/wi4OjB4iV8KVbH73rQaoialJrqv8gitZLxGLtM=
github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
github.com/go-openapi/spec v0.19.0 h1:A4SZ6IWh3lnjH0rG0Z5lkxazMGBECtrZcbyYQi+64k4=
github.com/go-openapi/spec v0.19.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
github.com/go-openapi/spec v0.19.14/go.mod h1:gwrgJS15eCUgjLpMjBJmbZezCsw88LmgeEip0M63doA=
github.com/go-openapi/spec v0.20.0 h1:HGLc8AJ7ynOxwv0Lq4TsnwLsWMawHAYiJIFzbcML86I=
github.com/go-openapi/spec v0.20.0/go.mod h1:+81FIL1JwC5P3/Iuuozq3pPE9dXdIEGxFutcFKaVbmU=
github.com/go-openapi/swag v0.17.0 h1:iqrgMg7Q7SvtbWLlltPrkMs0UBJI6oTSs79JFRUi880=
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.12 h1:Bc0bnY2c3AoF7Gc+IMIAQQsD8fLHjHpc19wXvYuayQI=
github.com/go-openapi/swag v0.19.12/go.mod h1:eFdyEBkTdoAf/9RXBvj4cr1nH7GD8Kzo5HTt47gr72M=
github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
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/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
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=
@@ -106,12 +105,14 @@ github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb
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 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
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/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=
@@ -122,8 +123,6 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGa
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-middleware v1.1.0 h1:THDBEeQ9xZ8JEaCLyLQqXMMdRqNr0QAUJTIkQAUtFjg=
github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE=
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=
@@ -151,8 +150,9 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
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 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
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=
@@ -161,18 +161,17 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
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/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
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/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 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=
@@ -181,10 +180,10 @@ github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
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.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.8 h1:HLtExJ+uU2HOZ+wI0Tt5DtUDrx8yhUqDcp7fYERX4CE=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
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 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
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=
@@ -196,15 +195,15 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
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=
@@ -237,8 +236,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
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.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
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=
@@ -259,7 +256,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
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 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -268,7 +264,6 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
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=
@@ -279,26 +274,17 @@ github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JT
github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/uber-go/atomic v1.4.0 h1:yOuPqEq4ovnhEjpHmfFwsqBXDYbQeT6Nb0bwD6XnD5o=
github.com/uber-go/atomic v1.4.0/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/uber/jaeger-client-go v2.18.1+incompatible h1:bI1w1jRSWBswo6LhGdqA2SbFQX/TSVleZhMty4IJ78A=
github.com/uber/jaeger-client-go v2.18.1+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.1.1+incompatible h1:VY/6p2WopO09BPnw787RbaCIlfKbCRC/kq3p5D0F168=
github.com/uber/jaeger-lib v2.1.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
github.com/ugorji/go v1.1.4 h1:j4s+tAvLfL3bZyefP2SEWmhBzmuIlH/eqNuPdFPgngw=
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.1.13 h1:nB3O5kBSQGjEQAcfe1aLUYuxmXdFKmYgBZhY32rQb6Q=
github.com/ugorji/go v1.1.13/go.mod h1:jxau1n+/wyTGLQoCkjok9r5zFa/FxT6eI5HiHKQszjc=
github.com/ugorji/go/codec v0.0.0-20181022190402-e5e69e061d4f/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.1.13 h1:013LbFhocBoIqgHeIHKlV4JWYhqogATYWZhIcH0WHn4=
github.com/ugorji/go/codec v1.1.13/go.mod h1:oNVt3Dq+FO91WNQ/9JnHKQP2QJxTzoN7wCBFCq1OeuU=
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
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/xinliangnote/go-util v0.0.0-20191115235314-e7d1576d41db h1:yReVDmqk6oBUOcSRUofahYHJlIwr7wrJ8yx1Ymkz84E=
github.com/xinliangnote/go-util v0.0.0-20191115235314-e7d1576d41db/go.mod h1:3ZornrplU9tYj9ar9jchik1XPQ10ZbSXkbgxXR5PVaU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -358,7 +344,6 @@ golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn
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 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
@@ -385,13 +370,12 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/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-20201223074533-0d417f636930 h1:vRgIt+nup/B/BwIS0g2oC0haq0iqbV3ZA+u6+0TlNCo=
@@ -399,7 +383,6 @@ golang.org/x/sys v0.0.0-20201223074533-0d417f636930/go.mod h1:h1NjWce9XRLGQEsW7w
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 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
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 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc=
@@ -418,7 +401,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
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-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=
@@ -429,7 +411,6 @@ golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtn
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 h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8=
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-20201120155355-20be4ac4bd6e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -456,27 +437,20 @@ google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRn
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 h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
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.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk=
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gGKlE2+asNV9m7xrywl36YYNnBG5ZQ0r/BOOxqPpmk=
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
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 h1:lFB4DoMU6B626w8ny76MV7VX6W2VHct2GVOI3xgiMrQ=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
@@ -489,8 +463,8 @@ gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bl
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 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
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=
@@ -500,7 +474,6 @@ gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C
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=
-45
View File
@@ -1,45 +0,0 @@
package config
var (
ApiAuthConfig = map[string]map[string]string{
// 调用方
"DEMO": {
"md5": "IgkibX71IEf382PT",
"aes": "IgkibX71IEf382PT",
"rsa": "rsa/public.pem",
},
}
)
const (
AppName = "go-gin-api"
// 签名超时时间
AppSignExpiry = "120"
// RSA Private File
AppRsaPrivateFile = "rsa/private.pem"
// 日志文件
AppErrorLogName = "log/" + AppName + "-error.log"
AppGrpcLogName = "log/" + AppName + "-grpc.log"
// 系统告警邮箱信息
SystemEmailUser = "xinliangnote@163.com"
SystemEmailPass = "" //密码或授权码
SystemEmailHost = "smtp.163.com"
SystemEmailPort = 465
// 告警接收人
ErrorNotifyUser = "xinliangnote@163.com"
// 告警开关 1=开通 -1=关闭
ErrorNotifyOpen = -1
// Jaeger 配置信息
JaegerHostPort = "172.20.2.212:6831"
// Jaeger 配置开关 1=开通 -1=关闭
JaegerOpen = 1
)
+21 -16
View File
@@ -101,7 +101,8 @@ type response []struct {
// @Tags Demo
// @Accept json
// @Produce json
// @Param name path string true "用户名"
// @Param name path string true "用户名(Tom)"
// @Param Authorization header string true "签名"
// @Success 200 {object} response "用户信息"
// @Router /demo/user/{name} [get]
func (d *Demo) User() core.HandlerFunc {
@@ -121,6 +122,7 @@ func (d *Demo) User() core.HandlerFunc {
httpclient.WithTTL(time.Second*2),
httpclient.WithJournal(c.Journal()),
httpclient.WithLogger(c.Logger()),
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
)
if err1 != nil {
d.logger.Error("get [demo/get] err", zap.Error(err1))
@@ -132,22 +134,25 @@ func (d *Demo) User() core.HandlerFunc {
httpclient.WithTTL(time.Second*2),
httpclient.WithJournal(c.Journal()),
httpclient.WithLogger(c.Logger()),
httpclient.WithHeader("Authorization", c.GetHeader("Authorization")),
)
if err2 != nil {
d.logger.Error("post [demo/post] err", zap.Error(err2))
}
data := &response{
{
Name: jsonparse.Get(string(body1), "data.name").(string),
Job: jsonparse.Get(string(body1), "data.job").(string),
},
{
Name: jsonparse.Get(string(body2), "data.name").(string),
Job: jsonparse.Get(string(body2), "data.job").(string),
},
data := &response{}
if err1 == nil && err2 == nil {
data = &response{
{
Name: jsonparse.Get(string(body1), "data.name").(string),
Job: jsonparse.Get(string(body1), "data.job").(string),
},
{
Name: jsonparse.Get(string(body2), "data.name").(string),
Job: jsonparse.Get(string(body2), "data.job").(string),
},
}
}
c.SetPayload(errno.OK.WithData(data))
}
}
@@ -158,9 +163,9 @@ func (d *Demo) RsaTest() core.HandlerFunc {
encryptStr := "param_1=xxx&param_2=xxx&ak=xxx&ts=1111111111"
count := 500
cfg := configs.Get()
rsaPublic := rsa.NewPublic(cfg.Rsa.Public)
rsaPrivate := rsa.NewPrivate(cfg.Rsa.Private)
cfg := configs.Get().Rsa
rsaPublic := rsa.NewPublic(cfg.Public)
rsaPrivate := rsa.NewPrivate(cfg.Private)
for i := 0; i < count; i++ {
// 生成签名
@@ -187,8 +192,8 @@ func (d *Demo) AesTest() core.HandlerFunc {
encryptStr := "param_1=xxx&param_2=xxx&ak=xxx&ts=1111111111"
count := 1000000
cfg := configs.Get()
aes := aes.New(cfg.Aes.Key, cfg.Aes.Iv)
cfg := configs.Get().Aes
aes := aes.New(cfg.Key, cfg.Iv)
for i := 0; i < count; i++ {
// 生成签名
sn, err := aes.Encrypt(encryptStr)
+47
View File
@@ -0,0 +1,47 @@
package demo
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
"github.com/xinliangnote/go-gin-api/internal/pkg/token"
"go.uber.org/zap"
)
type loginRequest struct {
UserID int `json:"user_id" form:"user_id"` // 用户ID>0
UserName string `json:"user_name" form:"user_name"` // 用户名
}
type loginResponse struct {
Authorization string `json:"authorization"` // 签名
}
// 登录获取 Authorization 码
// @Summary 登录获取 Authorization 码
// @Description 登录获取 Authorization 码
// @Tags Demo
// @Accept json
// @Produce json
// @Param loginRequest body loginRequest true "请求信息"
// @Success 200 {object} loginResponse "签名信息"
// @Router /user/login [post]
func (d *Demo) Login() core.HandlerFunc {
return func(c core.Context) {
req := new(loginRequest)
res := new(loginResponse)
if err := c.ShouldBindJSON(req); err != nil {
c.SetPayload(errno.ErrParam)
return
}
tokenString, err := token.Sign(req.UserID, req.UserName)
if err != nil {
d.logger.Error("token sign err", zap.Error(err))
res.Authorization = ""
} else {
res.Authorization = tokenString
}
c.SetPayload(errno.OK.WithData(res))
}
}
@@ -1,58 +0,0 @@
package jaeger_conn
import (
"context"
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/listen"
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/read"
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/speak"
"github.com/xinliangnote/go-gin-api/internal/api/model/proto/write"
"github.com/xinliangnote/go-gin-api/internal/api/util/grpc_client"
"github.com/xinliangnote/go-gin-api/internal/api/util/request"
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
"github.com/gin-gonic/gin"
)
func JaegerTest(c *gin.Context) {
// 调用 gRPC 服务
conn := grpc_client.CreateServiceListenConn(c)
grpcListenClient := listen.NewListenClient(conn)
resListen, _ := grpcListenClient.ListenData(context.Background(), &listen.Request{Name: "listen"})
// 调用 gRPC 服务
conn = grpc_client.CreateServiceSpeakConn(c)
grpcSpeakClient := speak.NewSpeakClient(conn)
resSpeak, _ := grpcSpeakClient.SpeakData(context.Background(), &speak.Request{Name: "speak"})
// 调用 gRPC 服务
conn = grpc_client.CreateServiceReadConn(c)
grpcReadClient := read.NewReadClient(conn)
resRead, _ := grpcReadClient.ReadData(context.Background(), &read.Request{Name: "read"})
// 调用 gRPC 服务
conn = grpc_client.CreateServiceWriteConn(c)
grpcWriteClient := write.NewWriteClient(conn)
resWrite, _ := grpcWriteClient.WriteData(context.Background(), &write.Request{Name: "write"})
defer conn.Close()
// 调用 HTTP 服务
resHttpGet := ""
_, err := request.HttpGet("http://localhost:9905/sing", c)
if err == nil {
resHttpGet = "[HttpGetOk]"
}
// 业务处理...
msg := resListen.Message + "-" +
resSpeak.Message + "-" +
resRead.Message + "-" +
resWrite.Message + "-" +
resHttpGet
utilGin := response.Gin{Ctx: c}
utilGin.Response(1, msg, nil)
}
@@ -1,208 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: listen.proto
package listen
import (
context "context"
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Request 请求结构
type Request struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_f05da38a4a3e5177, []int{0}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetName() string {
if m != nil {
return m.Name
}
return ""
}
// Response 响应结构
type Response struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_f05da38a4a3e5177, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*Request)(nil), "listen.Request")
proto.RegisterType((*Response)(nil), "listen.Response")
}
func init() { proto.RegisterFile("listen.proto", fileDescriptor_f05da38a4a3e5177) }
var fileDescriptor_f05da38a4a3e5177 = []byte{
// 133 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xc9, 0xc9, 0x2c, 0x2e,
0x49, 0xcd, 0xd3, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x83, 0xf0, 0x94, 0x64, 0xb9, 0xd8,
0x83, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25,
0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82,
0xfc, 0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12,
0x18, 0xd7, 0xc8, 0x9a, 0x8b, 0xcd, 0x07, 0x6c, 0x9c, 0x90, 0x21, 0x17, 0x17, 0x84, 0xe5, 0x92,
0x58, 0x92, 0x28, 0xc4, 0xaf, 0x07, 0xb5, 0x13, 0x6a, 0x85, 0x94, 0x00, 0x42, 0x00, 0x62, 0xa8,
0x12, 0x43, 0x12, 0x1b, 0xd8, 0x41, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcb, 0xbf, 0x9d,
0x0d, 0xa0, 0x00, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// ListenClient is the client API for Listen service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ListenClient interface {
// 定义 ListenData 方法
ListenData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
type listenClient struct {
cc *grpc.ClientConn
}
func NewListenClient(cc *grpc.ClientConn) ListenClient {
return &listenClient{cc}
}
func (c *listenClient) ListenData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/listen.Listen/ListenData", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ListenServer is the server API for Listen service.
type ListenServer interface {
// 定义 ListenData 方法
ListenData(context.Context, *Request) (*Response, error)
}
// UnimplementedListenServer can be embedded to have forward compatible implementations.
type UnimplementedListenServer struct {
}
func (*UnimplementedListenServer) ListenData(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method ListenData not implemented")
}
func RegisterListenServer(s *grpc.Server, srv ListenServer) {
s.RegisterService(&_Listen_serviceDesc, srv)
}
func _Listen_ListenData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ListenServer).ListenData(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/listen.Listen/ListenData",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ListenServer).ListenData(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Listen_serviceDesc = grpc.ServiceDesc{
ServiceName: "listen.Listen",
HandlerType: (*ListenServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ListenData",
Handler: _Listen_ListenData_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "listen.proto",
}
-208
View File
@@ -1,208 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: read.proto
package read
import (
context "context"
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Request 请求结构
type Request struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_7b10ec61df6818dd, []int{0}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetName() string {
if m != nil {
return m.Name
}
return ""
}
// Response 响应结构
type Response struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_7b10ec61df6818dd, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*Request)(nil), "read.Request")
proto.RegisterType((*Response)(nil), "read.Response")
}
func init() { proto.RegisterFile("read.proto", fileDescriptor_7b10ec61df6818dd) }
var fileDescriptor_7b10ec61df6818dd = []byte{
// 132 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2a, 0x4a, 0x4d, 0x4c,
0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x01, 0xb1, 0x95, 0x64, 0xb9, 0xd8, 0x83, 0x52,
0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15,
0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc, 0xbc,
0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18, 0xd7,
0xc8, 0x98, 0x8b, 0x25, 0x28, 0x35, 0x31, 0x45, 0x48, 0x1b, 0xa4, 0x3a, 0x31, 0xc5, 0x25, 0xb1,
0x24, 0x51, 0x88, 0x57, 0x0f, 0x6c, 0x17, 0xd4, 0x70, 0x29, 0x3e, 0x18, 0x17, 0x62, 0x98, 0x12,
0x43, 0x12, 0x1b, 0xd8, 0x19, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x42, 0x4a, 0x9b, 0x2d,
0x94, 0x00, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// ReadClient is the client API for Read service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type ReadClient interface {
// 定义方法
ReadData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
type readClient struct {
cc *grpc.ClientConn
}
func NewReadClient(cc *grpc.ClientConn) ReadClient {
return &readClient{cc}
}
func (c *readClient) ReadData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/read.Read/ReadData", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ReadServer is the server API for Read service.
type ReadServer interface {
// 定义方法
ReadData(context.Context, *Request) (*Response, error)
}
// UnimplementedReadServer can be embedded to have forward compatible implementations.
type UnimplementedReadServer struct {
}
func (*UnimplementedReadServer) ReadData(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method ReadData not implemented")
}
func RegisterReadServer(s *grpc.Server, srv ReadServer) {
s.RegisterService(&_Read_serviceDesc, srv)
}
func _Read_ReadData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ReadServer).ReadData(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/read.Read/ReadData",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ReadServer).ReadData(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Read_serviceDesc = grpc.ServiceDesc{
ServiceName: "read.Read",
HandlerType: (*ReadServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "ReadData",
Handler: _Read_ReadData_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "read.proto",
}
-208
View File
@@ -1,208 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: speak.proto
package speak
import (
context "context"
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Request 请求结构
type Request struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_3c7d3e2f29338937, []int{0}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetName() string {
if m != nil {
return m.Name
}
return ""
}
// Response 响应结构
type Response struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_3c7d3e2f29338937, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*Request)(nil), "speak.Request")
proto.RegisterType((*Response)(nil), "speak.Response")
}
func init() { proto.RegisterFile("speak.proto", fileDescriptor_3c7d3e2f29338937) }
var fileDescriptor_3c7d3e2f29338937 = []byte{
// 132 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x2e, 0x48, 0x4d,
0xcc, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0x64, 0xb9, 0xd8, 0x83,
0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18,
0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc,
0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18,
0xd7, 0xc8, 0x9c, 0x8b, 0x35, 0x18, 0x64, 0x9a, 0x90, 0x1e, 0x17, 0x27, 0x98, 0xe1, 0x92, 0x58,
0x92, 0x28, 0xc4, 0xa7, 0x07, 0xb1, 0x0f, 0x6a, 0xbe, 0x14, 0x3f, 0x9c, 0x0f, 0x31, 0x50, 0x89,
0x21, 0x89, 0x0d, 0xec, 0x16, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4d, 0x04, 0x6f, 0x05,
0x9a, 0x00, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// SpeakClient is the client API for Speak service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type SpeakClient interface {
// 定义方法
SpeakData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
type speakClient struct {
cc *grpc.ClientConn
}
func NewSpeakClient(cc *grpc.ClientConn) SpeakClient {
return &speakClient{cc}
}
func (c *speakClient) SpeakData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/speak.Speak/SpeakData", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// SpeakServer is the server API for Speak service.
type SpeakServer interface {
// 定义方法
SpeakData(context.Context, *Request) (*Response, error)
}
// UnimplementedSpeakServer can be embedded to have forward compatible implementations.
type UnimplementedSpeakServer struct {
}
func (*UnimplementedSpeakServer) SpeakData(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method SpeakData not implemented")
}
func RegisterSpeakServer(s *grpc.Server, srv SpeakServer) {
s.RegisterService(&_Speak_serviceDesc, srv)
}
func _Speak_SpeakData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(SpeakServer).SpeakData(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/speak.Speak/SpeakData",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(SpeakServer).SpeakData(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Speak_serviceDesc = grpc.ServiceDesc{
ServiceName: "speak.Speak",
HandlerType: (*SpeakServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "SpeakData",
Handler: _Speak_SpeakData_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "speak.proto",
}
-208
View File
@@ -1,208 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: write.proto
package write
import (
context "context"
fmt "fmt"
math "math"
proto "github.com/golang/protobuf/proto"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
// This is a compile-time assertion to ensure that this generated file
// is compatible with the proto package it is being compiled against.
// A compilation error at this line likely means your copy of the
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
// Request 请求结构
type Request struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Request) Reset() { *m = Request{} }
func (m *Request) String() string { return proto.CompactTextString(m) }
func (*Request) ProtoMessage() {}
func (*Request) Descriptor() ([]byte, []int) {
return fileDescriptor_67966b2b12a73214, []int{0}
}
func (m *Request) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Request.Unmarshal(m, b)
}
func (m *Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Request.Marshal(b, m, deterministic)
}
func (m *Request) XXX_Merge(src proto.Message) {
xxx_messageInfo_Request.Merge(m, src)
}
func (m *Request) XXX_Size() int {
return xxx_messageInfo_Request.Size(m)
}
func (m *Request) XXX_DiscardUnknown() {
xxx_messageInfo_Request.DiscardUnknown(m)
}
var xxx_messageInfo_Request proto.InternalMessageInfo
func (m *Request) GetName() string {
if m != nil {
return m.Name
}
return ""
}
// Response 响应结构
type Response struct {
Message string `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (*Response) Descriptor() ([]byte, []int) {
return fileDescriptor_67966b2b12a73214, []int{1}
}
func (m *Response) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Response.Unmarshal(m, b)
}
func (m *Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Response.Marshal(b, m, deterministic)
}
func (m *Response) XXX_Merge(src proto.Message) {
xxx_messageInfo_Response.Merge(m, src)
}
func (m *Response) XXX_Size() int {
return xxx_messageInfo_Response.Size(m)
}
func (m *Response) XXX_DiscardUnknown() {
xxx_messageInfo_Response.DiscardUnknown(m)
}
var xxx_messageInfo_Response proto.InternalMessageInfo
func (m *Response) GetMessage() string {
if m != nil {
return m.Message
}
return ""
}
func init() {
proto.RegisterType((*Request)(nil), "write.Request")
proto.RegisterType((*Response)(nil), "write.Response")
}
func init() { proto.RegisterFile("write.proto", fileDescriptor_67966b2b12a73214) }
var fileDescriptor_67966b2b12a73214 = []byte{
// 132 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0x2f, 0xca, 0x2c,
0x49, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x05, 0x73, 0x94, 0x64, 0xb9, 0xd8, 0x83,
0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18,
0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x15, 0x2e, 0x8e, 0xa0, 0xd4, 0xe2, 0x82, 0xfc,
0xbc, 0xe2, 0x54, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0x74, 0x98, 0x12, 0x18,
0xd7, 0xc8, 0x9c, 0x8b, 0x35, 0x1c, 0x64, 0x9a, 0x90, 0x1e, 0x17, 0x27, 0x98, 0xe1, 0x92, 0x58,
0x92, 0x28, 0xc4, 0xa7, 0x07, 0xb1, 0x0f, 0x6a, 0xbe, 0x14, 0x3f, 0x9c, 0x0f, 0x31, 0x50, 0x89,
0x21, 0x89, 0x0d, 0xec, 0x16, 0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x7d, 0xc4, 0x7d,
0x9a, 0x00, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
const _ = grpc.SupportPackageIsVersion4
// WriteClient is the client API for Write service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type WriteClient interface {
// 定义方法
WriteData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
type writeClient struct {
cc *grpc.ClientConn
}
func NewWriteClient(cc *grpc.ClientConn) WriteClient {
return &writeClient{cc}
}
func (c *writeClient) WriteData(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/write.Write/WriteData", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// WriteServer is the server API for Write service.
type WriteServer interface {
// 定义方法
WriteData(context.Context, *Request) (*Response, error)
}
// UnimplementedWriteServer can be embedded to have forward compatible implementations.
type UnimplementedWriteServer struct {
}
func (*UnimplementedWriteServer) WriteData(ctx context.Context, req *Request) (*Response, error) {
return nil, status.Errorf(codes.Unimplemented, "method WriteData not implemented")
}
func RegisterWriteServer(s *grpc.Server, srv WriteServer) {
s.RegisterService(&_Write_serviceDesc, srv)
}
func _Write_WriteData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Request)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(WriteServer).WriteData(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/write.Write/WriteData",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(WriteServer).WriteData(ctx, req.(*Request))
}
return interceptor(ctx, in, info, handler)
}
var _Write_serviceDesc = grpc.ServiceDesc{
ServiceName: "write.Write",
HandlerType: (*WriteServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "WriteData",
Handler: _Write_WriteData_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "write.proto",
}
@@ -1,53 +0,0 @@
package exception
import (
"fmt"
"runtime/debug"
"strings"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
"github.com/gin-gonic/gin"
"github.com/xinliangnote/go-util/mail"
"github.com/xinliangnote/go-util/time"
)
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
DebugStack := ""
for _, v := range strings.Split(string(debug.Stack()), "\n") {
DebugStack += v + "<br>"
}
subject := fmt.Sprintf("【重要错误】%s 项目出错了!", config.AppName)
body := strings.ReplaceAll(MailTemplate, "{ErrorMsg}", fmt.Sprintf("%s", err))
body = strings.ReplaceAll(body, "{RequestTime}", time.GetCurrentDate())
body = strings.ReplaceAll(body, "{RequestURL}", c.Request.Method+" "+c.Request.Host+c.Request.RequestURI)
body = strings.ReplaceAll(body, "{RequestUA}", c.Request.UserAgent())
body = strings.ReplaceAll(body, "{RequestIP}", c.ClientIP())
body = strings.ReplaceAll(body, "{DebugStack}", DebugStack)
options := &mail.Options{
MailHost: config.SystemEmailHost,
MailPort: config.SystemEmailPort,
MailUser: config.SystemEmailUser,
MailPass: config.SystemEmailPass,
MailTo: config.ErrorNotifyUser,
Subject: subject,
Body: body,
}
_ = mail.Send(options)
utilGin := response.Gin{Ctx: c}
utilGin.Response(500, "系统异常,请联系管理员!", nil)
}
}()
c.Next()
}
}
@@ -1,946 +0,0 @@
package exception
var MailTemplate = `<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<div class="content-wrap" style="margin: 0px auto; overflow: hidden; padding-top: 15px; background-color: rgb(255, 255, 255); width: 600px;">
<!---->
<div>
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(62, 207, 88); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: center; color: rgb(255, 255, 255); font-size: 16px;">
<div>
<p style="line-height: 20px; margin: 0px;">系统告警</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" vertical-align="middle" style="padding-top: 40px; width: 600px; background-image: url(&quot;&quot;); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(255, 255, 255); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
<tbody>
<tr>
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
<td style="width: 20%; max-width: 20%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 120px;">
<tbody>
<tr>
<td style="direction: ltr; width: 120px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 13px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: center; color: rgb(51, 51, 51); font-size: 16px;">
<div>
<p style="line-height: 20px; margin: 0px;">告警</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
<tbody>
<tr>
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 6px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(188, 12, 39); font-size: 21px;">
<div>
<p style="line-height: 20px; margin: 0px;">{ErrorMsg}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" vertical-align="middle" style="padding-top: 15px; width: 600px; background-image: url(&quot;&quot;); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(255, 255, 255); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
<tbody>
<tr>
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
<td style="width: 20%; max-width: 20%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 120px;">
<tbody>
<tr>
<td style="direction: ltr; width: 120px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 13px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: center; color: rgb(51, 51, 51); font-size: 16px;">
<div>
<p style="line-height: 20px; margin: 0px;">详情</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
<td style="width: 40%; max-width: 40%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px; line-height: 0px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 240px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="line-height: 0px; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; padding: 25px 0px; word-break: break-word; width: 240px; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse: collapse; border-spacing: 0px;">
<tbody>
<tr>
<td style="width: 375px; border-top: 1px solid rgb(204, 204, 204);"></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(241, 245, 240); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div columnnumber="3">
<div>
<table align="center" border="0" cellpadding="0" cellspacing="0" style="width: 100%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; border: 0px;"><a target="_blank" href="javascript:;" style="cursor: default;">
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">请求时间:</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">{RequestTime}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">请求地址:</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">{RequestURL}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">请求 UA</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">{RequestUA}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">请求 IP</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">{RequestIP}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 10px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">DebugStack</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
<div class="mj-column-per-25" style="width: 100%; max-width: 100%; font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" border="0" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td border="0">
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 0px 20px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(102, 102, 102); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">{DebugStack}</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div></a></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; padding-top: 0px; text-align: center; vertical-align: top;">
<div class="outlook-group-fix" style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" vertical-align="middle" style="padding-top: 15px; width: 600px; background-image: url(&quot;&quot;); background-size: 100px; background-position: 10% 50%; background-repeat: no-repeat;"></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div>
<div>
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="background-color: rgb(65, 207, 88); background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 1% 50%;">
<tbody>
<tr>
<td style="direction: ltr; font-size: 0px; text-align: center; vertical-align: top; width: 600px;">
<table border="0" cellpadding="0" cellspacing="0">
<tbody>
<tr>
<td style="width: 100%; max-width: 100%; min-height: 1px; font-size: 13px; text-align: left; direction: ltr; vertical-align: top; padding: 0px;">
<table border="0" cellpadding="0" cellspacing="0" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="center" style="font-size: 0px; word-break: break-word;">
<table align="center" border="0" cellpadding="0" cellspacing="0" style="border-collapse: collapse; border-spacing: 0px; width: 100%;">
<tbody>
<tr>
<td>
<div class="full">
<div style="margin: 0px auto; max-width: 600px;">
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width: 600px;">
<tbody>
<tr>
<td style="direction: ltr; width: 600px; font-size: 0px; padding-bottom: 0px; text-align: center; vertical-align: top; background-image: url(&quot;&quot;); background-repeat: no-repeat; background-size: 100px; background-position: 10% 50%;">
<div style="font-size: 13px; text-align: left; direction: ltr; display: inline-block; vertical-align: top; width: 100%;">
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%" style="vertical-align: top;">
<tbody>
<tr>
<td align="left" style="font-size: 0px; padding: 20px 10px; word-break: break-word;">
<div class="text" style="margin: 0px; text-align: left; color: rgb(255, 255, 255); font-size: 12px;">
<div>
<p style="line-height: 20px; margin: 0px;">请注意,该邮件地址不接收回复邮件。</p>
</div>
</div></td>
</tr>
</tbody>
</table>
</div></td>
</tr>
</tbody>
</table>
</div>
</div></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>`
@@ -1,40 +0,0 @@
package jaeger
import (
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/jaeger_trace"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
if config.JaegerOpen == 1 {
var parentSpan opentracing.Span
tracer, closer := jaeger_trace.NewJaegerTracer("LXL-TEST", config.JaegerHostPort)
defer closer.Close()
spCtx, err := opentracing.GlobalTracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(c.Request.Header))
if err != nil {
parentSpan = tracer.StartSpan(c.Request.URL.Path)
defer parentSpan.Finish()
} else {
parentSpan = opentracing.StartSpan(
c.Request.URL.Path,
opentracing.ChildOf(spCtx),
opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
ext.SpanKindRPCServer,
)
defer parentSpan.Finish()
}
c.Set("Tracer", tracer)
c.Set("ParentSpanContext", parentSpan.Context())
}
c.Next()
}
}
@@ -1,124 +0,0 @@
package sign_aes
import (
"errors"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
"github.com/gin-gonic/gin"
"github.com/xinliangnote/go-util/aes"
timeUtil "github.com/xinliangnote/go-util/time"
)
var AppSecret string
// AES 对称加密
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
utilGin := response.Gin{Ctx: c}
sign, err := verifySign(c)
if sign != nil {
utilGin.Response(-1, "Debug Sign", sign)
c.Abort()
return
}
if err != nil {
utilGin.Response(-1, err.Error(), sign)
c.Abort()
return
}
c.Next()
}
}
// 验证签名
func verifySign(c *gin.Context) (map[string]string, error) {
_ = c.Request.ParseForm()
req := c.Request.Form
debug := strings.Join(c.Request.Form["debug"], "")
ak := strings.Join(c.Request.Form["ak"], "")
sn := strings.Join(c.Request.Form["sn"], "")
ts := strings.Join(c.Request.Form["ts"], "")
// 验证来源
value, ok := config.ApiAuthConfig[ak]
if ok {
AppSecret = value["aes"]
} else {
return nil, errors.New("ak Error")
}
if debug == "1" {
currentUnix := timeUtil.GetCurrentUnix()
req.Set("ts", strconv.FormatInt(currentUnix, 10))
sn, err := createSign(req)
if err != nil {
return nil, errors.New("sn Exception")
}
res := map[string]string{
"ts": strconv.FormatInt(currentUnix, 10),
"sn": sn,
}
return res, nil
}
// 验证过期时间
timestamp := time.Now().Unix()
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
tsInt, _ := strconv.ParseInt(ts, 10, 64)
if tsInt > timestamp || timestamp-tsInt >= exp {
return nil, errors.New("ts Error")
}
// 验证签名
if sn == "" {
return nil, errors.New("sn Error")
}
decryptStr, decryptErr := aes.Decrypt(sn, []byte(AppSecret), AppSecret)
if decryptErr != nil {
return nil, errors.New(decryptErr.Error())
}
if decryptStr != createEncryptStr(req) {
return nil, errors.New("sn Error")
}
return nil, nil
}
// 创建签名
func createSign(params url.Values) (string, error) {
return aes.Encrypt(createEncryptStr(params), []byte(AppSecret), AppSecret)
}
func createEncryptStr(params url.Values) string {
var key []string
var str = ""
for k := range params {
if k != "sn" && k != "debug" {
key = append(key, k)
}
}
sort.Strings(key)
for i := 0; i < len(key); i++ {
if i == 0 {
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
} else {
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
}
}
return str
}
@@ -1,112 +0,0 @@
package sign_md5
import (
"errors"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
"github.com/gin-gonic/gin"
"github.com/xinliangnote/go-util/md5"
timeUtil "github.com/xinliangnote/go-util/time"
)
var AppSecret string
// MD5 组合加密
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
utilGin := response.Gin{Ctx: c}
sign, err := verifySign(c)
if sign != nil {
utilGin.Response(-1, "Debug Sign", sign)
c.Abort()
return
}
if err != nil {
utilGin.Response(-1, err.Error(), sign)
c.Abort()
return
}
c.Next()
}
}
// 验证签名
func verifySign(c *gin.Context) (map[string]string, error) {
_ = c.Request.ParseForm()
req := c.Request.Form
debug := strings.Join(c.Request.Form["debug"], "")
ak := strings.Join(c.Request.Form["ak"], "")
sn := strings.Join(c.Request.Form["sn"], "")
ts := strings.Join(c.Request.Form["ts"], "")
// 验证来源
value, ok := config.ApiAuthConfig[ak]
if ok {
AppSecret = value["md5"]
} else {
return nil, errors.New("ak Error")
}
if debug == "1" {
currentUnix := timeUtil.GetCurrentUnix()
req.Set("ts", strconv.FormatInt(currentUnix, 10))
res := map[string]string{
"ts": strconv.FormatInt(currentUnix, 10),
"sn": createSign(req),
}
return res, nil
}
// 验证过期时间
timestamp := time.Now().Unix()
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
tsInt, _ := strconv.ParseInt(ts, 10, 64)
if tsInt > timestamp || timestamp-tsInt >= exp {
return nil, errors.New("ts Error")
}
// 验证签名
if sn == "" || sn != createSign(req) {
return nil, errors.New("sn Error")
}
return nil, nil
}
// 创建签名
func createSign(params url.Values) string {
// 自定义 MD5 组合
return md5.MD5(AppSecret + createEncryptStr(params) + AppSecret)
}
func createEncryptStr(params url.Values) string {
var key []string
var str = ""
for k := range params {
if k != "sn" && k != "debug" {
key = append(key, k)
}
}
sort.Strings(key)
for i := 0; i < len(key); i++ {
if i == 0 {
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
} else {
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
}
}
return str
}
@@ -1,124 +0,0 @@
package sign_rsa
import (
"errors"
"fmt"
"net/url"
"sort"
"strconv"
"strings"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/response"
"github.com/gin-gonic/gin"
"github.com/xinliangnote/go-util/rsa"
timeUtil "github.com/xinliangnote/go-util/time"
)
var AppSecret string
// RSA 非对称加密
func SetUp() gin.HandlerFunc {
return func(c *gin.Context) {
utilGin := response.Gin{Ctx: c}
sign, err := verifySign(c)
if sign != nil {
utilGin.Response(-1, "Debug Sign", sign)
c.Abort()
return
}
if err != nil {
utilGin.Response(-1, err.Error(), sign)
c.Abort()
return
}
c.Next()
}
}
// 验证签名
func verifySign(c *gin.Context) (map[string]string, error) {
_ = c.Request.ParseForm()
req := c.Request.Form
debug := strings.Join(c.Request.Form["debug"], "")
ak := strings.Join(c.Request.Form["ak"], "")
sn := strings.Join(c.Request.Form["sn"], "")
ts := strings.Join(c.Request.Form["ts"], "")
// 验证来源
value, ok := config.ApiAuthConfig[ak]
if ok {
AppSecret = value["rsa"]
} else {
return nil, errors.New("ak Error")
}
if debug == "1" {
currentUnix := timeUtil.GetCurrentUnix()
req.Set("ts", strconv.FormatInt(currentUnix, 10))
sn, err := createSign(req)
if err != nil {
return nil, errors.New("sn Exception")
}
res := map[string]string{
"ts": strconv.FormatInt(currentUnix, 10),
"sn": sn,
}
return res, nil
}
// 验证过期时间
timestamp := time.Now().Unix()
exp, _ := strconv.ParseInt(config.AppSignExpiry, 10, 64)
tsInt, _ := strconv.ParseInt(ts, 10, 64)
if tsInt > timestamp || timestamp-tsInt >= exp {
return nil, errors.New("ts Error")
}
// 验证签名
if sn == "" {
return nil, errors.New("sn Error")
}
decryptStr, decryptErr := rsa.PrivateDecrypt(sn, config.AppRsaPrivateFile)
if decryptErr != nil {
return nil, errors.New(decryptErr.Error())
}
if decryptStr != createEncryptStr(req) {
return nil, errors.New("sn Error")
}
return nil, nil
}
// 创建签名
func createSign(params url.Values) (string, error) {
return rsa.PublicEncrypt(createEncryptStr(params), AppSecret)
}
func createEncryptStr(params url.Values) string {
var key []string
var str = ""
for k := range params {
if k != "sn" && k != "debug" {
key = append(key, k)
}
}
sort.Strings(key)
for i := 0; i < len(key); i++ {
if i == 0 {
str = fmt.Sprintf("%v=%v", key[i], params.Get(key[i]))
} else {
str = str + fmt.Sprintf("&%v=%v", key[i], params.Get(key[i]))
}
}
return str
}
@@ -1,77 +0,0 @@
package grpc_client
import (
"context"
"fmt"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-gin-api/internal/api/util/grpc_log"
"github.com/xinliangnote/go-gin-api/internal/api/util/jaeger_trace"
"github.com/gin-gonic/gin"
grpc_middeware "github.com/grpc-ecosystem/go-grpc-middleware"
"github.com/opentracing/opentracing-go"
"google.golang.org/grpc"
)
func CreateServiceListenConn(c *gin.Context) *grpc.ClientConn {
return createGrpcConn("127.0.0.1:9901", c)
}
func CreateServiceSpeakConn(c *gin.Context) *grpc.ClientConn {
return createGrpcConn("127.0.0.1:9902", c)
}
func CreateServiceReadConn(c *gin.Context) *grpc.ClientConn {
return createGrpcConn("127.0.0.1:9903", c)
}
func CreateServiceWriteConn(c *gin.Context) *grpc.ClientConn {
return createGrpcConn("127.0.0.1:9904", c)
}
func createGrpcConn(serviceAddress string, c *gin.Context) *grpc.ClientConn {
var conn *grpc.ClientConn
var err error
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*500)
defer cancel()
if config.JaegerOpen == 1 {
tracer, _ := c.Get("Tracer")
parentSpanContext, _ := c.Get("ParentSpanContext")
conn, err = grpc.DialContext(
ctx,
serviceAddress,
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithUnaryInterceptor(
grpc_middeware.ChainUnaryClient(
jaeger_trace.ClientInterceptor(tracer.(opentracing.Tracer), parentSpanContext.(opentracing.SpanContext)),
grpc_log.ClientInterceptor(),
),
),
)
} else {
conn, err = grpc.DialContext(
ctx,
serviceAddress,
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithUnaryInterceptor(
grpc_middeware.ChainUnaryClient(
grpc_log.ClientInterceptor(),
),
),
)
}
if err != nil {
fmt.Println(serviceAddress, "grpc conn err:", err)
}
return conn
}
-63
View File
@@ -1,63 +0,0 @@
package grpc_log
import (
"context"
"fmt"
"log"
"os"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/xinliangnote/go-util/json"
"github.com/xinliangnote/go-util/time"
"google.golang.org/grpc"
)
var grpcChannel = make(chan string, 100)
func ClientInterceptor() grpc.UnaryClientInterceptor {
go handleGrpcChannel()
return func(ctx context.Context, method string,
req, reply interface{}, cc *grpc.ClientConn,
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
// 开始时间
startTime := time.GetCurrentMilliUnix()
err := invoker(ctx, method, req, reply, cc, opts...)
// 结束时间
endTime := time.GetCurrentMilliUnix()
// 日志格式
grpcLogMap := make(map[string]interface{})
grpcLogMap["request_time"] = startTime
grpcLogMap["request_data"] = req
grpcLogMap["request_method"] = method
grpcLogMap["response_data"] = reply
grpcLogMap["response_error"] = err
grpcLogMap["cost_time"] = fmt.Sprintf("%vms", endTime-startTime)
grpcLogJson, _ := json.Encode(grpcLogMap)
grpcChannel <- grpcLogJson
return err
}
}
func handleGrpcChannel() {
if f, err := os.OpenFile(config.AppGrpcLogName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666); err != nil {
log.Println(err)
} else {
for accessLog := range grpcChannel {
_, _ = f.WriteString(accessLog + "\n")
}
}
return
}
@@ -1,98 +0,0 @@
package jaeger_trace
import (
"context"
"fmt"
"io"
"strings"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
"github.com/opentracing/opentracing-go/log"
"github.com/uber/jaeger-client-go"
jaegerConfig "github.com/uber/jaeger-client-go/config"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
)
func NewJaegerTracer(serviceName string, jaegerHostPort string) (opentracing.Tracer, io.Closer) {
cfg := &jaegerConfig.Configuration{
Sampler: &jaegerConfig.SamplerConfig{
Type: "const", //固定采样
Param: 1, //1=全采样、0=不采样
},
Reporter: &jaegerConfig.ReporterConfig{
LogSpans: true,
LocalAgentHostPort: jaegerHostPort,
},
ServiceName: serviceName,
}
tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
}
opentracing.SetGlobalTracer(tracer)
return tracer, closer
}
type MDReaderWriter struct {
metadata.MD
}
// ForeachKey implements ForeachKey of opentracing.TextMapReader
func (c MDReaderWriter) ForeachKey(handler func(key, val string) error) error {
for k, vs := range c.MD {
for _, v := range vs {
if err := handler(k, v); err != nil {
return err
}
}
}
return nil
}
// Set implements Set() of opentracing.TextMapWriter
func (c MDReaderWriter) Set(key, val string) {
key = strings.ToLower(key)
c.MD[key] = append(c.MD[key], val)
}
// ClientInterceptor grpc client
func ClientInterceptor(tracer opentracing.Tracer, spanContext opentracing.SpanContext) grpc.UnaryClientInterceptor {
return func(ctx context.Context, method string,
req, reply interface{}, cc *grpc.ClientConn,
invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {
span := opentracing.StartSpan(
"call gRPC",
opentracing.ChildOf(spanContext),
opentracing.Tag{Key: string(ext.Component), Value: "gRPC"},
ext.SpanKindRPCClient,
)
defer span.Finish()
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(nil)
} else {
md = md.Copy()
}
err := tracer.Inject(span.Context(), opentracing.TextMap, MDReaderWriter{md})
if err != nil {
span.LogFields(log.String("inject-error", err.Error()))
}
newCtx := metadata.NewOutgoingContext(ctx, md)
err = invoker(newCtx, method, req, reply, cc, opts...)
if err != nil {
span.LogFields(log.String("call-error", err.Error()))
}
return err
}
}
-64
View File
@@ -1,64 +0,0 @@
package request
import (
"crypto/tls"
"io/ioutil"
"log"
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/config"
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
func HttpGet(url string, c *gin.Context) (string, error) {
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
client := &http.Client{
Timeout: time.Second * 5, //默认5秒超时时间
Transport: tr,
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return "", err
}
if config.JaegerOpen == 1 {
tracer, _ := c.Get("Tracer")
parentSpanContext, _ := c.Get("ParentSpanContext")
span := opentracing.StartSpan(
"call Http Get",
opentracing.ChildOf(parentSpanContext.(opentracing.SpanContext)),
opentracing.Tag{Key: string(ext.Component), Value: "HTTP"},
ext.SpanKindRPCClient,
)
span.Finish()
injectErr := tracer.(opentracing.Tracer).Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
if injectErr != nil {
log.Fatalf("%s: Couldn't inject headers", err)
}
}
resp, err := client.Do(req)
if err != nil {
return "", err
}
content, err := ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
return "", err
}
return string(content), err
}
-22
View File
@@ -1,22 +0,0 @@
package response
import "github.com/gin-gonic/gin"
type Gin struct {
Ctx *gin.Context
}
type Response struct {
Code int `json:"code"`
Message string `json:"msg"`
Data interface{} `json:"data"`
}
func (g *Gin) Response(code int, msg string, data interface{}) {
g.Ctx.JSON(200, Response{
Code: code,
Message: msg,
Data: data,
})
return
}
+9 -2
View File
@@ -9,10 +9,17 @@ import (
var config = new(Config)
type Config struct {
DB struct {
Mail struct {
Host string `mapstructure:"host"`
Port int `mapstructure:"port"`
} `mapstructure:"db"`
User string `mapstructure:"user"`
Pass string `mapstructure:"pass"`
To string `mapstructure:"to"`
} `mapstructure:"mail"`
JWT struct {
Secret string `mapstructure:"secret"`
} `mapstructure:"jwt"`
Aes struct {
Key string `mapstructure:"key"`
+74 -8
View File
@@ -5,6 +5,7 @@ import (
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
@@ -20,10 +21,14 @@ type HandlerFunc func(c Context)
type Journal = journal.T
const (
_JournalName = "_journal_"
_LoggerName = "_logger_"
_BodyName = "_body_"
_PayloadName = "_payload_"
_Alias = "_alias_"
_JournalName = "_journal_"
_LoggerName = "_logger_"
_BodyName = "_body_"
_PayloadName = "_payload_"
_UserID = "_user_id_"
_UserName = "_user_name_"
_AbortErrorName = "_abort_error_"
)
var contextPool = &sync.Pool{
@@ -87,14 +92,22 @@ type Context interface {
GetHeader(key string) string
SetHeader(key, value string)
UserID() int
setUserID(userID int)
UserName() string
setUserName(userName string)
AbortWithError(err errno.Error)
abortError() errno.Error
Alias() string
setAlias(path string)
RawData() []byte
Method() string
Host() string
Path() string
URI() string
}
@@ -208,6 +221,59 @@ func (c *context) SetHeader(key, value string) {
c.ctx.Header(key, value)
}
func (c *context) UserID() int {
val, ok := c.ctx.Get(_UserID)
if !ok {
return 0
}
return val.(int)
}
func (c *context) setUserID(userID int) {
c.ctx.Set(_UserID, userID)
}
func (c *context) UserName() string {
val, ok := c.ctx.Get(_UserName)
if !ok {
return ""
}
return val.(string)
}
func (c *context) setUserName(userName string) {
c.ctx.Set(_UserName, userName)
}
func (c *context) AbortWithError(err errno.Error) {
if err != nil {
c.ctx.AbortWithStatus(http.StatusInternalServerError)
c.ctx.Set(_AbortErrorName, err)
}
}
func (c *context) abortError() errno.Error {
err, _ := c.ctx.Get(_AbortErrorName)
return err.(errno.Error)
}
func (c *context) Alias() string {
path, ok := c.ctx.Get(_Alias)
if !ok {
return ""
}
return path.(string)
}
func (c *context) setAlias(path string) {
if path = strings.TrimSpace(path); path != "" {
c.ctx.Set(_Alias, path)
}
}
func (c *context) RawData() []byte {
body, ok := c.ctx.Get(_BodyName)
if !ok {
+76 -8
View File
@@ -19,6 +19,7 @@ import (
cors "github.com/rs/cors/wrapper/gin"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
"go.uber.org/multierr"
"go.uber.org/zap"
"golang.org/x/time/rate"
)
@@ -41,6 +42,7 @@ type option struct {
disableSwagger bool
disablePrometheus bool
panicNotify OnPanicNotify
recordMetrics RecordMetrics
enableCors bool
enableRate bool
}
@@ -48,6 +50,10 @@ type option struct {
// OnPanicNotify 发生panic时通知用
type OnPanicNotify func(ctx Context, err interface{}, stackInfo string)
// RecordMetrics 记录prometheus指标用
// 如果使用AliasForRecordMetrics配置了别名,uri将被替换为别名。
type RecordMetrics func(method, uri string, success bool, httpCode, businessCode int, costSeconds float64)
// WithDisablePProf 禁用 pprof
func WithDisablePProf() Option {
return func(opt *option) {
@@ -73,6 +79,14 @@ func WithDisableproPrometheus() Option {
func WithPanicNotify(notify OnPanicNotify) Option {
return func(opt *option) {
opt.panicNotify = notify
fmt.Println(color.Green("* [register panic notify]"))
}
}
// WithRecordMetrics 设置记录prometheus记录指标回调
func WithRecordMetrics(record RecordMetrics) Option {
return func(opt *option) {
opt.recordMetrics = record
}
}
@@ -80,12 +94,14 @@ func WithPanicNotify(notify OnPanicNotify) Option {
func WithEnableCors() Option {
return func(opt *option) {
opt.enableCors = true
fmt.Println(color.Green("* [register cors]"))
}
}
func WithEnableRate() Option {
return func(opt *option) {
opt.enableRate = true
fmt.Println(color.Green("* [register rate]"))
}
}
@@ -94,6 +110,27 @@ func DisableJournal(ctx Context) {
ctx.disableJournal()
}
// AliasForRecordMetrics 对请求uri起个别名,用于prometheus记录指标。
// 如:Get /user/:username 这样的uri,因为username会有非常多的情况,这样记录prometheus指标会非常的不有好。
func AliasForRecordMetrics(path string) HandlerFunc {
return func(ctx Context) {
ctx.setAlias(path)
}
}
// WrapAuthHandler 用来处理 Auth 的入口,在之后的handler中只需 ctx.UserID() ctx.UserName() 即可。
func WrapAuthHandler(handler func(Context) (userID int, userName string, err errno.Error)) HandlerFunc {
return func(ctx Context) {
userID, userName, err := handler(ctx)
if err != nil {
ctx.AbortWithError(err)
return
}
ctx.setUserID(userID)
ctx.setUserName(userName)
}
}
// RouterGroup 包装gin的RouterGroup
type RouterGroup interface {
Group(string, ...HandlerFunc) RouterGroup
@@ -199,8 +236,6 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
gin.SetMode(gin.ReleaseMode)
gin.DisableBindValidation()
engine := gin.New()
mux := &mux{
engine: gin.New(),
}
@@ -232,15 +267,18 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
}
if !opt.disablePProf {
pprof.Register(engine) // register pprof to gin
pprof.Register(mux.engine) // register pprof to gin
fmt.Println(color.Green("* [register pprof]"))
}
if !opt.disableSwagger {
mux.engine.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler)) // register swagger
fmt.Println(color.Green("* [register swagger]"))
}
if !opt.disablePrometheus {
mux.engine.GET("/metrics", gin.WrapH(promhttp.Handler())) // register prometheus
fmt.Println(color.Green("* [register prometheus]"))
}
if opt.enableCors {
@@ -303,18 +341,48 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
return
}
response := context.GetPayload()
if x := context.Journal(); x != nil {
context.SetHeader(journal.JournalHeader, x.ID())
response.WithID(x.ID())
var (
response errno.Error
abortErr error
)
if ctx.IsAborted() {
for i := range ctx.Errors { // gin error
multierr.AppendInto(&abortErr, ctx.Errors[i])
}
if err := context.abortError(); err != nil { // customer err
multierr.AppendInto(&abortErr, errors.New(err.GetMsg()))
response = err
}
} else {
response.WithID("")
response = context.GetPayload()
}
if response != nil {
if x := context.Journal(); x != nil {
context.SetHeader(journal.JournalHeader, x.ID())
response.WithID(x.ID())
} else {
response.WithID("")
}
ctx.JSON(http.StatusOK, response)
}
if opt.recordMetrics != nil {
uri := context.URI()
if alias := context.Alias(); alias != "" {
uri = alias
}
businessCode := 0
if response != nil {
businessCode = response.GetCode()
}
opt.recordMetrics(context.Method(), uri, !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, ctx.Writer.Status(), businessCode, time.Since(ts).Seconds())
}
var j *journal.Journal
if x := context.Journal(); x != nil {
j = x.(*journal.Journal)
+12
View File
@@ -13,6 +13,10 @@ type Error interface {
WithData(data interface{}) Error
// WithID 设置当前请求的唯一ID
WithID(id string) Error
// GetCode 获取 Code
GetCode() int
// GetMsg 获取 Msg
GetMsg() string
// ToString 返回 JSON 格式的错误详情
ToString() string
}
@@ -44,6 +48,14 @@ func (e *err) WithID(id string) Error {
return e
}
func (e *err) GetCode() int {
return e.Code
}
func (e *err) GetMsg() string {
return e.Msg
}
// ToString 返回 JSON 格式的错误详情
func (e *err) ToString() string {
err := &struct {
+6
View File
@@ -0,0 +1,6 @@
package metrics
// RecordMetrics 记录指标
func RecordMetrics(method, uri string, success bool, httpCode, businessCode int, costSeconds float64) {
//fmt.Printf(">>>>>>>Metrics\nmethod:%s\nuri:%s\nsuccess:%t\nhttp code:%d\nbusiness code:%d\ncost seconds:%.9f\n<<<<<<<\n", method, uri, success, httpCode, businessCode, costSeconds)
}
-29
View File
@@ -1,29 +0,0 @@
package notify
import (
"bytes"
"fmt"
"strings"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/pkg/color"
)
// Email
func Email(ctx core.Context, err interface{}, stackInfo string) {
buf := bytes.NewBuffer(nil)
buf.WriteString(fmt.Sprintf("URI: %s %s%s <br/>", ctx.Method(), ctx.Host(), ctx.URI()))
buf.WriteString(fmt.Sprintf("JournalID: %s <br/>", ctx.Journal().ID()))
buf.WriteString(fmt.Sprintf("ErrorInfo: %+v <br/>", err))
buf.WriteString(fmt.Sprintf("StackInfo: <br/>"))
for _, v := range strings.Split(stackInfo, "\n") {
buf.WriteString(v)
buf.WriteString(" <br/>")
}
content := buf.String()
fmt.Println(color.Red(content))
}
+39
View File
@@ -0,0 +1,39 @@
package mail
import (
"strings"
"gopkg.in/gomail.v2"
)
type Options struct {
MailHost string
MailPort int
MailUser string // 发件人
MailPass string // 发件人密码
MailTo string // 收件人 多个用,分割
Subject string // 邮件主题
Body string // 邮件内容
}
func Send(o *Options) error {
m := gomail.NewMessage()
//设置发件人
m.SetHeader("From", o.MailUser)
//设置发送给多个用户
mailArrTo := strings.Split(o.MailTo, ",")
m.SetHeader("To", mailArrTo...)
//设置邮件主题
m.SetHeader("Subject", o.Subject)
//设置邮件正文
m.SetBody("text/html", o.Body)
d := gomail.NewDialer(o.MailHost, o.MailPort, o.MailUser, o.MailPass)
return d.DialAndSend(m)
}
+23
View File
@@ -0,0 +1,23 @@
package mail
import (
"testing"
)
func TestSend(t *testing.T) {
options := &Options{
MailHost: "smtp.163.com",
MailPort: 465,
MailUser: "xxx@163.com",
MailPass: "", //密码或授权码
MailTo: "",
Subject: "subject",
Body: "body",
}
err := Send(options)
if err != nil {
t.Error("Mail Send error", err)
return
}
t.Log("success")
}
+44
View File
@@ -0,0 +1,44 @@
package mail
import (
"bytes"
"fmt"
"html/template"
"time"
"github.com/xinliangnote/go-gin-api/internal/pkg/notify/mail/templates"
)
// NewPanicHTMLEmail 发送系统异常邮件 html
func NewPanicHTMLEmail(method, host, uri, id string, msg interface{}, stack string) (subject string, body string, err error) {
mailData := &struct {
URL string
ID string
Msg string
Stack string
Year int
}{
URL: fmt.Sprintf("%s %s%s", method, host, uri),
ID: id,
Msg: fmt.Sprintf("%+v", msg),
Stack: stack,
Year: time.Now().Year(),
}
mailTplContent, err := getEmailHTMLContent(templates.PanicMail, mailData)
return fmt.Sprintf("[系统异常]-%s", uri), mailTplContent, err
}
// getEmailHTMLContent 获取邮件模板
func getEmailHTMLContent(mailTpl string, mailData interface{}) (string, error) {
tpl, err := template.New("email tpl").Parse(mailTpl)
if err != nil {
return "", err
}
buffer := new(bytes.Buffer)
err = tpl.Execute(buffer, mailData)
if err != nil {
return "", err
}
return buffer.String(), nil
}
@@ -0,0 +1,119 @@
package templates
const PanicMail = `
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css" rel="stylesheet" media="all">
/* Media Queries */
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body style="margin: 0; padding: 0; width: 100%; background-color: #F2F4F6;">
<table width="100%" cellpadding="0" cellspacing="0">
<tr>
<td style="width: 100%; margin: 0; padding: 0; background-color: #F2F4F6;" align="center">
<table width="100%" cellpadding="0" cellspacing="0">
<!-- Logo -->
<tr>
<td style="padding: 25px 0; text-align: center;">
系统异常
</td>
</tr>
<!-- Email Body -->
<tr>
<td style="width: 100%; margin: 0; padding: 0; border-top: 1px solid #EDEFF2; border-bottom: 1px solid #EDEFF2; background-color: #FFF;"
width="100%">
<table style="width: auto; max-width: 750px; margin: 0 auto; padding: 0;" align="center"
width="750" cellpadding="0" cellspacing="0">
<tr>
<td style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px;">
<!-- Greeting -->
<h1 style="margin-top: 0; color: #2F3133; font-size: 19px; font-weight: bold; text-align: left;">
Hello!
</h1>
<!-- Intro -->
<p style="margin-top: 0; color: #74787E; line-height: 1.5em;">
您收到此电子邮件,请紧急安排处理。
</p>
<!-- Action Button -->
<table style="width: 100%; margin: 30px auto; padding: 0;"
width="100%" cellpadding="0" cellspacing="0">
<tr style="margin-top: 0; color: #74787E; line-height: 1.5em;">
<td style="width: 10%;">
ID:
</td>
<td style="width: 90%">
{{.ID}}
</td>
</tr>
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
<td style="width: 10%;">
URL:
</td>
<td style="width: 90%">
{{.URL}}
</td>
</tr>
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
<td style="width: 10%;">
Error:
</td>
<td style="width: 90%">
{{.Msg}}
</td>
</tr>
<tr style="margin-top: 0; color: #74787E; font-size: 16px; line-height: 1.5em;">
<td style="width: 10%;">
Stack:
</td>
<td style="width: 90%;">
{{.Stack}}}
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
<!-- Footer -->
<tr>
<td>
<table style="width: auto; max-width: 570px; margin: 0 auto; padding: 0; text-align: center;"
align="center" width="750" cellpadding="0" cellspacing="0">
<tr>
<td style="font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; color: #AEAEAE; padding: 35px; text-align: center;">
<p style="margin-top: 0; color: #74787E; font-size: 12px; line-height: 1.5em;">
&copy; {{.Year}}
All rights reserved.
</p>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>
</body>
</html>
`
+38
View File
@@ -0,0 +1,38 @@
package notify
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/notify/mail"
"go.uber.org/zap"
)
// OnPanicNotify
func OnPanicNotify(ctx core.Context, err interface{}, stackInfo string) {
cfg := configs.Get().Mail
if cfg.Host == "" || cfg.Port == 0 || cfg.User == "" || cfg.Pass == "" || cfg.To == "" {
ctx.Logger().Error("Mail config error")
return
}
subject, body, htmlErr := mail.NewPanicHTMLEmail(ctx.Method(), ctx.Host(), ctx.URI(), ctx.Journal().ID(), err, stackInfo)
if htmlErr != nil {
ctx.Logger().Error("NewPanicHTMLEmail error", zap.Error(htmlErr))
return
}
options := &mail.Options{
MailHost: cfg.Host,
MailPort: cfg.Port,
MailUser: cfg.User,
MailPass: cfg.Pass,
MailTo: cfg.To,
Subject: subject,
Body: body,
}
sendErr := mail.Send(options)
if sendErr != nil {
ctx.Logger().Error("Mail Send error", zap.Error(sendErr))
}
return
}
+53
View File
@@ -0,0 +1,53 @@
package token
import (
"time"
"github.com/dgrijalva/jwt-go"
)
//var secret = configs.Get().JWT.Secret
var secret = "i1ydX9RtHyuJTrw7frcu"
type claims struct {
UserID int
UserName string
jwt.StandardClaims
}
func Sign(userId int, userName string) (tokenString string, err error) {
// The token content.
// iss: Issuer)签发者
// iat: Issued At)签发时间,用Unix时间戳表示
// exp: Expiration Time)过期时间,用Unix时间戳表示
// aud: Audience)接收该JWT的一方
// sub: Subject)该JWT的主题
// nbf: Not Before)不要早于这个时间
// jti: JWT ID)用于标识JWT的唯一ID
claims := claims{
userId,
userName,
jwt.StandardClaims{
NotBefore: time.Now().Unix(),
IssuedAt: time.Now().Unix(),
ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
Issuer: "go-gin-api",
},
}
tokenString, err = jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(secret))
return
}
func Parse(tokenString string) (*claims, error) {
tokenClaims, err := jwt.ParseWithClaims(tokenString, &claims{}, func(token *jwt.Token) (interface{}, error) {
return []byte(secret), nil
})
if tokenClaims != nil {
if claims, ok := tokenClaims.Claims.(*claims); ok && tokenClaims.Valid {
return claims, nil
}
}
return nil, err
}
+26
View File
@@ -0,0 +1,26 @@
package token
import (
"testing"
)
// 执行 Test 时,先将 token.secret 设置值
func TestSign(t *testing.T) {
tokenString, err := Sign(123456789, "xinliangnote")
if err != nil {
t.Error("sign error", err)
return
}
t.Log(tokenString)
}
func TestParse(t *testing.T) {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEyMzQ1Njc4OSwidXNlcm5hbWUiOiJ4aW5saWFuZyIsImV4cCI6MTYwOTQ2NzcwNCwiaWF0IjoxNjA5MzgxMzA0LCJpc3MiOiJnby1naW4tYXBpIiwibmJmIjoxNjA5MzgxMzA0fQ.hccv8F713WpKcwiSldBrFLZz_2SZzOTPedPi-8ps7M4"
user, err := Parse(tokenString)
if err != nil {
t.Error("parse error", err)
return
}
t.Log(user)
}
+29
View File
@@ -0,0 +1,29 @@
package middleware
import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/errno"
"github.com/xinliangnote/go-gin-api/internal/pkg/token"
)
func AuthHandler(ctx core.Context) (userId int, userName string, err errno.Error) {
auth := ctx.GetHeader("Authorization")
if auth == "" {
err = errno.ErrSignParam
return
}
claims, errParse := token.Parse(auth)
if errParse != nil {
err = errno.ErrSignParam
return
}
userId = claims.UserID
if userId <= 0 {
err = errno.ErrSignParam
return
}
userName = claims.UserName
return
}
@@ -3,7 +3,9 @@ package router
import (
"github.com/xinliangnote/go-gin-api/internal/api/controller/demo"
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/metrics"
"github.com/xinliangnote/go-gin-api/internal/pkg/notify"
"github.com/xinliangnote/go-gin-api/internal/router/middleware"
"github.com/pkg/errors"
"go.uber.org/zap"
@@ -18,24 +20,27 @@ func NewHTTPMux(logger *zap.Logger) (core.Mux, error) {
mux, err := core.New(logger,
core.WithEnableCors(),
core.WithEnableRate(),
core.WithPanicNotify(notify.Email),
core.WithPanicNotify(notify.OnPanicNotify),
core.WithRecordMetrics(metrics.RecordMetrics),
)
if err != nil {
panic(err)
}
//设置路由中间件
//engine.Use(exception.SetUp(), jaeger.SetUp())
demoHandler := demo.NewDemo(logger)
d := mux.Group("/demo")
u := mux.Group("/user")
{
d.GET("user/:name", demoHandler.User())
u.POST("/login", demoHandler.Login())
}
d := mux.Group("/demo", core.WrapAuthHandler(middleware.AuthHandler)) //使用 auth 验证
{
d.GET("user/:name", demoHandler.User(), core.DisableJournal)
// 模拟数据
d.GET("get/:name", demoHandler.Get(), core.DisableJournal)
d.GET("get/:name", demoHandler.Get())
d.POST("post", demoHandler.Post(), core.DisableJournal)
// 测试加密性能
@@ -44,8 +49,5 @@ func NewHTTPMux(logger *zap.Logger) (core.Mux, error) {
d.GET("/md5/test", demoHandler.MD5Test())
}
// 测试链路追踪
//mux.GET("/jaeger_test", jaeger_conn.JaegerTest)
return mux, nil
}
View File
View File
+3 -2
View File
@@ -6,8 +6,8 @@ import (
"net/http"
"time"
"github.com/xinliangnote/go-gin-api/internal/api/router"
"github.com/xinliangnote/go-gin-api/internal/pkg/configs"
"github.com/xinliangnote/go-gin-api/internal/router"
"github.com/xinliangnote/go-gin-api/pkg/logger"
"github.com/xinliangnote/go-gin-api/pkg/shutdown"
@@ -22,7 +22,7 @@ import (
// @contact.url
// @contact.email
// @host localhost:9999
// @host 127.0.0.1:9999
// @BasePath
func main() {
loggers, err := logger.NewJSONLogger(
@@ -33,6 +33,7 @@ func main() {
if err != nil {
panic(err)
}
defer loggers.Sync()
mux, err := router.NewHTTPMux(loggers)
if err != nil {
+16
View File
@@ -0,0 +1,16 @@
package aes
import "testing"
const (
Key = "IgkibX71IEf382PT"
Iv = "IgkibX71IEf382PT"
)
func TestEncrypt(t *testing.T) {
t.Log(New(Key, Iv).Encrypt("123456"))
}
func TestDecrypt(t *testing.T) {
t.Log(New(Key, Iv).Decrypt("GO-ri84zevE-z1biJwfQPw=="))
}
+7
View File
@@ -0,0 +1,7 @@
package env
import "testing"
func TestActive(t *testing.T) {
t.Log(Active().Value())
}
+7
View File
@@ -0,0 +1,7 @@
package md5
import "testing"
func TestEncrypt(t *testing.T) {
t.Log(New().Encrypt("123456"))
}
+64
View File
@@ -0,0 +1,64 @@
package rsa
import (
"testing"
)
func TestEncrypt(t *testing.T) {
publicKey := `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1O3p0JN0/RrP7eY3f81i
zPf16FS0WMNGCJkd+y5c6yBzUvN0IEeoxiIWIBhoMKH0pzlzBg0rfttojSodOgNo
m/UCAzAYEgdIsNee5LSN/7e0T2/QvsIAHINuA8gI8fGoGiSA2TEzpUo6aVXwhZT3
4GGRdrSJ+m4iVk/Kt95tavBNk+NDVSeb5xAjxBchT5BjAMMlE0ffGZb0MMjjO5+e
9Tn8f99M2VMqpzXHXZzv1ABmqufzS20iWcSvnjhWcJ9hiKwO8Z30GgJyACmml+HM
xLYEFN9h2MWYgxLm9Z0rLMrWwMM+E2rCs8tsxAD5sO9RZMJPl1C0FIsMR53ngqbz
owIDAQAB
-----END PUBLIC KEY-----`
rsaPublic := NewPublic(publicKey)
str, err := rsaPublic.Encrypt("123456")
if err != nil {
t.Error("rsa public encrypt error", err)
return
}
t.Log(str)
}
func TestDecrypt(t *testing.T) {
privateKey := `-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEA1O3p0JN0/RrP7eY3f81izPf16FS0WMNGCJkd+y5c6yBzUvN0
IEeoxiIWIBhoMKH0pzlzBg0rfttojSodOgNom/UCAzAYEgdIsNee5LSN/7e0T2/Q
vsIAHINuA8gI8fGoGiSA2TEzpUo6aVXwhZT34GGRdrSJ+m4iVk/Kt95tavBNk+ND
VSeb5xAjxBchT5BjAMMlE0ffGZb0MMjjO5+e9Tn8f99M2VMqpzXHXZzv1ABmqufz
S20iWcSvnjhWcJ9hiKwO8Z30GgJyACmml+HMxLYEFN9h2MWYgxLm9Z0rLMrWwMM+
E2rCs8tsxAD5sO9RZMJPl1C0FIsMR53ngqbzowIDAQABAoIBAQCO1RE1ItUlO6kj
Un0ENAgEqojAUqGvsT33Yo7kAZO+/cOeb0UEqk0iq5bf7L9ncBynWDg6ZPc6X3/g
wdFdKxAvHck9zjM3VL+EMP+bNyrR0K8ZYk5Kx+Q/PEK+Mp8dfRdgggAUsZaNWB+a
rVVspiMo1wo28KBl5x8NevTnJkOLqXAyB7UyLWqnOL1fb988lZvZPR7ZUYroVIZa
pyXtZcafIJeKyQ3bvWI5+eFqOe61Z4Bx1+TpfZ3fKfSDW0vhxzNqaimOa8jSXtMJ
jMeOctL4nZ0TPo/jS3I+XlaH4ZQlFLuUWGscpxwfEeBN23I8HRLkZXJsw66yvRN3
s4bUKPXRAoGBAP/3oSZAECvfsYYzs76tnrAmR/0GxCqgguxDlWn5DowQzdWFOdHC
ZbTo/hUVoMSQnO1EKCFlnBS+wg/3TuIzUO0ewC1aeT7qHbOMDl0zKbNpS2Z9/j+U
zro+qz7XmkWolMCfmDrCrw9CtCxcMSII+ajbI8SAgFVMz9XnDt+xW9E9AoGBANT0
4F6kCUJTEyqf2+v84tjQ2wGIF6XtZPU9JR806zeMyahQ9F6z3hY8BYb0tIy5b3uJ
VlJ9TG1qg/t59TWxIq43mYSUJHe0aJi3ilooObQtHlhPu8nwmmX47sX0PyG2hMoD
kBVxTpTDmBaDz7O9uBnlMXJN5qEygctaixpEbmZfAoGBAMBA9kEMjRjnAyeRXcgy
D6aumhNqKZz6wltCx864yjxZwsBFOJBcOpgPCAg+HmqFU9jCAIJVF05dmNT1I8Ky
WG5BUoa+FaMzpOtenstRylh/Far9pyGKW1t4BpdEyRLY9CFZvbUk1OfZagqHlD/E
DgDN16eX/MwUzWYUDg/l3tjhAoGBAKGip/ZNjVWRFpggs9z/mfK1O7WC5Wgksp9N
ZLK2CN6l9p3RrFmBLk00C4HulGfHi+15RVLhFbRqx3iFje/N3iPbwaMWikNtZIKd
tN5Pb9To9gJTqpZRD+/cLOeFRrHBBjMK1z7fPKS/fN2B+JFVq7nD827t3+J0In4F
4FT0odMDAoGBAJk3ELB/FHY8xzZ4jF1wG/a1CK681Xm6SuU5KIELDSAUNoou6OPG
mS8gU20MMPAeV2z7khyDcSxlHsUyL73eLeaakbQov9NMW7cc99XX4wnP4W7FRpmr
QbHmKuHIRFHCFv+XX8c0aK2mDZMUlzJdy4FgD/YCEZ7kZMZKyvZW/ZuV
-----END RSA PRIVATE KEY-----`
decryptStr := "KTKXckjkCLI6Vk_y_XROnY-a6nJpllruL-CX-v_2AFxfghA2tZ2nkQyS6d1-IIYMlgwm4ivwlzy-phLtaN9BB03htA5D9rwjA_JwYtqAG4iwuvgaDl2SiZ_H2ACv-aV1kNRgnyjh14hs0JiSt5VHEiJ3D2xYzOCKwtEzoo0WczJ-MYb3u_-bfcnm9YtvgtG5-y3Jy7WYr-IwXdBKqPO0E-jzrtY7m3Q1yC4znHdzjNpxCj0I6YRx4CZ362b706qNX7sl3E5KTJeSmYrsurB-SxQT1CaqGzVt7mshx1v2qGnv5NBNXpj7ZPKWGJbgaCUxcuxd1Mg0o81HnfbsGuSlFQ=="
rsaPrivate := NewPrivate(privateKey)
str, err := rsaPrivate.Decrypt(decryptStr)
if err != nil {
t.Error("rsa private decrypt error", err)
return
}
t.Log(str)
}
+14
View File
@@ -0,0 +1,14 @@
package sql
import "strings"
// EscapeString 转义 SQL 语句中使用的字符串中的特殊字符
func EscapeString(value string) string {
replace := map[string]string{"\\": "\\\\", "'": `\'`, "\\0": "\\\\0", "\n": "\\n", "\r": "\\r", `"`: `\"`, "\x1a": "\\Z"}
for b, a := range replace {
value = strings.Replace(value, b, a, -1)
}
return value
}
+8
View File
@@ -0,0 +1,8 @@
package sql
import "testing"
func TestEscapeString(t *testing.T) {
str := "' OR ''=' union select 1,database(),2#"
t.Log(EscapeString(str))
}