From 106d5b65d71b25bc3342ff8c72f2daaff27bd219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B0=E4=BA=AE?= Date: Sat, 23 Jan 2021 16:46:01 +0800 Subject: [PATCH] upgrade --- README.md | 2 + configs/configs.go | 3 +- configs/fat_configs.toml | 53 ++--- docs/docs.go | 184 +++++++++-------- docs/swagger.json | 186 ++++++++++-------- docs/swagger.yaml | 129 +++++++----- go.mod | 1 - internal/api/code/code.go | 9 +- internal/api/controller/demo/demo.go | 104 +++++++--- internal/api/controller/user_handler/user.go | 115 ++++++----- internal/api/model/user_model/user.go | 15 +- .../db_repo/user_demo_repo/user_demo.go | 25 ++- .../api/router/middleware/{ => auth}/auth.go | 12 +- internal/api/router/router.go | 31 +-- internal/api/service/user_service/user.go | 19 +- .../cache_repo/init.go => pkg/cache/redis.go} | 82 +++++++- internal/pkg/core/context.go | 102 +++++----- internal/pkg/core/core.go | 25 ++- .../db_repo/init.go => pkg/db/mysql.go} | 2 +- .../repository/db_repo => pkg/db}/plugin.go | 6 +- internal/pkg/metrics/metrics.go | 38 ++-- internal/pkg/notify/notify.go | 13 +- internal/pkg/trace/dialog.go | 4 +- internal/pkg/trace/redis.go | 11 ++ internal/pkg/trace/sql.go | 2 +- internal/pkg/trace/trace.go | 54 +++-- main.go | 11 +- pkg/errno/errno.go | 21 +- pkg/httpclient/util.go | 6 +- pkg/logger/logger.go | 1 + pkg/logger/logger_test.go | 17 +- pkg/p/print.go | 8 +- pkg/rsa/rsa_test.go | 8 +- pkg/token/token.go | 9 +- pkg/token/token_test.go | 5 +- 35 files changed, 811 insertions(+), 502 deletions(-) rename internal/api/router/middleware/{ => auth}/auth.go (61%) rename internal/{api/repository/cache_repo/init.go => pkg/cache/redis.go} (56%) rename internal/{api/repository/db_repo/init.go => pkg/db/mysql.go} (99%) rename internal/{api/repository/db_repo => pkg/db}/plugin.go (96%) create mode 100644 internal/pkg/trace/redis.go diff --git a/README.md b/README.md index c34b535..7087969 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ - third_party_requests,当前请求涉及到调用第三方的信息 - debugs,当前请求的调试信息 - sqls,当前请求执行的 sql 信息 +- redis,当前请求执行的 redis 信息 - success,当前请求结果 - cost_seconds,执行时长,单位秒 @@ -60,6 +61,7 @@ - [x] 支持设置 third_party_requests 三方请求信息 - [x] 支持设置 debugs 打印调试信息 - [x] 支持设置 sqls 执行 SQL 信息 + - [x] 支持设置 redis 执行 Redis 信息 - [x] 可记录 cost_seconds 执行时长 - [x] [errno] 统一定义错误码 - [x] [env] 支持 FAT、UAT、PRO 环境 diff --git a/configs/configs.go b/configs/configs.go index df6f582..b929612 100644 --- a/configs/configs.go +++ b/configs/configs.go @@ -49,7 +49,8 @@ type Config struct { } `toml:"mail"` JWT struct { - Secret string `toml:"secret"` + Secret string `toml:"secret"` + ExpireDuration time.Duration `toml:"expireDuration"` } `toml:"jwt"` Aes struct { diff --git a/configs/fat_configs.toml b/configs/fat_configs.toml index 3498dc4..f3b881c 100644 --- a/configs/fat_configs.toml +++ b/configs/fat_configs.toml @@ -1,37 +1,38 @@ [mysql] -[mysql.read] - addr = '' - user = '' - pass = '' - name = '' -[mysql.write] - addr = '' - user = '' - pass = '' - name = '' -[mysql.base] - maxOpenConn = 10 - maxIdleConn = 60 - connMaxLifeTime = 60 +[mysql.read] # 从库信息,可读 + addr = '127.0.0.1:3306' # MySQL 地址:端口 + user = 'root' # 用户名 + pass = 'root' # 密码 + name = 'go_gin_api' # 数据库名称 +[mysql.write] # 主库信息,可读写 + addr = '127.0.0.1:3306' # MySQL 地址:端口 + user = 'root' # 用户名 + pass = 'root' # 密码 + name = 'go_gin_api' # 数据库名称 +[mysql.base] # 基础配置 + maxOpenConn = 10 # 最大打开的连接数 + maxIdleConn = 60 # 闲置的连接数 + connMaxLifeTime = 60 # 最大连接超时(单位:分) [redis] -addr = '' -pass = '' -db = 0 -maxRetries = 3 -poolSize = 10 -minIdleConns = 5 +addr = '127.0.0.1:6379' # Redis 地址:端口 +pass = '' # 密码 +db = 0 # 序号从 0 开始,默认是0,可以不用设置 +maxRetries = 3 # 命令执行失败时,最多重试多少次,默认为 0 即不重试 +poolSize = 10 # 连接池最大连接数,默认为 CPU 数 * 10 +minIdleConns = 5 # 最小空闲连接数 [mail] -host = 'smtp.163.com' -port = 465 -user = "" -pass = "" -to = "" +host = 'smtp.163.com' # 邮箱服务器,比如 smtp.163.com +port = 465 # 端口 +user = "" # 发件人邮箱 +pass = "" # 发件人邮箱密码或授权码,根据邮箱服务器而定 +to = "" # 收件人邮箱,多个可以逗号(,)分割 [jwt] -secret = 'i1ydX9RtHyuJTrw7frcu' +secret = 'i1ydX9RtHyuJTrw7frcu' # JWT secret +expireDuration = 24 # JWT ExpiresAt 过期时间(单位:小时) [aes] key = 'IgkibX71IEf382PT' diff --git a/docs/docs.go b/docs/docs.go index 05ff3e7..91afb3b 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -19,14 +19,18 @@ var doc = `{ "description": "{{.Description}}", "title": "{{.Title}}", "contact": {}, + "license": { + "name": "MIT", + "url": "https://github.com/xinliangnote/go-gin-api/blob/master/LICENSE" + }, "version": "{{.Version}}" }, "host": "{{.Host}}", "basePath": "{{.BasePath}}", "paths": { - "/demo/user/{name}": { - "get": { - "description": "获取用户信息", + "/auth/get": { + "post": { + "description": "获取授权信息", "consumes": [ "application/json" ], @@ -36,15 +40,31 @@ var doc = `{ "tags": [ "Demo" ], - "summary": "获取用户信息", + "summary": "获取授权信息", + "responses": { + "200": { + "description": "返回信息", + "schema": { + "$ref": "#/definitions/demo.authResponse" + } + } + } + } + }, + "/demo/trace": { + "get": { + "description": "Trace 示例", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Demo" + ], + "summary": "Trace 示例", "parameters": [ - { - "type": "string", - "description": "用户名(Tom)", - "name": "name", - "in": "path", - "required": true - }, { "type": "string", "description": "签名", @@ -86,7 +106,7 @@ var doc = `{ "application/json" ], "tags": [ - "Demo" + "User" ], "summary": "创建用户", "parameters": [ @@ -98,6 +118,13 @@ var doc = `{ "schema": { "$ref": "#/definitions/user_model.CreateRequest" } + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -110,6 +137,42 @@ var doc = `{ } } }, + "/user/delete/{id}": { + "patch": { + "description": "删除用户 - 更新 is_deleted = 1", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "删除用户 - 更新 is_deleted = 1", + "parameters": [ + { + "type": "integer", + "description": "用户ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "返回信息" + } + } + } + }, "/user/info/{username}": { "get": { "description": "用户详情", @@ -120,7 +183,7 @@ var doc = `{ "application/json" ], "tags": [ - "Demo" + "User" ], "summary": "用户详情", "parameters": [ @@ -130,6 +193,13 @@ var doc = `{ "name": "username", "in": "path", "required": true + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -142,43 +212,9 @@ var doc = `{ } } }, - "/user/login": { - "post": { - "description": "用户登录", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Demo" - ], - "summary": "用户登录", - "parameters": [ - { - "description": "请求信息", - "name": "RequestInfo", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/user_model.LoginRequest" - } - } - ], - "responses": { - "200": { - "description": "返回信息", - "schema": { - "$ref": "#/definitions/user_model.LoginResponse" - } - } - } - } - }, "/user/update": { - "post": { - "description": "更新用户名称", + "put": { + "description": "编辑用户 - 通过用户主键ID更新用户昵称", "consumes": [ "application/json" ], @@ -186,9 +222,9 @@ var doc = `{ "application/json" ], "tags": [ - "Demo" + "User" ], - "summary": "更新用户名称", + "summary": "编辑用户 - 通过用户主键ID更新用户昵称", "parameters": [ { "description": "请求信息", @@ -198,6 +234,13 @@ var doc = `{ "schema": { "$ref": "#/definitions/user_model.UpdateNickNameByIDRequest" } + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -212,6 +255,19 @@ var doc = `{ } }, "definitions": { + "demo.authResponse": { + "type": "object", + "properties": { + "authorization": { + "description": "签名", + "type": "string" + }, + "expire_time": { + "description": "过期时间", + "type": "integer" + } + } + }, "user_model.CreateRequest": { "type": "object", "properties": { @@ -259,32 +315,6 @@ var doc = `{ } } }, - "user_model.LoginRequest": { - "type": "object", - "properties": { - "user_id": { - "description": "用户ID(\u003e0)", - "type": "integer" - }, - "user_name": { - "description": "用户名", - "type": "string" - } - } - }, - "user_model.LoginResponse": { - "type": "object", - "properties": { - "authorization": { - "description": "签名", - "type": "string" - }, - "expire_time": { - "description": "过期时间", - "type": "integer" - } - } - }, "user_model.UpdateNickNameByIDRequest": { "type": "object", "properties": { diff --git a/docs/swagger.json b/docs/swagger.json index feb764c..6ebbf82 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -2,13 +2,17 @@ "swagger": "2.0", "info": { "title": "go-gin-api docs api", - "contact": {} + "contact": {}, + "license": { + "name": "MIT", + "url": "https://github.com/xinliangnote/go-gin-api/blob/master/LICENSE" + } }, "host": "127.0.0.1:9999", "paths": { - "/demo/user/{name}": { - "get": { - "description": "获取用户信息", + "/auth/get": { + "post": { + "description": "获取授权信息", "consumes": [ "application/json" ], @@ -18,15 +22,31 @@ "tags": [ "Demo" ], - "summary": "获取用户信息", + "summary": "获取授权信息", + "responses": { + "200": { + "description": "返回信息", + "schema": { + "$ref": "#/definitions/demo.authResponse" + } + } + } + } + }, + "/demo/trace": { + "get": { + "description": "Trace 示例", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Demo" + ], + "summary": "Trace 示例", "parameters": [ - { - "type": "string", - "description": "用户名(Tom)", - "name": "name", - "in": "path", - "required": true - }, { "type": "string", "description": "签名", @@ -68,7 +88,7 @@ "application/json" ], "tags": [ - "Demo" + "User" ], "summary": "创建用户", "parameters": [ @@ -80,6 +100,13 @@ "schema": { "$ref": "#/definitions/user_model.CreateRequest" } + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -92,6 +119,42 @@ } } }, + "/user/delete/{id}": { + "patch": { + "description": "删除用户 - 更新 is_deleted = 1", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "User" + ], + "summary": "删除用户 - 更新 is_deleted = 1", + "parameters": [ + { + "type": "integer", + "description": "用户ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "返回信息" + } + } + } + }, "/user/info/{username}": { "get": { "description": "用户详情", @@ -102,7 +165,7 @@ "application/json" ], "tags": [ - "Demo" + "User" ], "summary": "用户详情", "parameters": [ @@ -112,6 +175,13 @@ "name": "username", "in": "path", "required": true + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -124,43 +194,9 @@ } } }, - "/user/login": { - "post": { - "description": "用户登录", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "Demo" - ], - "summary": "用户登录", - "parameters": [ - { - "description": "请求信息", - "name": "RequestInfo", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/user_model.LoginRequest" - } - } - ], - "responses": { - "200": { - "description": "返回信息", - "schema": { - "$ref": "#/definitions/user_model.LoginResponse" - } - } - } - } - }, "/user/update": { - "post": { - "description": "更新用户名称", + "put": { + "description": "编辑用户 - 通过用户主键ID更新用户昵称", "consumes": [ "application/json" ], @@ -168,9 +204,9 @@ "application/json" ], "tags": [ - "Demo" + "User" ], - "summary": "更新用户名称", + "summary": "编辑用户 - 通过用户主键ID更新用户昵称", "parameters": [ { "description": "请求信息", @@ -180,6 +216,13 @@ "schema": { "$ref": "#/definitions/user_model.UpdateNickNameByIDRequest" } + }, + { + "type": "string", + "description": "签名", + "name": "Authorization", + "in": "header", + "required": true } ], "responses": { @@ -194,6 +237,19 @@ } }, "definitions": { + "demo.authResponse": { + "type": "object", + "properties": { + "authorization": { + "description": "签名", + "type": "string" + }, + "expire_time": { + "description": "过期时间", + "type": "integer" + } + } + }, "user_model.CreateRequest": { "type": "object", "properties": { @@ -241,32 +297,6 @@ } } }, - "user_model.LoginRequest": { - "type": "object", - "properties": { - "user_id": { - "description": "用户ID(\u003e0)", - "type": "integer" - }, - "user_name": { - "description": "用户名", - "type": "string" - } - } - }, - "user_model.LoginResponse": { - "type": "object", - "properties": { - "authorization": { - "description": "签名", - "type": "string" - }, - "expire_time": { - "description": "过期时间", - "type": "integer" - } - } - }, "user_model.UpdateNickNameByIDRequest": { "type": "object", "properties": { diff --git a/docs/swagger.yaml b/docs/swagger.yaml index c766813..308eea0 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,13 @@ definitions: + demo.authResponse: + properties: + authorization: + description: 签名 + type: string + expire_time: + description: 过期时间 + type: integer + type: object user_model.CreateRequest: properties: mobile: @@ -32,24 +41,6 @@ definitions: description: 用户名 type: string type: object - user_model.LoginRequest: - properties: - user_id: - description: 用户ID(>0) - type: integer - user_name: - description: 用户名 - type: string - type: object - user_model.LoginResponse: - properties: - authorization: - description: 签名 - type: string - expire_time: - description: 过期时间 - type: integer - type: object user_model.UpdateNickNameByIDRequest: properties: id: @@ -68,19 +59,32 @@ definitions: host: 127.0.0.1:9999 info: contact: {} + license: + name: MIT + url: https://github.com/xinliangnote/go-gin-api/blob/master/LICENSE title: go-gin-api docs api paths: - /demo/user/{name}: + /auth/get: + post: + consumes: + - application/json + description: 获取授权信息 + produces: + - application/json + responses: + "200": + description: 返回信息 + schema: + $ref: '#/definitions/demo.authResponse' + summary: 获取授权信息 + tags: + - Demo + /demo/trace: get: consumes: - application/json - description: 获取用户信息 + description: Trace 示例 parameters: - - description: 用户名(Tom) - in: path - name: name - required: true - type: string - description: 签名 in: header name: Authorization @@ -102,7 +106,7 @@ paths: type: string type: object type: array - summary: 获取用户信息 + summary: Trace 示例 tags: - Demo /user/create: @@ -117,6 +121,11 @@ paths: required: true schema: $ref: '#/definitions/user_model.CreateRequest' + - description: 签名 + in: header + name: Authorization + required: true + type: string produces: - application/json responses: @@ -126,7 +135,31 @@ paths: $ref: '#/definitions/user_model.CreateResponse' summary: 创建用户 tags: - - Demo + - User + /user/delete/{id}: + patch: + consumes: + - application/json + description: 删除用户 - 更新 is_deleted = 1 + parameters: + - description: 用户ID + in: path + name: id + required: true + type: integer + - description: 签名 + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: 返回信息 + summary: 删除用户 - 更新 is_deleted = 1 + tags: + - User /user/info/{username}: get: consumes: @@ -138,6 +171,11 @@ paths: name: username required: true type: string + - description: 签名 + in: header + name: Authorization + required: true + type: string produces: - application/json responses: @@ -147,34 +185,12 @@ paths: $ref: '#/definitions/user_model.DetailResponse' summary: 用户详情 tags: - - Demo - /user/login: - post: - consumes: - - application/json - description: 用户登录 - parameters: - - description: 请求信息 - in: body - name: RequestInfo - required: true - schema: - $ref: '#/definitions/user_model.LoginRequest' - produces: - - application/json - responses: - "200": - description: 返回信息 - schema: - $ref: '#/definitions/user_model.LoginResponse' - summary: 用户登录 - tags: - - Demo + - User /user/update: - post: + put: consumes: - application/json - description: 更新用户名称 + description: 编辑用户 - 通过用户主键ID更新用户昵称 parameters: - description: 请求信息 in: body @@ -182,6 +198,11 @@ paths: required: true schema: $ref: '#/definitions/user_model.UpdateNickNameByIDRequest' + - description: 签名 + in: header + name: Authorization + required: true + type: string produces: - application/json responses: @@ -189,7 +210,7 @@ paths: description: 返回信息 schema: $ref: '#/definitions/user_model.UpdateNickNameByIDResponse' - summary: 更新用户名称 + summary: 编辑用户 - 通过用户主键ID更新用户昵称 tags: - - Demo + - User swagger: "2.0" diff --git a/go.mod b/go.mod index d8cf85e..0b2aca9 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.15 require ( github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 - github.com/davecgh/go-spew v1.1.1 github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/gin-contrib/pprof v1.2.1 github.com/gin-gonic/gin v1.6.3 diff --git a/internal/api/code/code.go b/internal/api/code/code.go index 339077e..b69a82b 100644 --- a/internal/api/code/code.go +++ b/internal/api/code/code.go @@ -18,10 +18,11 @@ var ( // 模块级错误码 - 用户模块 ErrUser = errno.NewError(http.StatusBadRequest, 20101, "非法用户") - ErrUserCreate = errno.NewError(http.StatusBadRequest, 20102, "创建用户失败") - ErrUserUpdate = errno.NewError(http.StatusBadRequest, 20103, "更新用户失败") - ErrUserSearch = errno.NewError(http.StatusBadRequest, 20104, "查询用户失败") - ErrUserHTTP = errno.NewError(http.StatusBadRequest, 20105, "调用他方接口失败") + ErrUserName = errno.NewError(http.StatusBadRequest, 20102, "账号不能为空") + ErrUserCreate = errno.NewError(http.StatusBadRequest, 20103, "创建用户失败") + ErrUserUpdate = errno.NewError(http.StatusBadRequest, 20104, "更新用户失败") + ErrUserSearch = errno.NewError(http.StatusBadRequest, 20105, "查询用户失败") + ErrUserHTTP = errno.NewError(http.StatusBadRequest, 20106, "调用他方接口失败") // ... ) diff --git a/internal/api/controller/demo/demo.go b/internal/api/controller/demo/demo.go index c339d9e..91f1718 100644 --- a/internal/api/controller/demo/demo.go +++ b/internal/api/controller/demo/demo.go @@ -3,21 +3,32 @@ package demo import ( "time" + "github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/internal/api/code" "github.com/xinliangnote/go-gin-api/internal/api/repository/third_party_request/go_gin_api_repo" + "github.com/xinliangnote/go-gin-api/internal/api/service/user_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" "github.com/xinliangnote/go-gin-api/pkg/httpclient" + "github.com/xinliangnote/go-gin-api/pkg/p" + "github.com/xinliangnote/go-gin-api/pkg/token" + "github.com/pkg/errors" "go.uber.org/zap" ) type Demo struct { - logger *zap.Logger + logger *zap.Logger + cache cache.Repo + userService user_service.UserService } -func NewDemo(logger *zap.Logger) *Demo { +func NewDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) *Demo { return &Demo{ - logger: logger, + logger: logger, + cache: cache, + userService: user_service.NewUserService(db, cache), } } @@ -35,16 +46,16 @@ func (d *Demo) Get() core.HandlerFunc { return func(c core.Context) { req := new(request) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(code.ErrParamBind) + c.AbortWithError(code.ErrParamBind.WithErr(err)) return } if req.Name != "Tom" { - c.AbortWithError(code.ErrUser) + c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Tom"))) return } - c.SetPayload(code.OK.WithData(&response{ + c.Payload(code.OK.WithData(&response{ Name: "Tom", Job: "Student", })) @@ -64,56 +75,70 @@ func (d *Demo) Post() core.HandlerFunc { return func(c core.Context) { req := new(request) if err := c.ShouldBindPostForm(req); err != nil { - c.AbortWithError(code.ErrParamBind) + c.AbortWithError(code.ErrParamBind.WithErr(err)) return } if req.Name != "Jack" { - c.AbortWithError(code.ErrUser) + c.AbortWithError(code.ErrUser.WithErr(errors.New("req.Name != Jack"))) return } - c.SetPayload(code.OK.WithData(&response{ + c.Payload(code.OK.WithData(&response{ Name: "Jack", Job: "Teacher", })) } } -type request struct { - Name string `uri:"name"` +type authResponse struct { + Authorization string `json:"authorization"` // 签名 + ExpireTime int64 `json:"expire_time"` // 过期时间 } -type response []struct { +type traceResponse []struct { Name string `json:"name"` //用户名 Job string `json:"job"` //工作 } -// Get 获取用户信息 -// @Summary 获取用户信息 -// @Description 获取用户信息 +// 获取授权信息 +// @Summary 获取授权信息 +// @Description 获取授权信息 // @Tags Demo // @Accept json // @Produce json -// @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 { +// @Success 200 {object} authResponse "返回信息" +// @Router /auth/get [post] +func (d *Demo) Auth() core.HandlerFunc { return func(c core.Context) { - req := new(request) - if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(code.ErrParamBind) + cfg := configs.Get().JWT + tokenString, err := token.New(cfg.Secret).Sign(1, "xinliangnote", time.Hour*cfg.ExpireDuration) + if err != nil { + c.AbortWithError(code.ErrAuthorization.WithErr(err)) return } - if req.Name != "Tom" { - c.AbortWithError(code.ErrUser) - return - } + res := new(authResponse) + res.Authorization = tokenString + res.ExpireTime = time.Now().Add(time.Hour * cfg.ExpireDuration).Unix() + c.Payload(code.OK.WithData(res)) + } +} - res1, err := go_gin_api_repo.DemoGet(req.Name, +// Trace 示例 +// @Summary Trace 示例 +// @Description Trace 示例 +// @Tags Demo +// @Accept json +// @Produce json +// @Param Authorization header string true "签名" +// @Success 200 {object} traceResponse "用户信息" +// @Router /demo/trace [get] +func (d *Demo) Trace() core.HandlerFunc { + return func(c core.Context) { + // 三方请求信息 + res1, err := go_gin_api_repo.DemoGet("Tom", httpclient.WithTTL(time.Second*5), httpclient.WithTrace(c.Trace()), httpclient.WithLogger(c.Logger()), @@ -123,10 +148,14 @@ func (d *Demo) User() core.HandlerFunc { if err != nil { d.logger.Error("get [demo/get] err", zap.Error(err)) - c.AbortWithError(code.ErrUserHTTP) + c.AbortWithError(code.ErrUserHTTP.WithErr(err)) return } + // 调试信息 + p.Println("res1.Data.Name", res1.Data.Name, p.WithTrace(c.Trace())) + + // 三方请求信息 res2, err := go_gin_api_repo.DemoPost("Jack", httpclient.WithTTL(time.Second*5), httpclient.WithTrace(c.Trace()), @@ -137,11 +166,22 @@ func (d *Demo) User() core.HandlerFunc { if err != nil { d.logger.Error("post [demo/post] err", zap.Error(err)) - c.AbortWithError(code.ErrUserHTTP) + c.AbortWithError(code.ErrUserHTTP.WithErr(err)) return } - data := &response{ + // 调试信息 + p.Println("res2.Data.Name", res2.Data.Name, p.WithTrace(c.Trace())) + + // 执行 SQL 信息 + d.userService.GetUserByUserName(c, "test_user") + + // 执行 Redis 信息 + _ = d.cache.Set("name", "tom", time.Minute*10, cache.WithTrace(c.Trace())) + val, _ := d.cache.Get("name", cache.WithTrace(c.Trace())) + p.Println("redis-name", val, p.WithTrace(c.Trace())) + + data := &traceResponse{ { Name: res1.Data.Name, Job: res1.Data.Job, @@ -151,6 +191,6 @@ func (d *Demo) User() core.HandlerFunc { Job: res2.Data.Job, }, } - c.SetPayload(code.OK.WithData(data)) + c.Payload(code.OK.WithData(data)) } } diff --git a/internal/api/controller/user_handler/user.go b/internal/api/controller/user_handler/user.go index 0f5b350..d4831a3 100644 --- a/internal/api/controller/user_handler/user.go +++ b/internal/api/controller/user_handler/user.go @@ -1,14 +1,14 @@ package user_handler import ( - "github.com/xinliangnote/go-gin-api/configs" + "errors" + "github.com/xinliangnote/go-gin-api/internal/api/code" "github.com/xinliangnote/go-gin-api/internal/api/model/user_model" - "github.com/xinliangnote/go-gin-api/internal/api/repository/cache_repo" - "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" "github.com/xinliangnote/go-gin-api/internal/api/service/user_service" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/token" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" "go.uber.org/zap" ) @@ -18,24 +18,30 @@ var _ UserDemo = (*userDemo)(nil) type UserDemo interface { // i 为了避免被其他包实现 i() - // 创建用户 + + // Create 创建用户 Create() core.HandlerFunc - // 通过用户主键ID更新用户昵称 + + // UpdateNickNameByID 编辑用户 - 通过主键ID更新用户昵称 UpdateNickNameByID() core.HandlerFunc - // 用户登录 - Login() core.HandlerFunc - // 用户详情 + + // Delete 删除用户 - 通过主键ID更新 is_deleted = 1 + Delete() core.HandlerFunc + + // Detail 用户详情 Detail() core.HandlerFunc } type userDemo struct { logger *zap.Logger + cache cache.Repo userService user_service.UserService } -func NewUserDemo(logger *zap.Logger, db db_repo.Repo, cache cache_repo.Repo) UserDemo { +func NewUserDemo(logger *zap.Logger, db db.Repo, cache cache.Repo) UserDemo { return &userDemo{ logger: logger, + cache: cache, userService: user_service.NewUserService(db, cache), } } @@ -45,10 +51,11 @@ func (u *userDemo) i() {} // 创建用户 // @Summary 创建用户 // @Description 创建用户 -// @Tags Demo +// @Tags User // @Accept json // @Produce json // @Param RequestInfo body user_model.CreateRequest true "请求信息" +// @Param Authorization header string true "签名" // @Success 200 {object} user_model.CreateResponse "返回信息" // @Router /user/create [post] func (u *userDemo) Create() core.HandlerFunc { @@ -56,102 +63,92 @@ func (u *userDemo) Create() core.HandlerFunc { req := new(user_model.CreateRequest) res := new(user_model.CreateResponse) if err := c.ShouldBindJSON(req); err != nil { - u.logger.Error("[user] should bind json err", zap.Error(err)) - c.AbortWithError(code.ErrParamBind) + c.AbortWithError(code.ErrParamBind.WithErr(err)) + return + } + + if req.UserName == "" { + c.AbortWithError(code.ErrUserName.WithErr(errors.New("req.UserName = ''"))) return } id, err := u.userService.Create(c, req) if err != nil { - u.logger.Error("[user] Create err", zap.Error(err)) - c.AbortWithError(code.ErrUserCreate) + c.AbortWithError(code.ErrUserCreate.WithErr(err)) return } res.Id = id - c.SetPayload(code.OK.WithData(res)) + c.Payload(code.OK.WithData(res)) } } -// 更新用户名称 -// @Summary 更新用户名称 -// @Description 更新用户名称 -// @Tags Demo +// 编辑用户 - 通过用户主键ID更新用户昵称 +// @Summary 编辑用户 - 通过用户主键ID更新用户昵称 +// @Description 编辑用户 - 通过用户主键ID更新用户昵称 +// @Tags User // @Accept json // @Produce json // @Param RequestInfo body user_model.UpdateNickNameByIDRequest true "请求信息" +// @Param Authorization header string true "签名" // @Success 200 {object} user_model.UpdateNickNameByIDResponse "返回信息" -// @Router /user/update [post] +// @Router /user/update [put] func (u *userDemo) UpdateNickNameByID() core.HandlerFunc { return func(c core.Context) { req := new(user_model.UpdateNickNameByIDRequest) res := new(user_model.UpdateNickNameByIDResponse) if err := c.ShouldBindJSON(req); err != nil { - u.logger.Error("[user] should bind json err", zap.Error(err)) - c.AbortWithError(code.ErrParamBind) + c.AbortWithError(code.ErrParamBind.WithErr(err)) return } err := u.userService.UpdateNickNameByID(c, req.Id, req.NickName) if err != nil { - u.logger.Error("[user] UpdateNickNameByID err", zap.Error(err)) - c.AbortWithError(code.ErrUserUpdate) + c.AbortWithError(code.ErrUserUpdate.WithErr(err)) return } res.Id = req.Id - c.SetPayload(code.OK.WithData(res)) + c.Payload(code.OK.WithData(res)) } } -// 用户登录 -// @Summary 用户登录 -// @Description 用户登录 -// @Tags Demo +// 删除用户 - 更新 is_deleted = 1 +// @Summary 删除用户 - 更新 is_deleted = 1 +// @Description 删除用户 - 更新 is_deleted = 1 +// @Tags User // @Accept json // @Produce json -// @Param RequestInfo body user_model.LoginRequest true "请求信息" -// @Success 200 {object} user_model.LoginResponse "返回信息" -// @Router /user/login [post] -func (u *userDemo) Login() core.HandlerFunc { +// @Param id path int true "用户ID" +// @Param Authorization header string true "签名" +// @Success 200 "返回信息" +// @Router /user/delete/{id} [patch] +func (u *userDemo) Delete() core.HandlerFunc { return func(c core.Context) { - req := new(user_model.LoginRequest) - res := new(user_model.LoginResponse) - if err := c.ShouldBindJSON(req); err != nil { - u.logger.Error("should bind json err", zap.Error(err)) - c.AbortWithError(code.ErrParamBind) + req := new(user_model.DeleteRequest) + if err := c.ShouldBindURI(req); err != nil { + c.AbortWithError(code.ErrParamBind.WithErr(err)) return } - cfg := configs.Get().JWT - tokenString, err := token.New(cfg.Secret).Sign(req.UserID, req.UserName) + err := u.userService.Delete(c, req.Id) if err != nil { - u.logger.Error("token sign err", zap.Error(err)) - c.AbortWithError(code.ErrAuthorization) + c.AbortWithError(code.ErrUserUpdate.WithErr(err)) return } - claims, err := token.New(cfg.Secret).Parse(tokenString) - if err != nil { - u.logger.Error("token parse err", zap.Error(err)) - c.AbortWithError(code.ErrAuthorization) - return - } - - res.Authorization = tokenString - res.ExpireTime = claims.ExpiresAt - - c.SetPayload(code.OK.WithData(res)) + c.Payload(code.OK.WithData(nil)) } } // 用户详情 // @Summary 用户详情 // @Description 用户详情 -// @Tags Demo +// @Tags User // @Accept json // @Produce json // @Param username path string true "用户名" +// @Param Authorization header string true "签名" // @Success 200 {object} user_model.DetailResponse "返回信息" // @Router /user/info/{username} [get] func (u *userDemo) Detail() core.HandlerFunc { @@ -159,15 +156,13 @@ func (u *userDemo) Detail() core.HandlerFunc { req := new(user_model.DetailRequest) res := new(user_model.DetailResponse) if err := c.ShouldBindURI(req); err != nil { - u.logger.Error("should bind uri err", zap.Error(err)) - c.AbortWithError(code.ErrParamBind) + c.AbortWithError(code.ErrParamBind.WithErr(err)) return } user, err := u.userService.GetUserByUserName(c, req.UserName) if err != nil { - u.logger.Error("[user] GetUserByUserName err", zap.Error(err)) - c.AbortWithError(code.ErrUserSearch) + c.AbortWithError(code.ErrUserSearch.WithErr(err)) return } @@ -175,6 +170,6 @@ func (u *userDemo) Detail() core.HandlerFunc { res.UserName = user.UserName res.NickName = user.NickName res.Mobile = user.Mobile - c.SetPayload(code.OK.WithData(res)) + c.Payload(code.OK.WithData(res)) } } diff --git a/internal/api/model/user_model/user.go b/internal/api/model/user_model/user.go index 961af32..8f71169 100644 --- a/internal/api/model/user_model/user.go +++ b/internal/api/model/user_model/user.go @@ -28,7 +28,7 @@ type CreateRequest struct { // user_handler Create Response type CreateResponse struct { - Id uint `json:"id"` //主键ID + Id uint `json:"id"` // 主键ID } // user_handler UpdateNickNameByID Request @@ -42,16 +42,9 @@ type UpdateNickNameByIDResponse struct { Id uint `json:"id"` // 用户主键ID } -// user_handler Login Request -type LoginRequest struct { - UserID int `json:"user_id" form:"user_id"` // 用户ID(>0) - UserName string `json:"user_name" form:"user_name"` // 用户名 -} - -// user_handler Login Response -type LoginResponse struct { - Authorization string `json:"authorization"` // 签名 - ExpireTime int64 `json:"expire_time"` // 过期时间 +// user_handler Delete Request +type DeleteRequest struct { + Id uint `uri:"id"` // 用户ID } // user_handler Detail Request diff --git a/internal/api/repository/db_repo/user_demo_repo/user_demo.go b/internal/api/repository/db_repo/user_demo_repo/user_demo.go index a57fe89..4595812 100644 --- a/internal/api/repository/db_repo/user_demo_repo/user_demo.go +++ b/internal/api/repository/db_repo/user_demo_repo/user_demo.go @@ -2,8 +2,8 @@ package user_demo_repo import ( "github.com/xinliangnote/go-gin-api/internal/api/model/user_model" - "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" "github.com/pkg/errors" ) @@ -16,14 +16,15 @@ type UserRepo interface { Create(ctx core.Context, user user_model.UserDemo) (id uint, err error) UpdateNickNameByID(ctx core.Context, id uint, username string) (err error) GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error) + Delete(ctx core.Context, id uint) (err error) getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error) } type userRepo struct { - db db_repo.Repo + db db.Repo } -func NewUserRepo(db db_repo.Repo) UserRepo { +func NewUserRepo(db db.Repo) UserRepo { return &userRepo{ db: db, } @@ -32,7 +33,7 @@ func NewUserRepo(db db_repo.Repo) UserRepo { func (u *userRepo) i() {} func (u *userRepo) Create(ctx core.Context, user user_model.UserDemo) (id uint, err error) { - err = u.db.GetDbW().WithContext(ctx).Create(&user).Error + err = u.db.GetDbW().WithContext(ctx.RequestContext()).Create(&user).Error if err != nil { return 0, errors.Wrap(err, "[user_repo] create user err") } @@ -41,7 +42,7 @@ func (u *userRepo) Create(ctx core.Context, user user_model.UserDemo) (id uint, func (u *userRepo) getUserByID(ctx core.Context, id uint) (*user_model.UserDemo, error) { data := new(user_model.UserDemo) - err := u.db.GetDbR().WithContext(ctx).First(data, id).Error + err := u.db.GetDbR().WithContext(ctx.RequestContext()).First(data, id).Where("is_deleted = ?", -1).Error if err != nil { return nil, errors.Wrap(err, "[user_demo] get user data err") } @@ -53,15 +54,23 @@ func (u *userRepo) UpdateNickNameByID(ctx core.Context, id uint, nickname string if err != nil { return errors.Wrap(err, "[user_demo] update user data err") } - return u.db.GetDbW().WithContext(ctx).Model(user).Update("nick_name", nickname).Error + return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("nick_name", nickname).Error +} + +func (u *userRepo) Delete(ctx core.Context, id uint) (err error) { + user, err := u.getUserByID(ctx, id) + if err != nil { + return errors.Wrap(err, "[user_demo] update user data err") + } + return u.db.GetDbW().WithContext(ctx.RequestContext()).Model(user).Update("is_deleted", 1).Error } func (u *userRepo) GetUserByUserName(ctx core.Context, username string) (*user_model.UserDemo, error) { data := new(user_model.UserDemo) err := u.db.GetDbR(). - WithContext(ctx). + WithContext(ctx.RequestContext()). Select([]string{"id", "user_name", "nick_name", "mobile"}). - Where("user_name = ?", username). + Where("user_name = ? and is_deleted = ?", username, -1). First(data).Error if err != nil { return nil, errors.Wrap(err, "[user_demo] get user data err") diff --git a/internal/api/router/middleware/auth.go b/internal/api/router/middleware/auth/auth.go similarity index 61% rename from internal/api/router/middleware/auth.go rename to internal/api/router/middleware/auth/auth.go index 7ded664..38b3633 100644 --- a/internal/api/router/middleware/auth.go +++ b/internal/api/router/middleware/auth/auth.go @@ -1,4 +1,4 @@ -package middleware +package auth import ( "github.com/xinliangnote/go-gin-api/configs" @@ -6,25 +6,27 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/token" + + "github.com/pkg/errors" ) -func AuthHandler(ctx core.Context) (userId int, userName string, err errno.Error) { +func AuthHandler(ctx core.Context) (userId int64, userName string, err errno.Error) { auth := ctx.GetHeader("Authorization") if auth == "" { - err = code.ErrAuthorization + err = code.ErrAuthorization.WithErr(errors.New("Header 中缺少 Authorization 参数")) return } cfg := configs.Get().JWT claims, errParse := token.New(cfg.Secret).Parse(auth) if errParse != nil { - err = code.ErrAuthorization + err = code.ErrAuthorization.WithErr(errParse) return } userId = claims.UserID if userId <= 0 { - err = code.ErrAuthorization + err = code.ErrAuthorization.WithErr(errors.New("claims.UserID <= 0 ")) return } userName = claims.UserName diff --git a/internal/api/router/router.go b/internal/api/router/router.go index 5afa24f..ef9c795 100644 --- a/internal/api/router/router.go +++ b/internal/api/router/router.go @@ -3,10 +3,10 @@ package router import ( "github.com/xinliangnote/go-gin-api/internal/api/controller/demo" "github.com/xinliangnote/go-gin-api/internal/api/controller/user_handler" - "github.com/xinliangnote/go-gin-api/internal/api/repository/cache_repo" - "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" - auth "github.com/xinliangnote/go-gin-api/internal/api/router/middleware" + "github.com/xinliangnote/go-gin-api/internal/api/router/middleware/auth" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" "github.com/xinliangnote/go-gin-api/internal/pkg/metrics" "github.com/xinliangnote/go-gin-api/internal/pkg/notify" @@ -14,7 +14,7 @@ import ( "go.uber.org/zap" ) -func NewHTTPMux(logger *zap.Logger, db db_repo.Repo, cache cache_repo.Repo) (core.Mux, error) { +func NewHTTPMux(logger *zap.Logger, db db.Repo, cache cache.Repo) (core.Mux, error) { if logger == nil { return nil, errors.New("logger required") @@ -31,20 +31,29 @@ func NewHTTPMux(logger *zap.Logger, db db_repo.Repo, cache cache_repo.Repo) (cor panic(err) } - demoHandler := demo.NewDemo(logger) + demoHandler := demo.NewDemo(logger, db, cache) userHandler := user_handler.NewUserDemo(logger, db, cache) - u := mux.Group("/user") + // user_demo CURD + user := mux.Group("/user", core.WrapAuthHandler(auth.AuthHandler)) { - u.POST("/login", userHandler.Login()) - u.POST("/create", userHandler.Create()) - u.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail()) - u.POST("/update", userHandler.UpdateNickNameByID()) + user.POST("/create", userHandler.Create()) + user.PUT("/update", userHandler.UpdateNickNameByID()) + user.PATCH("/delete/:id", userHandler.Delete()) + user.GET("/info/:username", core.AliasForRecordMetrics("/user/info"), userHandler.Detail()) } + // auth + a := mux.Group("/auth") + { + a.POST("/get", demoHandler.Auth()) + } + + // demo d := mux.Group("/demo", core.WrapAuthHandler(auth.AuthHandler)) //使用 auth 验证 { - d.GET("user/:name", core.AliasForRecordMetrics("/demo/user"), demoHandler.User()) + // 为了演示 Trace ,增加了一些看起来无意义的调试信息和 SQL 信息。 + d.GET("/trace", demoHandler.Trace()) // 模拟数据 d.GET("get/:name", core.AliasForRecordMetrics("/demo/get"), demoHandler.Get()) diff --git a/internal/api/service/user_service/user.go b/internal/api/service/user_service/user.go index 09aa152..84f8204 100644 --- a/internal/api/service/user_service/user.go +++ b/internal/api/service/user_service/user.go @@ -2,10 +2,10 @@ package user_service import ( "github.com/xinliangnote/go-gin-api/internal/api/model/user_model" - "github.com/xinliangnote/go-gin-api/internal/api/repository/cache_repo" - "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo/user_demo_repo" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" ) var _ UserService = (*userSer)(nil) @@ -17,15 +17,16 @@ type UserService interface { Create(ctx core.Context, user *user_model.CreateRequest) (id uint, err error) UpdateNickNameByID(ctx core.Context, id uint, username string) (err error) GetUserByUserName(ctx core.Context, username string) (user *user_model.UserDemo, err error) + Delete(ctx core.Context, id uint) (err error) } type userSer struct { - db db_repo.Repo - cache cache_repo.Repo + db db.Repo + cache cache.Repo userRepo user_demo_repo.UserRepo } -func NewUserService(db db_repo.Repo, cache cache_repo.Repo) UserService { +func NewUserService(db db.Repo, cache cache.Repo) UserService { userRepo := user_demo_repo.NewUserRepo(db) return &userSer{ db: db, @@ -65,3 +66,11 @@ func (u *userSer) GetUserByUserName(ctx core.Context, username string) (user *us } return user, nil } + +func (u *userSer) Delete(ctx core.Context, id uint) (err error) { + err = u.userRepo.Delete(ctx, id) + if err != nil { + return nil + } + return nil +} diff --git a/internal/api/repository/cache_repo/init.go b/internal/pkg/cache/redis.go similarity index 56% rename from internal/api/repository/cache_repo/init.go rename to internal/pkg/cache/redis.go index f16d60c..3a0d107 100644 --- a/internal/api/repository/cache_repo/init.go +++ b/internal/pkg/cache/redis.go @@ -1,25 +1,40 @@ -package cache_repo +package cache import ( "time" "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/pkg/trace" + "github.com/xinliangnote/go-gin-api/pkg/time_parse" "github.com/go-redis/redis/v7" "github.com/pkg/errors" ) +type Option func(*option) + +type Trace = trace.T + +type option struct { + Trace *trace.Trace + Redis *trace.Redis +} + +func newOption() *option { + return &option{} +} + var _ Repo = (*cacheRepo)(nil) type Repo interface { i() - Set(key, value string, ttl time.Duration) error - Get(key string) (string, error) + Set(key, value string, ttl time.Duration, options ...Option) error + Get(key string, options ...Option) (string, error) TTL(key string) (time.Duration, error) Expire(key string, ttl time.Duration) bool ExpireAt(key string, ttl time.Time) bool Del(keys ...string) bool - Incr(key string) int64 + Incr(key string, options ...Option) int64 Close() } @@ -59,7 +74,23 @@ func redisConnect() (*redis.Client, error) { } // Set set some into redis -func (c *cacheRepo) Set(key, value string, ttl time.Duration) error { +func (c *cacheRepo) Set(key, value string, ttl time.Duration, options ...Option) error { + opt := newOption() + defer func() { + if opt.Trace != nil { + opt.Redis.Timestamp = time_parse.CSTLayoutString() + opt.Redis.Handle = "set" + opt.Redis.Key = key + opt.Redis.Value = value + opt.Redis.TTL = ttl + opt.Trace.AppendRedis(opt.Redis) + } + }() + + for _, f := range options { + f(opt) + } + if err := c.client.Set(key, value, ttl).Err(); err != nil { return errors.Wrapf(err, "redis set key: %s err", key) } @@ -68,7 +99,21 @@ func (c *cacheRepo) Set(key, value string, ttl time.Duration) error { } // Get get some key from redis -func (c *cacheRepo) Get(key string) (string, error) { +func (c *cacheRepo) Get(key string, options ...Option) (string, error) { + opt := newOption() + defer func() { + if opt.Trace != nil { + opt.Redis.Timestamp = time_parse.CSTLayoutString() + opt.Redis.Handle = "get" + opt.Redis.Key = key + opt.Trace.AppendRedis(opt.Redis) + } + }() + + for _, f := range options { + f(opt) + } + value, err := c.client.Get(key).Result() if err != nil { return "", errors.Wrapf(err, "redis get key: %s err", key) @@ -109,7 +154,20 @@ func (c *cacheRepo) Del(keys ...string) bool { return value > 0 } -func (c *cacheRepo) Incr(key string) int64 { +func (c *cacheRepo) Incr(key string, options ...Option) int64 { + opt := newOption() + defer func() { + if opt.Trace != nil { + opt.Redis.Timestamp = time_parse.CSTLayoutString() + opt.Redis.Handle = "incr" + opt.Redis.Key = key + opt.Trace.AppendRedis(opt.Redis) + } + }() + + for _, f := range options { + f(opt) + } value, _ := c.client.Incr(key).Result() return value } @@ -118,3 +176,13 @@ func (c *cacheRepo) Incr(key string) int64 { func (c *cacheRepo) Close() { c.client.Close() } + +// WithTrace 设置trace信息 +func WithTrace(t Trace) Option { + return func(opt *option) { + if t != nil { + opt.Trace = t.(*trace.Trace) + opt.Redis = new(trace.Redis) + } + } +} diff --git a/internal/pkg/core/context.go b/internal/pkg/core/context.go index f610cf4..eec44cb 100644 --- a/internal/pkg/core/context.go +++ b/internal/pkg/core/context.go @@ -2,12 +2,12 @@ package core import ( "bytes" + stdctx "context" "io/ioutil" "net/http" "net/url" "strings" "sync" - "time" "github.com/xinliangnote/go-gin-api/internal/pkg/trace" "github.com/xinliangnote/go-gin-api/pkg/errno" @@ -55,72 +55,90 @@ var _ Context = (*context)(nil) type Context interface { init() - // ShouldBindQuery 反序列化querystring - // tag: `form:"xxx"` (注:不要写成query) + // ShouldBindQuery 反序列化 querystring + // tag: `form:"xxx"` (注:不要写成 query) ShouldBindQuery(obj interface{}) error - // ShouldBindPostForm 反序列化postform(querystring会被忽略) + // ShouldBindPostForm 反序列化 postform (querystring会被忽略) // tag: `form:"xxx"` ShouldBindPostForm(obj interface{}) error - // ShouldBindForm 同时反序列化querystring和postform; - // 当querystring和postform存在相同字段时,postform优先使用。 + // ShouldBindForm 同时反序列化 querystring 和 postform; + // 当 querystring 和 postform 存在相同字段时,postform 优先使用。 // tag: `form:"xxx"` ShouldBindForm(obj interface{}) error - // ShouldBindJSON 反序列化postjson + // ShouldBindJSON 反序列化 postjson // tag: `json:"xxx"` ShouldBindJSON(obj interface{}) error - // ShouldBindURI 反序列化path参数(如路由路径为 /user/:name) + // ShouldBindURI 反序列化 path 参数(如路由路径为 /user/:name) // tag: `uri:"xxx"` ShouldBindURI(obj interface{}) error // Redirect 重定向 Redirect(code int, location string) + // Trace 获取 Trace 对象 Trace() Trace setTrace(trace Trace) disableTrace() + // Logger 获取 Logger 对象 Logger() *zap.Logger setLogger(logger *zap.Logger) + // Payload 正确返回 + Payload(payload errno.Error) getPayload() errno.Error - SetPayload(payload errno.Error) - - Header() http.Header - GetHeader(key string) string - SetHeader(key, value string) - - UserID() int - setUserID(userID int) - - UserName() string - setUserName(userName string) + // AbortWithError 错误返回 AbortWithError(err errno.Error) abortError() errno.Error + // Header 获取 Header 对象 + Header() http.Header + // GetHeader 获取 Header + GetHeader(key string) string + // SetHeader 设置 Header + SetHeader(key, value string) + + // UserID 获取 JWT 中 UserID + UserID() int64 + setUserID(userID int64) + + // UserName 获取 JWT 中 UserName + UserName() string + setUserName(userName string) + + // Alias 设置路由别名 for metrics uri Alias() string setAlias(path string) + // RawData 获取 Request.Body RawData() []byte + // Method 获取 Request.Method Method() string + // Host 获取 Request.Host Host() string + // Path 获取 请求的路径 Request.URL.Path (不附带 querystring) Path() string + // URI 获取 unescape 后的 Request.URL.RequestURI() URI() string - - Deadline() (deadline time.Time, ok bool) - Done() <-chan struct{} - Err() error - Value(key interface{}) interface{} + // RequestContext 获取请求的 context (当 client 关闭后,会自动 canceled) + RequestContext() StdContext } type context struct { ctx *gin.Context } +type StdContext struct { + stdctx.Context + Trace + *zap.Logger +} + func (c *context) init() { body, err := c.ctx.GetRawData() if err != nil { @@ -137,7 +155,7 @@ func (c *context) ShouldBindQuery(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.Query) } -// ShouldBindPostForm 反序列化postform(querystring会被忽略) +// ShouldBindPostForm 反序列化 postform (querystring 会被忽略) // tag: `form:"xxx"` func (c *context) ShouldBindPostForm(obj interface{}) error { return c.ctx.ShouldBindWith(obj, binding.FormPost) @@ -202,7 +220,7 @@ func (c *context) getPayload() errno.Error { return payload.(errno.Error) } -func (c *context) SetPayload(payload errno.Error) { +func (c *context) Payload(payload errno.Error) { c.ctx.Set(_PayloadName, payload) } @@ -227,16 +245,16 @@ func (c *context) SetHeader(key, value string) { c.ctx.Header(key, value) } -func (c *context) UserID() int { +func (c *context) UserID() int64 { val, ok := c.ctx.Get(_UserID) if !ok { return 0 } - return val.(int) + return val.(int64) } -func (c *context) setUserID(userID int) { +func (c *context) setUserID(userID int64) { c.ctx.Set(_UserID, userID) } @@ -315,25 +333,11 @@ func (c *context) URI() string { return uri } -func (c *context) Deadline() (deadline time.Time, ok bool) { - return -} - -func (c *context) Done() <-chan struct{} { - return nil -} - -func (c *context) Err() error { - return nil -} - -func (c *context) Value(key interface{}) interface{} { - if key == 0 { - return c.ctx.Request +// RequestContext 获取请求的 context (当client关闭后,会自动canceled) +func (c *context) RequestContext() StdContext { + return StdContext{ + c.ctx.Request.Context(), + c.Trace(), + c.Logger(), } - if keyAsString, ok := key.(string); ok { - val, _ := c.ctx.Get(keyAsString) - return val - } - return nil } diff --git a/internal/pkg/core/core.go b/internal/pkg/core/core.go index 08c880e..28a906d 100644 --- a/internal/pkg/core/core.go +++ b/internal/pkg/core/core.go @@ -121,11 +121,10 @@ func AliasForRecordMetrics(path string) HandlerFunc { } // WrapAuthHandler 用来处理 Auth 的入口,在之后的handler中只需 ctx.UserID() ctx.UserName() 即可。 -func WrapAuthHandler(handler func(Context) (userID int, userName string, err errno.Error)) HandlerFunc { +func WrapAuthHandler(handler func(Context) (userID int64, userName string, err errno.Error)) HandlerFunc { return func(ctx Context) { userID, userName, err := handler(ctx) if err != nil { - ctx.Logger().Error("auth handler err", zap.Error(errors.WithStack(errors.New(err.GetMsg())))) ctx.AbortWithError(err) return } @@ -362,7 +361,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { } if err := context.abortError(); err != nil { // customer err - multierr.AppendInto(&abortErr, errors.New(err.GetMsg())) + multierr.AppendInto(&abortErr, err.GetErr()) response = err } } else { @@ -389,7 +388,15 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { uri = alias } - opt.recordMetrics(context.Method(), uri, !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, ctx.Writer.Status(), businessCode, time.Since(ts).Seconds(), traceId) + opt.recordMetrics( + context.Method(), + uri, + !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, + ctx.Writer.Status(), + businessCode, + time.Since(ts).Seconds(), + traceId, + ) } var t *trace.Trace @@ -420,7 +427,12 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { t.Success = !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK t.CostSeconds = time.Since(ts).Seconds() - logger.Info("core-interceptor", zap.Any("trace", t)) + if abortErr == nil { + logger.Info("core-interceptor", zap.Any("trace", t)) + } else { + logger.Info("core-interceptor", zap.Any("trace", t), zap.Error(abortErr)) + } + }() ctx.Next() @@ -436,6 +448,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { context.AbortWithError(code.ErrManyRequest) return } + ctx.Next() }) } @@ -457,7 +470,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { Host: ctx.Host(), Status: "ok", } - ctx.SetPayload(code.OK.WithData(resp)) + ctx.Payload(code.OK.WithData(resp)) }) } diff --git a/internal/api/repository/db_repo/init.go b/internal/pkg/db/mysql.go similarity index 99% rename from internal/api/repository/db_repo/init.go rename to internal/pkg/db/mysql.go index c0aa3fc..cb4e554 100644 --- a/internal/api/repository/db_repo/init.go +++ b/internal/pkg/db/mysql.go @@ -1,4 +1,4 @@ -package db_repo +package db import ( "fmt" diff --git a/internal/api/repository/db_repo/plugin.go b/internal/pkg/db/plugin.go similarity index 96% rename from internal/api/repository/db_repo/plugin.go rename to internal/pkg/db/plugin.go index eb36fe7..400420e 100644 --- a/internal/api/repository/db_repo/plugin.go +++ b/internal/pkg/db/plugin.go @@ -1,4 +1,4 @@ -package db_repo +package db import ( "time" @@ -51,7 +51,7 @@ func before(db *gorm.DB) { func after(db *gorm.DB) { _ctx := db.Statement.Context - ctx, ok := _ctx.(core.Context) + ctx, ok := _ctx.(core.StdContext) if !ok { return } @@ -74,7 +74,7 @@ func after(db *gorm.DB) { sqlInfo.Stack = utils.FileWithLineNum() sqlInfo.Rows = db.Statement.RowsAffected sqlInfo.CostSeconds = time.Since(ts).Seconds() - ctx.Trace().AppendSQL(sqlInfo) + ctx.Trace.AppendSQL(sqlInfo) return } diff --git a/internal/pkg/metrics/metrics.go b/internal/pkg/metrics/metrics.go index 94b9c2a..88bbf05 100644 --- a/internal/pkg/metrics/metrics.go +++ b/internal/pkg/metrics/metrics.go @@ -5,30 +5,45 @@ import ( "github.com/spf13/cast" ) -// requestsCounter 定义计数器(Counter) -var requestsCounter = prometheus.NewCounterVec( +const ( + namespace = "xinliangnote" + subsystem = "go_gin_api" +) + +// metricsRequestsTotal metrics for request total 计数器(Counter) +var metricsRequestsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ - Name: "api_requests_total", + Namespace: namespace, + Subsystem: subsystem, + Name: "requests_total", + Help: "request(s) total", }, []string{"method", "path"}, ) -// httpDurationsHistogram 定义累积直方图(Histogram) -var httpDurationsHistogram = prometheus.NewHistogramVec( +// metricsRequestsCost metrics for requests cost 累积直方图(Histogram) +var metricsRequestsCost = prometheus.NewHistogramVec( prometheus.HistogramOpts{ - Name: "api_http_durations_histogram_seconds", - Buckets: []float64{0.01, 0.02, 0.03}, + Namespace: namespace, + Subsystem: subsystem, + Name: "requests_cost", + Help: "request(s) cost seconds", }, []string{"method", "path", "success", "http_code", "business_code", "cost_seconds", "trace_id"}, ) func init() { - prometheus.MustRegister(requestsCounter, httpDurationsHistogram) + prometheus.MustRegister(metricsRequestsTotal, metricsRequestsCost) } // RecordMetrics 记录指标 func RecordMetrics(method, uri string, success bool, httpCode, businessCode int, costSeconds float64, traceId string) { - httpDurationsHistogram.With(prometheus.Labels{ + metricsRequestsTotal.With(prometheus.Labels{ + "method": method, + "path": uri, + }).Inc() + + metricsRequestsCost.With(prometheus.Labels{ "method": method, "path": uri, "success": cast.ToString(success), @@ -37,9 +52,4 @@ func RecordMetrics(method, uri string, success bool, httpCode, businessCode int, "cost_seconds": cast.ToString(costSeconds), "trace_id": traceId, }).Observe(costSeconds) - - requestsCounter.With(prometheus.Labels{ - "method": method, - "path": uri, - }).Add(1) } diff --git a/internal/pkg/notify/notify.go b/internal/pkg/notify/notify.go index 0222d65..83b2bd1 100644 --- a/internal/pkg/notify/notify.go +++ b/internal/pkg/notify/notify.go @@ -8,7 +8,7 @@ import ( "go.uber.org/zap" ) -// OnPanicNotify +// OnPanicNotify 发生 panic 时进行通知 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 == "" { @@ -16,11 +16,19 @@ func OnPanicNotify(ctx core.Context, err interface{}, stackInfo string) { return } - subject, body, htmlErr := NewPanicHTMLEmail(ctx.Method(), ctx.Host(), ctx.URI(), ctx.Trace().ID(), err, stackInfo) + subject, body, htmlErr := NewPanicHTMLEmail( + ctx.Method(), + ctx.Host(), + ctx.URI(), + ctx.Trace().ID(), + err, + stackInfo, + ) if htmlErr != nil { ctx.Logger().Error("NewPanicHTMLEmail error", zap.Error(htmlErr)) return } + options := &mail.Options{ MailHost: cfg.Host, MailPort: cfg.Port, @@ -34,5 +42,6 @@ func OnPanicNotify(ctx core.Context, err interface{}, stackInfo string) { if sendErr != nil { ctx.Logger().Error("Mail Send error", zap.Error(sendErr)) } + return } diff --git a/internal/pkg/trace/dialog.go b/internal/pkg/trace/dialog.go index 4fdb27e..e5c9740 100644 --- a/internal/pkg/trace/dialog.go +++ b/internal/pkg/trace/dialog.go @@ -9,13 +9,13 @@ type D interface { AppendResponse(resp *Response) } -// Dialog 内部调用其它方接口的会话信息;失败时会有retry操作,所以response会有多次。 +// Dialog 内部调用其它方接口的会话信息;失败时会有retry操作,所以 response 会有多次。 type Dialog struct { mux sync.Mutex Request *Request `json:"request"` // 请求信息 Responses []*Response `json:"responses"` // 返回信息 Success bool `json:"success"` // 是否成功,true 或 false - CostSeconds float64 `json:"cost_seconds"` // 执行时长,单位:秒 + CostSeconds float64 `json:"cost_seconds"` // 执行时长(单位秒) } func (d *Dialog) i() {} diff --git a/internal/pkg/trace/redis.go b/internal/pkg/trace/redis.go new file mode 100644 index 0000000..1d0bb0f --- /dev/null +++ b/internal/pkg/trace/redis.go @@ -0,0 +1,11 @@ +package trace + +import "time" + +type Redis struct { + Timestamp string `json:"timestamp"` // 时间,格式:2006-01-02 15:04:05 + Handle string `json:"handle"` // 操作,function + Key string `json:"key"` + Value string `json:"value,omitempty"` + TTL time.Duration `json:"ttl,omitempty"` +} diff --git a/internal/pkg/trace/sql.go b/internal/pkg/trace/sql.go index 476843d..3baa2c5 100644 --- a/internal/pkg/trace/sql.go +++ b/internal/pkg/trace/sql.go @@ -3,7 +3,7 @@ package trace type SQL struct { Timestamp string `json:"timestamp"` // 时间,格式:2006-01-02 15:04:05 Stack string `json:"stack"` // 文件地址和行号 - CostSeconds float64 `json:"cost_seconds"` // 执行时长,单位:秒 + CostSeconds float64 `json:"cost_seconds"` // 执行时长(单位秒) SQL string `json:"sql"` // SQL 语句 Rows int64 `json:"rows_affected"` // 影响行数 } diff --git a/internal/pkg/trace/trace.go b/internal/pkg/trace/trace.go index fdf26e6..f1eaf95 100644 --- a/internal/pkg/trace/trace.go +++ b/internal/pkg/trace/trace.go @@ -23,34 +23,35 @@ type T interface { // Trace 记录的参数 type Trace struct { mux sync.Mutex - Identifier string `json:"trace_id"` - Request *Request `json:"request"` - Response *Response `json:"response"` - ThirdPartyRequests []*Dialog `json:"third_party_requests"` - Debugs []*Debug `json:"debugs"` - SQLs []*SQL `json:"sqls"` - Success bool `json:"success"` - CostSeconds float64 `json:"cost_seconds"` + Identifier string `json:"trace_id"` // 链路ID + Request *Request `json:"request"` // 请求信息 + Response *Response `json:"response"` // 返回信息 + ThirdPartyRequests []*Dialog `json:"third_party_requests"` // 调用第三方接口的信息 + Debugs []*Debug `json:"debugs"` // 调试信息 + SQLs []*SQL `json:"sqls"` // 执行的 SQL 信息 + Redis []*Redis `json:"redis"` // 执行的 Redis 信息 + Success bool `json:"success"` // 请求结果 true or false + CostSeconds float64 `json:"cost_seconds"` // 执行时长(单位秒) } // Request 请求信息 type Request struct { - TTL string `json:"ttl"` - Method string `json:"method"` - DecodedURL string `json:"decoded_url"` - Header interface{} `json:"header"` - Body interface{} `json:"body"` + TTL string `json:"ttl"` // 请求超时时间 + Method string `json:"method"` // 请求方式 + DecodedURL string `json:"decoded_url"` // 请求地址 + Header interface{} `json:"header"` // 请求 Header 信息 + Body interface{} `json:"body"` // 请求 Body 信息 } // Response 响应信息 type Response struct { - Header interface{} `json:"header"` - Body interface{} `json:"body"` - BusinessCode int `json:"business_code,omitempty"` - BusinessCodeMsg string `json:"business_code_msg,omitempty"` - HttpCode int `json:"http_code"` - HttpCodeMsg string `json:"http_code_msg"` - CostSeconds float64 `json:"cost_seconds"` + Header interface{} `json:"header"` // Header 信息 + Body interface{} `json:"body"` // Body 信息 + BusinessCode int `json:"business_code,omitempty"` // 业务码 + BusinessCodeMsg string `json:"business_code_msg,omitempty"` // 提示信息 + HttpCode int `json:"http_code"` // HTTP 状态码 + HttpCodeMsg string `json:"http_code_msg"` // HTTP 状态码信息 + CostSeconds float64 `json:"cost_seconds"` // 执行时间(单位秒) } func New(id string) *Trace { @@ -122,3 +123,16 @@ func (t *Trace) AppendSQL(sql *SQL) *Trace { t.SQLs = append(t.SQLs, sql) return t } + +// AppendRedis 追加 Redis +func (t *Trace) AppendRedis(redis *Redis) *Trace { + if redis == nil { + return t + } + + t.mux.Lock() + defer t.mux.Unlock() + + t.Redis = append(t.Redis, redis) + return t +} diff --git a/main.go b/main.go index 8e2a0b6..fd83e5e 100644 --- a/main.go +++ b/main.go @@ -7,9 +7,9 @@ import ( "time" "github.com/xinliangnote/go-gin-api/configs" - "github.com/xinliangnote/go-gin-api/internal/api/repository/cache_repo" - "github.com/xinliangnote/go-gin-api/internal/api/repository/db_repo" "github.com/xinliangnote/go-gin-api/internal/api/router" + "github.com/xinliangnote/go-gin-api/internal/pkg/cache" + "github.com/xinliangnote/go-gin-api/internal/pkg/db" "github.com/xinliangnote/go-gin-api/pkg/logger" "github.com/xinliangnote/go-gin-api/pkg/shutdown" @@ -24,6 +24,9 @@ import ( // @contact.url // @contact.email +// @license.name MIT +// @license.url https://github.com/xinliangnote/go-gin-api/blob/master/LICENSE + // @host 127.0.0.1:9999 // @BasePath func main() { @@ -39,13 +42,13 @@ func main() { defer loggers.Sync() // 初始化数据库 - dbRepo, err := db_repo.New() + dbRepo, err := db.New() if err != nil { loggers.Fatal("new db err", zap.Error(err)) } // 初始化缓存 - cacheRepo, err := cache_repo.New() + cacheRepo, err := cache.New() if err != nil { loggers.Fatal("new cache err", zap.Error(err)) } diff --git a/pkg/errno/errno.go b/pkg/errno/errno.go index d9269ca..c8d2241 100644 --- a/pkg/errno/errno.go +++ b/pkg/errno/errno.go @@ -2,6 +2,8 @@ package errno import ( "encoding/json" + + "github.com/pkg/errors" ) var _ Error = (*err)(nil) @@ -13,12 +15,16 @@ type Error interface { WithData(data interface{}) Error // WithID 设置当前请求的唯一ID WithID(id string) Error + // WithErr 设置错误信息 + WithErr(err error) Error // GetBusinessCode 获取 Business Code GetBusinessCode() int // GetHttpCode 获取 HTTP Code GetHttpCode() int // GetMsg 获取 Msg GetMsg() string + // GetErr 获取错误信息 + GetErr() error // ToString 返回 JSON 格式的错误详情 ToString() string } @@ -26,8 +32,9 @@ type Error interface { type err struct { HttpCode int `json:"-"` // HTTP Code BusinessCode int `json:"code"` // Business Code - Msg string `json:"msg"` // 错误描述 - Data interface{} `json:"data"` // 成功时返回的数据 + Msg string `json:"msg"` // 描述信息 + Data interface{} `json:"data"` // 接口数据 + Err error `json:"-"` // 错误信息 ID string `json:"id,omitempty"` // 当前请求的唯一ID,便于问题定位,忽略也可以 } @@ -37,6 +44,7 @@ func NewError(httpCode, businessCode int, msg string) Error { BusinessCode: businessCode, Msg: msg, Data: nil, + Err: nil, } } @@ -52,6 +60,11 @@ func (e *err) WithID(id string) Error { return e } +func (e *err) WithErr(err error) Error { + e.Err = errors.WithStack(err) + return e +} + func (e *err) GetHttpCode() int { return e.HttpCode } @@ -64,6 +77,10 @@ func (e *err) GetMsg() string { return e.Msg } +func (e *err) GetErr() error { + return e.Err +} + // ToString 返回 JSON 格式的错误详情 func (e *err) ToString() string { err := &struct { diff --git a/pkg/httpclient/util.go b/pkg/httpclient/util.go index 806b1d0..19cd482 100644 --- a/pkg/httpclient/util.go +++ b/pkg/httpclient/util.go @@ -29,6 +29,9 @@ var defaultClient = &http.Client{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, + MaxIdleConns: 100, + MaxConnsPerHost: 100, + MaxIdleConnsPerHost: 100, }, } @@ -47,12 +50,11 @@ func doHTTP(ctx context.Context, method, url string, payload []byte, opt *option return mock(), http.StatusOK, nil } - req, err := http.NewRequest(method, url, bytes.NewReader(payload)) + req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewReader(payload)) if err != nil { return nil, -1, errors.Wrapf(err, "new request [%s %s] err", method, url) } - req = req.WithContext(ctx) for key, value := range opt.header { req.Header.Set(key, value[0]) } diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 373c55a..8f98875 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -14,6 +14,7 @@ import ( const ( // DefaultLevel the default log level DefaultLevel = zapcore.InfoLevel + // DefaultTimeLayout the default time layout; DefaultTimeLayout = time.RFC3339 ) diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 49619f4..fe4393d 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -7,7 +7,9 @@ import ( ) func TestJSONLogger(t *testing.T) { - logger, err := NewJSONLogger(WithField("defined_key", "defined_value")) + logger, err := NewJSONLogger( + WithField("defined_key", "defined_value"), + ) if err != nil { t.Fatal(err) } @@ -18,3 +20,16 @@ func TestJSONLogger(t *testing.T) { logger.Error("err occurs", WrapMeta(err, NewMeta("para1", "value1"), NewMeta("para2", "value2"))...) } + +func BenchmarkJsonLogger(b *testing.B) { + b.ResetTimer() + logger, err := NewJSONLogger( + WithField("defined_key", "defined_value"), + ) + if err != nil { + b.Fatal(err) + } + + defer logger.Sync() + +} diff --git a/pkg/p/print.go b/pkg/p/print.go index f7731df..5038744 100644 --- a/pkg/p/print.go +++ b/pkg/p/print.go @@ -1,9 +1,9 @@ package p import ( - "github.com/xinliangnote/go-gin-api/internal/pkg/trace" + "fmt" - "github.com/davecgh/go-spew/spew" + "github.com/xinliangnote/go-gin-api/internal/pkg/trace" ) type Option func(*option) @@ -19,7 +19,7 @@ func newOption() *option { return &option{} } -func Print(key string, value interface{}, options ...Option) { +func Println(key string, value interface{}, options ...Option) { opt := newOption() defer func() { if opt.Trace != nil { @@ -33,7 +33,7 @@ func Print(key string, value interface{}, options ...Option) { f(opt) } - spew.Dump(key, value) + fmt.Println(fmt.Sprintf("KEY: %s | VALUE: %v", key, value)) } // WithTrace 设置trace信息 diff --git a/pkg/rsa/rsa_test.go b/pkg/rsa/rsa_test.go index 7d39746..d5cc756 100644 --- a/pkg/rsa/rsa_test.go +++ b/pkg/rsa/rsa_test.go @@ -45,24 +45,24 @@ QbHmKuHIRFHCFv+XX8c0aK2mDZMUlzJdy4FgD/YCEZ7kZMZKyvZW/ZuV ) func TestEncrypt(t *testing.T) { - rsaPublic := NewPublic(publicKey) - str, err := rsaPublic.Encrypt("123456") + str, err := NewPublic(publicKey).Encrypt("123456") if err != nil { t.Error("rsa public encrypt error", err) return } + t.Log(str) } func TestDecrypt(t *testing.T) { 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) + str, err := NewPrivate(privateKey).Decrypt(decryptStr) if err != nil { t.Error("rsa private decrypt error", err) return } + t.Log(str) } diff --git a/pkg/token/token.go b/pkg/token/token.go index f3b7ded..bdd6dd2 100644 --- a/pkg/token/token.go +++ b/pkg/token/token.go @@ -11,7 +11,7 @@ var _ Token = (*token)(nil) type Token interface { // i 为了避免被其他包实现 i() - Sign(userId int, userName string) (tokenString string, err error) + Sign(userId int64, userName string, expireDuration time.Duration) (tokenString string, err error) Parse(tokenString string) (*claims, error) } @@ -20,7 +20,7 @@ type token struct { } type claims struct { - UserID int + UserID int64 UserName string jwt.StandardClaims } @@ -33,7 +33,7 @@ func New(secret string) Token { func (t *token) i() {} -func (t *token) Sign(userId int, userName string) (tokenString string, err error) { +func (t *token) Sign(userId int64, userName string, expireDuration time.Duration) (tokenString string, err error) { // The token content. // iss: (Issuer)签发者 // iat: (Issued At)签发时间,用Unix时间戳表示 @@ -48,8 +48,7 @@ func (t *token) Sign(userId int, userName string) (tokenString string, err error jwt.StandardClaims{ NotBefore: time.Now().Unix(), IssuedAt: time.Now().Unix(), - ExpiresAt: time.Now().Add(24 * time.Hour).Unix(), - Issuer: "go-gin-api", + ExpiresAt: time.Now().Add(expireDuration).Unix(), }, } tokenString, err = jwt.NewWithClaims(jwt.SigningMethodHS256, claims).SignedString([]byte(t.secret)) diff --git a/pkg/token/token_test.go b/pkg/token/token_test.go index 5cf232d..b439710 100644 --- a/pkg/token/token_test.go +++ b/pkg/token/token_test.go @@ -2,12 +2,13 @@ package token import ( "testing" + "time" ) const secret = "i1ydX9RtHyuJTrw7frcu" func TestSign(t *testing.T) { - tokenString, err := New(secret).Sign(123456789, "xinliangnote") + tokenString, err := New(secret).Sign(123456789, "xinliangnote", 24*time.Hour) if err != nil { t.Error("sign error", err) return @@ -29,7 +30,7 @@ func BenchmarkSignAndParse(b *testing.B) { b.ResetTimer() token := New(secret) for i := 0; i < b.N; i++ { - tokenString, _ := token.Sign(123456789, "xinliangnote") + tokenString, _ := token.Sign(123456789, "xinliangnote", 24*time.Hour) token.Parse(tokenString) } }