From 4c37a7e6b571b29b91c59a129c52cea8e899e7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B0=E4=BA=AE?= Date: Sun, 28 Nov 2021 13:25:27 +0800 Subject: [PATCH] =?UTF-8?q?feature(1.2.8):=20swagger=20=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=96=B0=E5=A2=9E=20Security?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 将 middleware 命名为 interceptor - 将 deploy 命名为 deployments - 移除 pkg/errno - 使用 proposal 目录 - 优化代码 --- Dockerfile | 6 +- cmd/handlergen/main.go | 4 +- configs/constants.go | 10 +- {deploy => deployments}/loki/loki.yaml | 0 {deploy => deployments}/loki/promtail.yaml | 0 .../prometheus/prometheus.yml | 0 docs/docs.go | 716 +++++++++++++----- docs/swagger.json | 714 ++++++++++++----- docs/swagger.yaml | 352 +++++++-- internal/alert/alert.go | 53 ++ .../panic-mail.go => alert/email_template.go} | 54 +- internal/api/admin/func_create.go | 16 +- internal/api/admin/func_createadminmenu.go | 16 +- internal/api/admin/func_delete.go | 14 +- internal/api/admin/func_detail.go | 28 +- internal/api/admin/func_list.go | 24 +- internal/api/admin/func_listadminmenu.go | 22 +- internal/api/admin/func_login.go | 65 +- internal/api/admin/func_logout.go | 10 +- internal/api/admin/func_modifypassword.go | 34 +- internal/api/admin/func_modifypersonalinfo.go | 26 +- internal/api/admin/func_offline.go | 14 +- internal/api/admin/func_resetpassword.go | 14 +- internal/api/admin/func_updateused.go | 16 +- internal/api/admin/handler.go | 4 +- internal/api/authorized/func_create.go | 12 +- internal/api/authorized/func_createapi.go | 20 +- internal/api/authorized/func_delete.go | 14 +- internal/api/authorized/func_deleteapi.go | 14 +- internal/api/authorized/func_list.go | 24 +- internal/api/authorized/func_listapi.go | 24 +- internal/api/authorized/func_updateused.go | 18 +- internal/api/config/func_email.go | 16 +- internal/api/cron/func_create.go | 12 +- internal/api/cron/func_detail.go | 16 +- internal/api/cron/func_execute.go | 16 +- internal/api/cron/func_list.go | 24 +- internal/api/cron/func_modify.go | 18 +- internal/api/cron/func_updateused.go | 18 +- internal/api/cron/handler.go | 4 +- internal/api/helper/func_md5.go | 49 ++ internal/api/helper/func_sign.go | 100 +++ internal/api/helper/handler.go | 42 + internal/api/menu/func_create.go | 20 +- internal/api/menu/func_createaction.go | 20 +- internal/api/menu/func_delete.go | 14 +- internal/api/menu/func_deleteaction.go | 14 +- internal/api/menu/func_detail.go | 16 +- internal/api/menu/func_list.go | 12 +- internal/api/menu/func_listaction.go | 24 +- internal/api/menu/func_updatesort.go | 18 +- internal/api/menu/func_updateused.go | 18 +- internal/api/tool/func_clearcache.go | 12 +- internal/api/tool/func_dbs.go | 3 +- internal/api/tool/func_hashidsdecode.go | 12 +- internal/api/tool/func_hashidsencode.go | 12 +- internal/api/tool/func_searchcache.go | 18 +- internal/api/tool/func_searchmysql.go | 22 +- internal/api/tool/func_sendmessage.go | 22 +- internal/api/tool/func_tables.go | 12 +- internal/metrics/metrics.go | 26 + .../metrics.go => metrics/prometheus.go} | 6 +- internal/pkg/core/context.go | 77 +- internal/pkg/core/core.go | 197 +++-- internal/pkg/core/error.go | 82 ++ internal/pkg/notify/notify.go | 52 -- internal/pkg/notify/template.go | 44 -- internal/proposal/alert.go | 28 + internal/proposal/metrics.go | 28 + internal/proposal/session.go | 15 + internal/render/admin/admin.go | 9 +- internal/render/authorized/authorized.go | 5 +- internal/render/cron/cron.go | 5 +- internal/render/install/execute.go | 31 +- internal/render/upgrade/execute.go | 21 +- internal/repository/mysql/authorized/model.go | 6 + internal/repository/mysql/plugin.go | 5 +- internal/router/interceptor/check_login.go | 56 ++ .../check_rbac.go} | 35 +- .../check_signature.go} | 63 +- .../interceptor.go} | 37 +- .../router/middleware/middle_disablelog.go | 9 - internal/router/middleware/middle_token.go | 66 -- internal/router/router.go | 24 +- internal/router/router_api.go | 15 +- internal/router/router_render.go | 5 +- internal/router/router_socket.go | 3 +- internal/services/admin/service_create.go | 2 +- internal/services/admin/service_createmenu.go | 2 +- internal/services/admin/service_delete.go | 2 +- .../services/admin/service_modifypassword.go | 2 +- .../admin/service_modifypersonalinfo.go | 2 +- .../services/admin/service_resetpassword.go | 2 +- internal/services/admin/service_updateused.go | 2 +- .../services/authorized/service_create.go | 2 +- .../services/authorized/service_createapi.go | 2 +- .../services/authorized/service_delete.go | 2 +- .../services/authorized/service_deleteapi.go | 2 +- .../authorized/service_detailbykey.go | 3 +- .../services/authorized/service_updateused.go | 2 +- internal/services/cron/service_create.go | 2 +- internal/services/cron/service_modify.go | 2 +- internal/services/cron/service_updateused.go | 2 +- internal/services/menu/service_create.go | 2 +- .../services/menu/service_createaction.go | 2 +- internal/services/menu/service_delete.go | 2 +- .../services/menu/service_deleteaction.go | 2 +- internal/services/menu/service_modify.go | 2 +- internal/services/menu/service_updatesort.go | 2 +- internal/services/menu/service_updateused.go | 2 +- main.go | 7 +- pkg/{p => debugs}/print.go | 2 +- pkg/errno/errno.go | 80 -- pkg/trace/README.md | 81 -- 114 files changed, 2611 insertions(+), 1478 deletions(-) rename {deploy => deployments}/loki/loki.yaml (100%) rename {deploy => deployments}/loki/promtail.yaml (100%) rename {deploy => deployments}/prometheus/prometheus.yml (100%) create mode 100644 internal/alert/alert.go rename internal/{pkg/notify/templates/panic-mail.go => alert/email_template.go} (84%) create mode 100755 internal/api/helper/func_md5.go create mode 100755 internal/api/helper/func_sign.go create mode 100644 internal/api/helper/handler.go create mode 100644 internal/metrics/metrics.go rename internal/{pkg/metrics/metrics.go => metrics/prometheus.go} (89%) create mode 100644 internal/pkg/core/error.go delete mode 100644 internal/pkg/notify/notify.go delete mode 100644 internal/pkg/notify/template.go create mode 100644 internal/proposal/alert.go create mode 100644 internal/proposal/metrics.go create mode 100644 internal/proposal/session.go create mode 100644 internal/repository/mysql/authorized/model.go create mode 100644 internal/router/interceptor/check_login.go rename internal/router/{middleware/middle_rbac.go => interceptor/check_rbac.go} (58%) rename internal/router/{middleware/middle_signature.go => interceptor/check_signature.go} (51%) rename internal/router/{middleware/middlerware.go => interceptor/interceptor.go} (54%) delete mode 100644 internal/router/middleware/middle_disablelog.go delete mode 100644 internal/router/middleware/middle_token.go rename pkg/{p => debugs}/print.go (98%) delete mode 100644 pkg/errno/errno.go delete mode 100644 pkg/trace/README.md diff --git a/Dockerfile b/Dockerfile index 8663cad..4776560 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,12 @@ -# FROM 基于 golang:1.15-alpine -FROM golang:1.15-alpine AS builder +# FROM 基于 golang:1.16-alpine +FROM golang:1.16-alpine AS builder # ENV 设置环境变量 ENV GOPATH=/opt/repo ENV GO111MODULE=on ENV GOPROXY=https://goproxy.io,direct -# ADD 源路径 目标路径 +# COPY 源路径 目标路径 COPY . $GOPATH/src/github.com/xinliangnote/go-gin-api # RUN 执行 go build . diff --git a/cmd/handlergen/main.go b/cmd/handlergen/main.go index d005778..097abca 100644 --- a/cmd/handlergen/main.go +++ b/cmd/handlergen/main.go @@ -89,14 +89,14 @@ func main() { funcContent += fmt.Sprintf("// @Description%s \n", nameArr[1]) // Tags funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[1]) - funcContent += fmt.Sprintf("// @Accept multipart/form-data \n") + funcContent += fmt.Sprintf("// @Accept application/x-www-form-urlencoded \n") funcContent += fmt.Sprintf("// @Produce json \n") funcContent += fmt.Sprintf("// @Param Request body %sRequest true \"请求信息\" \n", Lcfirst(v.Names[0].String())) funcContent += fmt.Sprintf("// @Success 200 {object} %sResponse \n", Lcfirst(v.Names[0].String())) funcContent += fmt.Sprintf("// @Failure 400 {object} code.Failure \n") // Router funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[2]) - funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(c core.Context) {\n\n}}", v.Names[0].String()) + funcContent += fmt.Sprintf("func (h *handler) %s() core.HandlerFunc { \n return func(ctx core.Context) {\n\n}}", v.Names[0].String()) funcFile.WriteString(funcContent) funcFile.Close() diff --git a/configs/constants.go b/configs/constants.go index 1a253ae..ae5d98b 100644 --- a/configs/constants.go +++ b/configs/constants.go @@ -1,5 +1,7 @@ package configs +import "time" + const ( // MinGoVersion 最小 Go 版本 MinGoVersion = 1.16 @@ -28,12 +30,15 @@ const ( // HeaderLoginToken 登录验证 Token,Header 中传递的参数 HeaderLoginToken = "Token" - // HeaderSignToken 签名验证 Token,Header 中传递的参数 + // HeaderSignToken 签名验证 Authorization,Header 中传递的参数 HeaderSignToken = "Authorization" // HeaderSignTokenDate 签名验证 Date,Header 中传递的参数 HeaderSignTokenDate = "Authorization-Date" + // HeaderSignTokenTimeout 签名有效期为 2 分钟 + HeaderSignTokenTimeout = time.Minute * 2 + // RedisKeyPrefixLoginUser Redis Key 前缀 - 登录用户信息 RedisKeyPrefixLoginUser = ProjectName + ":login-user:" @@ -48,4 +53,7 @@ const ( // MaxRequestsPerSecond 每秒最大请求量 MaxRequestsPerSecond = 10000 + + // LoginSessionTTL 登录有效期为 24 小时 + LoginSessionTTL = time.Hour * 24 ) diff --git a/deploy/loki/loki.yaml b/deployments/loki/loki.yaml similarity index 100% rename from deploy/loki/loki.yaml rename to deployments/loki/loki.yaml diff --git a/deploy/loki/promtail.yaml b/deployments/loki/promtail.yaml similarity index 100% rename from deploy/loki/promtail.yaml rename to deployments/loki/promtail.yaml diff --git a/deploy/prometheus/prometheus.yml b/deployments/prometheus/prometheus.yml similarity index 100% rename from deploy/prometheus/prometheus.yml rename to deployments/prometheus/prometheus.yml diff --git a/docs/docs.go b/docs/docs.go index 629dd36..8a5d1d9 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -29,9 +29,14 @@ var doc = `{ "paths": { "/api/admin": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -43,15 +48,19 @@ var doc = `{ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -88,9 +97,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "新增管理员", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -123,7 +137,7 @@ var doc = `{ }, { "type": "string", - "description": "密码", + "description": "MD5后的密码", "name": "password", "in": "formData", "required": true @@ -147,9 +161,14 @@ var doc = `{ }, "/api/admin/info": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员详情", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -174,56 +193,16 @@ var doc = `{ } } }, - "/api/admin/login": { - "post": { - "description": "管理员登录", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.admin" - ], - "summary": "管理员登录", - "parameters": [ - { - "type": "string", - "description": "用户名", - "name": "username", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "密码", - "name": "password", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/admin.loginResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, "/api/admin/logout": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员登出", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -250,9 +229,14 @@ var doc = `{ }, "/api/admin/menu": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "提交菜单授权", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -293,11 +277,16 @@ var doc = `{ } } }, - "/api/admin/menu/:id": { + "/api/admin/menu/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单授权列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -333,9 +322,14 @@ var doc = `{ }, "/api/admin/modify_password": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改密码", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -378,9 +372,14 @@ var doc = `{ }, "/api/admin/modify_personal_info": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改个人信息", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -423,9 +422,14 @@ var doc = `{ }, "/api/admin/offline": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "下线管理员", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -461,6 +465,11 @@ var doc = `{ }, "/api/admin/reset_password/{id}": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "重置密码", "consumes": [ "application/json" @@ -499,9 +508,14 @@ var doc = `{ }, "/api/admin/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新管理员为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -544,6 +558,11 @@ var doc = `{ }, "/api/admin/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除管理员", "consumes": [ "application/json" @@ -582,9 +601,14 @@ var doc = `{ }, "/api/authorized": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "调用方列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -596,15 +620,19 @@ var doc = `{ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -647,9 +675,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "新增调用方", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -699,9 +732,14 @@ var doc = `{ }, "/api/authorized/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新调用方为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -713,7 +751,7 @@ var doc = `{ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashID", "name": "id", "in": "formData", "required": true @@ -744,6 +782,11 @@ var doc = `{ }, "/api/authorized/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除调用方", "consumes": [ "application/json" @@ -782,9 +825,14 @@ var doc = `{ }, "/api/authorized_api": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "调用方接口地址列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -818,9 +866,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "授权调用方接口地址", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -870,6 +923,11 @@ var doc = `{ }, "/api/authorized_api/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除调用方接口地址", "consumes": [ "application/json" @@ -908,9 +966,14 @@ var doc = `{ }, "/api/config/email": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改邮件配置", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -974,9 +1037,14 @@ var doc = `{ }, "/api/cron": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "任务列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -988,15 +1056,19 @@ var doc = `{ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -1033,9 +1105,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建任务", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1155,8 +1232,63 @@ var doc = `{ } } }, - "/api/cron/:id": { + "/api/cron/used": { + "patch": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "更新任务为启用/禁用", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.cron" + ], + "summary": "更新任务为启用/禁用", + "parameters": [ + { + "type": "string", + "description": "hashID", + "name": "id", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "是否启用 1:是 -1:否", + "name": "used", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cron.updateUsedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/cron/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "获取单条任务详情", "consumes": [ "application/json" @@ -1192,93 +1324,15 @@ var doc = `{ } } }, - "patch": { - "description": "手动执行单条任务", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.cron" - ], - "summary": "手动执行单条任务", - "parameters": [ - { - "type": "string", - "description": "hashId", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/cron.detailResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, - "/api/cron/used": { - "patch": { - "description": "更新任务为启用/禁用", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.cron" - ], - "summary": "更新任务为启用/禁用", - "parameters": [ - { - "type": "string", - "description": "Hashid", - "name": "id", - "in": "formData", - "required": true - }, - { - "type": "integer", - "description": "是否启用 1:是 -1:否", - "name": "used", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/cron.updateUsedResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, - "/api/cron/{id}": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "编辑任务", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1290,7 +1344,7 @@ var doc = `{ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashID", "name": "id", "in": "formData", "required": true @@ -1403,13 +1457,109 @@ var doc = `{ } } } + }, + "patch": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "手动执行单条任务", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.cron" + ], + "summary": "手动执行单条任务", + "parameters": [ + { + "type": "string", + "description": "hashId", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cron.detailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/login": { + "post": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "管理员登录", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.admin" + ], + "summary": "管理员登录", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "MD5后的密码", + "name": "password", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.loginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } } }, "/api/menu": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1434,9 +1584,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建/编辑菜单", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1474,9 +1629,14 @@ var doc = `{ }, "/api/menu/sort": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新菜单排序", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1488,7 +1648,7 @@ var doc = `{ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashId", "name": "id", "in": "formData", "required": true @@ -1519,9 +1679,14 @@ var doc = `{ }, "/api/menu/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新菜单为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1533,7 +1698,7 @@ var doc = `{ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashId", "name": "id", "in": "formData", "required": true @@ -1564,9 +1729,14 @@ var doc = `{ }, "/api/menu/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单详情", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1600,6 +1770,11 @@ var doc = `{ } }, "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除菜单", "consumes": [ "application/json" @@ -1638,9 +1813,14 @@ var doc = `{ }, "/api/menu_action": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "功能权限列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1674,9 +1854,14 @@ var doc = `{ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建功能权限", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1726,6 +1911,11 @@ var doc = `{ }, "/api/menu_action/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除功能权限", "consumes": [ "application/json" @@ -1764,9 +1954,14 @@ var doc = `{ }, "/api/tool/cache/clear": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "清空缓存", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1802,9 +1997,14 @@ var doc = `{ }, "/api/tool/cache/search": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询缓存", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1840,9 +2040,14 @@ var doc = `{ }, "/api/tool/data/dbs": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询 DB", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1869,9 +2074,14 @@ var doc = `{ }, "/api/tool/data/mysql": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "执行 SQL 语句", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1921,9 +2131,14 @@ var doc = `{ }, "/api/tool/data/tables": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询 Table", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1959,9 +2174,14 @@ var doc = `{ }, "/api/tool/hashids/decode/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "HashIds 解密", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1997,9 +2217,14 @@ var doc = `{ }, "/api/tool/hashids/encode/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "HashIds 加密", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -2035,9 +2260,14 @@ var doc = `{ }, "/api/tool/send_message": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "发送消息", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -2070,6 +2300,103 @@ var doc = `{ } } } + }, + "/helper/md5/{str}": { + "get": { + "description": "加密", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helper" + ], + "summary": "加密", + "parameters": [ + { + "type": "string", + "description": "需要加密的字符串", + "name": "str", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.md5Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/helper/sign": { + "post": { + "description": "签名", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helper" + ], + "summary": "签名", + "parameters": [ + { + "type": "string", + "description": "调用方 KEY", + "name": "key", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求路径 (不附带 querystring),例如:/api/login", + "name": "path", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求方式,例如:POST", + "name": "method", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求参数,例如:username=tom\u0026password=123456", + "name": "params", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.signResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } } }, "definitions": { @@ -2687,6 +3014,28 @@ var doc = `{ } } }, + "helper.md5Response": { + "type": "object", + "properties": { + "md5_str": { + "description": "MD5后的字符串", + "type": "string" + } + } + }, + "helper.signResponse": { + "type": "object", + "properties": { + "authorization": { + "description": "签名信息-Authorization", + "type": "string" + }, + "authorization_date": { + "description": "签名信息-Authorization-Date", + "type": "string" + } + } + }, "menu.createActionResponse": { "type": "object", "properties": { @@ -3004,6 +3353,13 @@ var doc = `{ } } } + }, + "securityDefinitions": { + "LoginToken": { + "type": "apiKey", + "name": "token", + "in": "header" + } } }` @@ -3019,8 +3375,8 @@ type swaggerInfo struct { // SwaggerInfo holds exported Swagger Info so clients can modify it var SwaggerInfo = swaggerInfo{ Version: "2.0", - Host: "127.0.0.1:9999", - BasePath: "", + Host: "", + BasePath: "/", Schemes: []string{}, Title: "swagger 接口文档", Description: "", diff --git a/docs/swagger.json b/docs/swagger.json index 2b07639..e148093 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -9,13 +9,18 @@ }, "version": "2.0" }, - "host": "127.0.0.1:9999", + "basePath": "/", "paths": { "/api/admin": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -27,15 +32,19 @@ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -72,9 +81,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "新增管理员", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -107,7 +121,7 @@ }, { "type": "string", - "description": "密码", + "description": "MD5后的密码", "name": "password", "in": "formData", "required": true @@ -131,9 +145,14 @@ }, "/api/admin/info": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员详情", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -158,56 +177,16 @@ } } }, - "/api/admin/login": { - "post": { - "description": "管理员登录", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.admin" - ], - "summary": "管理员登录", - "parameters": [ - { - "type": "string", - "description": "用户名", - "name": "username", - "in": "formData", - "required": true - }, - { - "type": "string", - "description": "密码", - "name": "password", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/admin.loginResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, "/api/admin/logout": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "管理员登出", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -234,9 +213,14 @@ }, "/api/admin/menu": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "提交菜单授权", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -277,11 +261,16 @@ } } }, - "/api/admin/menu/:id": { + "/api/admin/menu/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单授权列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -317,9 +306,14 @@ }, "/api/admin/modify_password": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改密码", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -362,9 +356,14 @@ }, "/api/admin/modify_personal_info": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改个人信息", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -407,9 +406,14 @@ }, "/api/admin/offline": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "下线管理员", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -445,6 +449,11 @@ }, "/api/admin/reset_password/{id}": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "重置密码", "consumes": [ "application/json" @@ -483,9 +492,14 @@ }, "/api/admin/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新管理员为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -528,6 +542,11 @@ }, "/api/admin/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除管理员", "consumes": [ "application/json" @@ -566,9 +585,14 @@ }, "/api/authorized": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "调用方列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -580,15 +604,19 @@ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -631,9 +659,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "新增调用方", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -683,9 +716,14 @@ }, "/api/authorized/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新调用方为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -697,7 +735,7 @@ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashID", "name": "id", "in": "formData", "required": true @@ -728,6 +766,11 @@ }, "/api/authorized/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除调用方", "consumes": [ "application/json" @@ -766,9 +809,14 @@ }, "/api/authorized_api": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "调用方接口地址列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -802,9 +850,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "授权调用方接口地址", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -854,6 +907,11 @@ }, "/api/authorized_api/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除调用方接口地址", "consumes": [ "application/json" @@ -892,9 +950,14 @@ }, "/api/config/email": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "修改邮件配置", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -958,9 +1021,14 @@ }, "/api/cron": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "任务列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -972,15 +1040,19 @@ "parameters": [ { "type": "integer", + "default": 1, "description": "第几页", "name": "page", - "in": "query" + "in": "query", + "required": true }, { - "type": "string", + "type": "integer", + "default": 10, "description": "每页显示条数", "name": "page_size", - "in": "query" + "in": "query", + "required": true }, { "type": "string", @@ -1017,9 +1089,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建任务", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1139,8 +1216,63 @@ } } }, - "/api/cron/:id": { + "/api/cron/used": { + "patch": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "更新任务为启用/禁用", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.cron" + ], + "summary": "更新任务为启用/禁用", + "parameters": [ + { + "type": "string", + "description": "hashID", + "name": "id", + "in": "formData", + "required": true + }, + { + "type": "integer", + "description": "是否启用 1:是 -1:否", + "name": "used", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cron.updateUsedResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/cron/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "获取单条任务详情", "consumes": [ "application/json" @@ -1176,93 +1308,15 @@ } } }, - "patch": { - "description": "手动执行单条任务", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.cron" - ], - "summary": "手动执行单条任务", - "parameters": [ - { - "type": "string", - "description": "hashId", - "name": "id", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/cron.detailResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, - "/api/cron/used": { - "patch": { - "description": "更新任务为启用/禁用", - "consumes": [ - "multipart/form-data" - ], - "produces": [ - "application/json" - ], - "tags": [ - "API.cron" - ], - "summary": "更新任务为启用/禁用", - "parameters": [ - { - "type": "string", - "description": "Hashid", - "name": "id", - "in": "formData", - "required": true - }, - { - "type": "integer", - "description": "是否启用 1:是 -1:否", - "name": "used", - "in": "formData", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/cron.updateUsedResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/code.Failure" - } - } - } - } - }, - "/api/cron/{id}": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "编辑任务", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1274,7 +1328,7 @@ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashID", "name": "id", "in": "formData", "required": true @@ -1387,13 +1441,109 @@ } } } + }, + "patch": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "手动执行单条任务", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.cron" + ], + "summary": "手动执行单条任务", + "parameters": [ + { + "type": "string", + "description": "hashId", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/cron.detailResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/api/login": { + "post": { + "security": [ + { + "LoginToken": [] + } + ], + "description": "管理员登录", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "API.admin" + ], + "summary": "管理员登录", + "parameters": [ + { + "type": "string", + "description": "用户名", + "name": "username", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "MD5后的密码", + "name": "password", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/admin.loginResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } } }, "/api/menu": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单列表", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1418,9 +1568,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建/编辑菜单", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1458,9 +1613,14 @@ }, "/api/menu/sort": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新菜单排序", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1472,7 +1632,7 @@ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashId", "name": "id", "in": "formData", "required": true @@ -1503,9 +1663,14 @@ }, "/api/menu/used": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "更新菜单为启用/禁用", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1517,7 +1682,7 @@ "parameters": [ { "type": "string", - "description": "Hashid", + "description": "hashId", "name": "id", "in": "formData", "required": true @@ -1548,9 +1713,14 @@ }, "/api/menu/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "菜单详情", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1584,6 +1754,11 @@ } }, "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除菜单", "consumes": [ "application/json" @@ -1622,9 +1797,14 @@ }, "/api/menu_action": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "功能权限列表", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1658,9 +1838,14 @@ } }, "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "创建功能权限", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1710,6 +1895,11 @@ }, "/api/menu_action/{id}": { "delete": { + "security": [ + { + "LoginToken": [] + } + ], "description": "删除功能权限", "consumes": [ "application/json" @@ -1748,9 +1938,14 @@ }, "/api/tool/cache/clear": { "patch": { + "security": [ + { + "LoginToken": [] + } + ], "description": "清空缓存", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1786,9 +1981,14 @@ }, "/api/tool/cache/search": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询缓存", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1824,9 +2024,14 @@ }, "/api/tool/data/dbs": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询 DB", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1853,9 +2058,14 @@ }, "/api/tool/data/mysql": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "执行 SQL 语句", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1905,9 +2115,14 @@ }, "/api/tool/data/tables": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "查询 Table", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1943,9 +2158,14 @@ }, "/api/tool/hashids/decode/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "HashIds 解密", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -1981,9 +2201,14 @@ }, "/api/tool/hashids/encode/{id}": { "get": { + "security": [ + { + "LoginToken": [] + } + ], "description": "HashIds 加密", "consumes": [ - "application/json" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -2019,9 +2244,14 @@ }, "/api/tool/send_message": { "post": { + "security": [ + { + "LoginToken": [] + } + ], "description": "发送消息", "consumes": [ - "multipart/form-data" + "application/x-www-form-urlencoded" ], "produces": [ "application/json" @@ -2054,6 +2284,103 @@ } } } + }, + "/helper/md5/{str}": { + "get": { + "description": "加密", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helper" + ], + "summary": "加密", + "parameters": [ + { + "type": "string", + "description": "需要加密的字符串", + "name": "str", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.md5Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } + }, + "/helper/sign": { + "post": { + "description": "签名", + "consumes": [ + "application/x-www-form-urlencoded" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Helper" + ], + "summary": "签名", + "parameters": [ + { + "type": "string", + "description": "调用方 KEY", + "name": "key", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求路径 (不附带 querystring),例如:/api/login", + "name": "path", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求方式,例如:POST", + "name": "method", + "in": "formData", + "required": true + }, + { + "type": "string", + "description": "请求参数,例如:username=tom\u0026password=123456", + "name": "params", + "in": "formData", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/helper.signResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/code.Failure" + } + } + } + } } }, "definitions": { @@ -2671,6 +2998,28 @@ } } }, + "helper.md5Response": { + "type": "object", + "properties": { + "md5_str": { + "description": "MD5后的字符串", + "type": "string" + } + } + }, + "helper.signResponse": { + "type": "object", + "properties": { + "authorization": { + "description": "签名信息-Authorization", + "type": "string" + }, + "authorization_date": { + "description": "签名信息-Authorization-Date", + "type": "string" + } + } + }, "menu.createActionResponse": { "type": "object", "properties": { @@ -2988,5 +3337,12 @@ } } } + }, + "securityDefinitions": { + "LoginToken": { + "type": "apiKey", + "name": "token", + "in": "header" + } } } \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml index a8657ba..d72d09b 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,3 +1,4 @@ +basePath: / definitions: admin.ListMenuData: properties: @@ -428,6 +429,21 @@ definitions: description: 主键ID type: integer type: object + helper.md5Response: + properties: + md5_str: + description: MD5后的字符串 + type: string + type: object + helper.signResponse: + properties: + authorization: + description: 签名信息-Authorization + type: string + authorization_date: + description: 签名信息-Authorization-Date + type: string + type: object menu.createActionResponse: properties: id: @@ -647,7 +663,6 @@ definitions: $ref: '#/definitions/tool.tableData' type: array type: object -host: 127.0.0.1:9999 info: contact: {} license: @@ -659,17 +674,21 @@ paths: /api/admin: get: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 管理员列表 parameters: - - description: 第几页 + - default: 1 + description: 第几页 in: query name: page + required: true type: integer - - description: 每页显示条数 + - default: 10 + description: 每页显示条数 in: query name: page_size - type: string + required: true + type: integer - description: 用户名 in: query name: username @@ -693,12 +712,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 管理员列表 tags: - API.admin post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 新增管理员 parameters: - description: 用户名 @@ -716,7 +737,7 @@ paths: name: mobile required: true type: string - - description: 密码 + - description: MD5后的密码 in: formData name: password required: true @@ -732,6 +753,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 新增管理员 tags: - API.admin @@ -757,13 +780,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 删除管理员 tags: - API.admin /api/admin/info: get: consumes: - - application/json + - application/x-www-form-urlencoded description: 管理员详情 produces: - application/json @@ -776,43 +801,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 管理员详情 tags: - API.admin - /api/admin/login: - post: - consumes: - - multipart/form-data - description: 管理员登录 - parameters: - - description: 用户名 - in: formData - name: username - required: true - type: string - - description: 密码 - in: formData - name: password - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/admin.loginResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/code.Failure' - summary: 管理员登录 - tags: - - API.admin /api/admin/logout: post: consumes: - - application/json + - application/x-www-form-urlencoded description: 管理员登出 produces: - application/json @@ -825,13 +822,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 管理员登出 tags: - API.admin /api/admin/menu: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 提交菜单授权 parameters: - description: Hashid @@ -855,13 +854,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 提交菜单授权 tags: - API.admin - /api/admin/menu/:id: + /api/admin/menu/{id}: get: consumes: - - application/json + - application/x-www-form-urlencoded description: 菜单授权列表 parameters: - description: hashId @@ -880,13 +881,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 菜单授权列表 tags: - API.admin /api/admin/modify_password: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 修改密码 parameters: - description: 旧密码 @@ -910,13 +913,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 修改密码 tags: - API.admin /api/admin/modify_personal_info: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 修改个人信息 parameters: - description: 昵称 @@ -940,13 +945,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 修改个人信息 tags: - API.admin /api/admin/offline: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 下线管理员 parameters: - description: Hashid @@ -965,6 +972,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 下线管理员 tags: - API.admin @@ -990,13 +999,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 重置密码 tags: - API.admin /api/admin/used: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 更新管理员为启用/禁用 parameters: - description: Hashid @@ -1020,23 +1031,29 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 更新管理员为启用/禁用 tags: - API.admin /api/authorized: get: consumes: - - application/json + - application/x-www-form-urlencoded description: 调用方列表 parameters: - - description: 第几页 + - default: 1 + description: 第几页 in: query name: page + required: true type: integer - - description: 每页显示条数 + - default: 10 + description: 每页显示条数 in: query name: page_size - type: string + required: true + type: integer - description: 调用方key in: query name: business_key @@ -1064,12 +1081,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 调用方列表 tags: - API.authorized post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 新增调用方 parameters: - description: 调用方key @@ -1098,6 +1117,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 新增调用方 tags: - API.authorized @@ -1123,16 +1144,18 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 删除调用方 tags: - API.authorized /api/authorized/used: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 更新调用方为启用/禁用 parameters: - - description: Hashid + - description: hashID in: formData name: id required: true @@ -1153,13 +1176,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 更新调用方为启用/禁用 tags: - API.authorized /api/authorized_api: get: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 调用方接口地址列表 parameters: - description: hashID @@ -1178,12 +1203,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 调用方接口地址列表 tags: - API.authorized post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 授权调用方接口地址 parameters: - description: HashID @@ -1212,6 +1239,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 授权调用方接口地址 tags: - API.authorized @@ -1237,13 +1266,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 删除调用方接口地址 tags: - API.authorized /api/config/email: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 修改邮件配置 parameters: - description: 邮箱服务器 @@ -1282,23 +1313,29 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 修改邮件配置 tags: - API.config /api/cron: get: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 任务列表 parameters: - - description: 第几页 + - default: 1 + description: 第几页 in: query name: page + required: true type: integer - - description: 每页显示条数 + - default: 10 + description: 每页显示条数 in: query name: page_size - type: string + required: true + type: integer - description: 任务名称 in: query name: name @@ -1322,12 +1359,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 任务列表 tags: - API.cron post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 创建任务 parameters: - description: 任务名称 @@ -1406,10 +1445,12 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 创建任务 tags: - API.cron - /api/cron/:id: + /api/cron/{id}: get: consumes: - application/json @@ -1431,6 +1472,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 获取单条任务详情 tags: - API.cron @@ -1455,16 +1498,17 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 手动执行单条任务 tags: - API.cron - /api/cron/{id}: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 编辑任务 parameters: - - description: Hashid + - description: hashID in: formData name: id required: true @@ -1545,16 +1589,18 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 编辑任务 tags: - API.cron /api/cron/used: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 更新任务为启用/禁用 parameters: - - description: Hashid + - description: hashID in: formData name: id required: true @@ -1575,13 +1621,47 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 更新任务为启用/禁用 tags: - API.cron + /api/login: + post: + consumes: + - application/x-www-form-urlencoded + description: 管理员登录 + parameters: + - description: 用户名 + in: formData + name: username + required: true + type: string + - description: MD5后的密码 + in: formData + name: password + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/admin.loginResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] + summary: 管理员登录 + tags: + - API.admin /api/menu: get: consumes: - - application/json + - application/x-www-form-urlencoded description: 菜单列表 produces: - application/json @@ -1594,12 +1674,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 菜单列表 tags: - API.menu post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 创建/编辑菜单 parameters: - description: 请求信息 @@ -1619,6 +1701,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 创建/编辑菜单 tags: - API.menu @@ -1644,12 +1728,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 删除菜单 tags: - API.menu get: consumes: - - application/json + - application/x-www-form-urlencoded description: 菜单详情 parameters: - description: hashId @@ -1668,16 +1754,18 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 菜单详情 tags: - API.menu /api/menu/sort: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 更新菜单排序 parameters: - - description: Hashid + - description: hashId in: formData name: id required: true @@ -1698,16 +1786,18 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 更新菜单排序 tags: - API.menu /api/menu/used: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 更新菜单为启用/禁用 parameters: - - description: Hashid + - description: hashId in: formData name: id required: true @@ -1728,13 +1818,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 更新菜单为启用/禁用 tags: - API.menu /api/menu_action: get: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 功能权限列表 parameters: - description: hashID @@ -1753,12 +1845,14 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 功能权限列表 tags: - API.menu post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 创建功能权限 parameters: - description: HashID @@ -1787,6 +1881,8 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 创建功能权限 tags: - API.menu @@ -1812,13 +1908,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 删除功能权限 tags: - API.menu /api/tool/cache/clear: patch: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 清空缓存 parameters: - description: Redis Key @@ -1837,13 +1935,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 清空缓存 tags: - API.tool /api/tool/cache/search: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 查询缓存 parameters: - description: Redis Key @@ -1862,13 +1962,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 查询缓存 tags: - API.tool /api/tool/data/dbs: get: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 查询 DB produces: - application/json @@ -1881,13 +1983,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 查询 DB tags: - API.tool /api/tool/data/mysql: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 执行 SQL 语句 parameters: - description: 数据库名称 @@ -1916,13 +2020,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 执行 SQL 语句 tags: - API.tool /api/tool/data/tables: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 查询 Table parameters: - description: 数据库名称 @@ -1941,13 +2047,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 查询 Table tags: - API.tool /api/tool/hashids/decode/{id}: get: consumes: - - application/json + - application/x-www-form-urlencoded description: HashIds 解密 parameters: - description: 需解密的密文 @@ -1966,13 +2074,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: HashIds 解密 tags: - API.tool /api/tool/hashids/encode/{id}: get: consumes: - - application/json + - application/x-www-form-urlencoded description: HashIds 加密 parameters: - description: 需加密的数字 @@ -1991,13 +2101,15 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: HashIds 加密 tags: - API.tool /api/tool/send_message: post: consumes: - - multipart/form-data + - application/x-www-form-urlencoded description: 发送消息 parameters: - description: 消息内容 @@ -2016,7 +2128,79 @@ paths: description: Bad Request schema: $ref: '#/definitions/code.Failure' + security: + - LoginToken: [] summary: 发送消息 tags: - API.tool + /helper/md5/{str}: + get: + consumes: + - application/x-www-form-urlencoded + description: 加密 + parameters: + - description: 需要加密的字符串 + in: path + name: str + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/helper.md5Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 加密 + tags: + - Helper + /helper/sign: + post: + consumes: + - application/x-www-form-urlencoded + description: 签名 + parameters: + - description: 调用方 KEY + in: formData + name: key + required: true + type: string + - description: 请求路径 (不附带 querystring),例如:/api/login + in: formData + name: path + required: true + type: string + - description: 请求方式,例如:POST + in: formData + name: method + required: true + type: string + - description: 请求参数,例如:username=tom&password=123456 + in: formData + name: params + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/helper.signResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/code.Failure' + summary: 签名 + tags: + - Helper +securityDefinitions: + LoginToken: + in: header + name: token + type: apiKey swagger: "2.0" diff --git a/internal/alert/alert.go b/internal/alert/alert.go new file mode 100644 index 0000000..a2f1eb1 --- /dev/null +++ b/internal/alert/alert.go @@ -0,0 +1,53 @@ +package alert + +import ( + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/proposal" + "github.com/xinliangnote/go-gin-api/pkg/errors" + "github.com/xinliangnote/go-gin-api/pkg/mail" + + "go.uber.org/zap" +) + +// NotifyHandler 告警通知 +func NotifyHandler(logger *zap.Logger) func(msg *proposal.AlertMessage) { + if logger == nil { + panic("logger required") + } + + return func(msg *proposal.AlertMessage) { + cfg := configs.Get().Mail + if cfg.Host == "" || cfg.Port == 0 || cfg.User == "" || cfg.Pass == "" || cfg.To == "" { + logger.Error("Mail config error") + return + } + + subject, body, err := newHTMLEmail( + msg.Method, + msg.HOST, + msg.URI, + msg.TraceID, + msg.ErrorMessage, + msg.ErrorStack, + ) + if err != nil { + logger.Error("email template error", zap.Error(err)) + return + } + + options := &mail.Options{ + MailHost: cfg.Host, + MailPort: cfg.Port, + MailUser: cfg.User, + MailPass: cfg.Pass, + MailTo: cfg.To, + Subject: subject, + Body: body, + } + if err := mail.Send(options); err != nil { + logger.Error("发送告警通知邮件失败", zap.Error(errors.WithStack(err))) + } + + return + } +} diff --git a/internal/pkg/notify/templates/panic-mail.go b/internal/alert/email_template.go similarity index 84% rename from internal/pkg/notify/templates/panic-mail.go rename to internal/alert/email_template.go index 490b100..a21fd21 100644 --- a/internal/pkg/notify/templates/panic-mail.go +++ b/internal/alert/email_template.go @@ -1,6 +1,54 @@ -package templates +package alert -const PanicMail = ` +import ( + "bytes" + "fmt" + "html/template" + "time" +) + +// NewHTMLEmail 告警邮件模板 +func newHTMLEmail(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(), + } + + // subject 邮件主题 + subject = fmt.Sprintf("[系统告警]-%s", uri) + + // body 邮件内容 + body, err = getEmailHTMLContent(mailTemplate, mailData) + + return +} + +// 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 +} + +const mailTemplate = ` @@ -27,7 +75,7 @@ const PanicMail = ` - 系统异常 + 系统告警 diff --git a/internal/api/admin/func_create.go b/internal/api/admin/func_create.go index 7da0032..9e6eb76 100755 --- a/internal/api/admin/func_create.go +++ b/internal/api/admin/func_create.go @@ -7,14 +7,13 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createRequest struct { Username string `form:"username" binding:"required"` // 用户名 Nickname string `form:"nickname" binding:"required"` // 昵称 Mobile string `form:"mobile" binding:"required"` // 手机号 - Password string `form:"password" binding:"required"` // 密码 + Password string `form:"password" binding:"required"` // MD5后的密码 } type createResponse struct { @@ -25,24 +24,25 @@ type createResponse struct { // @Summary 新增管理员 // @Description 新增管理员 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param username formData string true "用户名" // @Param nickname formData string true "昵称" // @Param mobile formData string true "手机号" -// @Param password formData string true "密码" +// @Param password formData string true "MD5后的密码" // @Success 200 {object} createResponse // @Failure 400 {object} code.Failure // @Router /api/admin [post] +// @Security LoginToken func (h *handler) Create() core.HandlerFunc { return func(c core.Context) { req := new(createRequest) res := new(createResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } @@ -55,10 +55,10 @@ func (h *handler) Create() core.HandlerFunc { id, err := h.adminService.Create(c, createData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminCreateError, - code.Text(code.AdminCreateError)).WithErr(err), + code.Text(code.AdminCreateError)).WithError(err), ) return } diff --git a/internal/api/admin/func_createadminmenu.go b/internal/api/admin/func_createadminmenu.go index 7789266..7ca7808 100755 --- a/internal/api/admin/func_createadminmenu.go +++ b/internal/api/admin/func_createadminmenu.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createAdminMenuRequest struct { @@ -22,32 +21,33 @@ type createAdminMenuResponse struct { // @Summary 提交菜单授权 // @Description 提交菜单授权 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id formData string true "Hashid" // @Param actions formData string true "功能权限ID,多个用,分割" // @Success 200 {object} createResponse // @Failure 400 {object} code.Failure // @Router /api/admin/menu [post] +// @Security LoginToken func (h *handler) CreateAdminMenu() core.HandlerFunc { return func(c core.Context) { req := new(createAdminMenuRequest) res := new(createAdminMenuResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -58,10 +58,10 @@ func (h *handler) CreateAdminMenu() core.HandlerFunc { err = h.adminService.CreateMenu(c, createData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminMenuCreateError, - code.Text(code.AdminMenuCreateError)).WithErr(err), + code.Text(code.AdminMenuCreateError)).WithError(err), ) return } diff --git a/internal/api/admin/func_delete.go b/internal/api/admin/func_delete.go index c42fcf5..45b337e 100755 --- a/internal/api/admin/func_delete.go +++ b/internal/api/admin/func_delete.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type deleteRequest struct { @@ -26,25 +25,26 @@ type deleteResponse struct { // @Success 200 {object} deleteResponse // @Failure 400 {object} code.Failure // @Router /api/admin/{id} [delete] +// @Security LoginToken func (h *handler) Delete() core.HandlerFunc { return func(c core.Context) { req := new(deleteRequest) res := new(deleteResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc { err = h.adminService.Delete(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminDeleteError, - code.Text(code.AdminDeleteError)).WithErr(err), + code.Text(code.AdminDeleteError)).WithError(err), ) return } diff --git a/internal/api/admin/func_detail.go b/internal/api/admin/func_detail.go index 6a8d903..4477253 100644 --- a/internal/api/admin/func_detail.go +++ b/internal/api/admin/func_detail.go @@ -10,9 +10,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/password" "github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" - - "github.com/spf13/cast" ) type detailResponse struct { @@ -26,35 +23,36 @@ type detailResponse struct { // @Summary 管理员详情 // @Description 管理员详情 // @Tags API.admin -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Success 200 {object} detailResponse // @Failure 400 {object} code.Failure // @Router /api/admin/info [get] +// @Security LoginToken func (h *handler) Detail() core.HandlerFunc { - return func(c core.Context) { + return func(ctx core.Context) { res := new(detailResponse) searchOneData := new(admin.SearchOneData) - searchOneData.Id = cast.ToInt32(c.UserID()) + searchOneData.Id = ctx.SessionUserInfo().UserID searchOneData.IsUsed = 1 - info, err := h.adminService.Detail(c, searchOneData) + info, err := h.adminService.Detail(ctx, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminDetailError, - code.Text(code.AdminDetailError)).WithErr(err), + code.Text(code.AdminDetailError)).WithError(err), ) return } - menuCacheData, err := h.cache.Get(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(searchOneData.Id)+":menu", redis.WithTrace(c.Trace())) + menuCacheData, err := h.cache.Get(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(searchOneData.Id)+":menu", redis.WithTrace(ctx.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminDetailError, - code.Text(code.AdminDetailError)).WithErr(err), + code.Text(code.AdminDetailError)).WithError(err), ) return } @@ -62,10 +60,10 @@ func (h *handler) Detail() core.HandlerFunc { var menuData []admin.ListMyMenuData err = json.Unmarshal([]byte(menuCacheData), &menuData) if err != nil { - c.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminDetailError, - code.Text(code.AdminDetailError)).WithErr(err), + code.Text(code.AdminDetailError)).WithError(err), ) return } @@ -74,6 +72,6 @@ func (h *handler) Detail() core.HandlerFunc { res.Nickname = info.Nickname res.Mobile = info.Mobile res.Menu = menuData - c.Payload(res) + ctx.Payload(res) } } diff --git a/internal/api/admin/func_list.go b/internal/api/admin/func_list.go index b623077..747058e 100755 --- a/internal/api/admin/func_list.go +++ b/internal/api/admin/func_list.go @@ -8,7 +8,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/password" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/timeutil" "github.com/spf13/cast" @@ -49,25 +48,26 @@ type listResponse struct { // @Summary 管理员列表 // @Description 管理员列表 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param page query int false "第几页" -// @Param page_size query string false "每页显示条数" +// @Param page query int true "第几页" default(1) +// @Param page_size query int true "每页显示条数" default(10) // @Param username query string false "用户名" // @Param nickname query string false "昵称" // @Param mobile query string false "手机号" // @Success 200 {object} listResponse // @Failure 400 {object} code.Failure // @Router /api/admin [get] +// @Security LoginToken func (h *handler) List() core.HandlerFunc { return func(c core.Context) { req := new(listRequest) res := new(listResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -91,20 +91,20 @@ func (h *handler) List() core.HandlerFunc { resListData, err := h.adminService.PageList(c, searchData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminListError, - code.Text(code.AdminListError)).WithErr(err), + code.Text(code.AdminListError)).WithError(err), ) return } resCountData, err := h.adminService.PageListCount(c, searchData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminListError, - code.Text(code.AdminListError)).WithErr(err), + code.Text(code.AdminListError)).WithError(err), ) return } @@ -116,10 +116,10 @@ func (h *handler) List() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/admin/func_listadminmenu.go b/internal/api/admin/func_listadminmenu.go index 68db90d..2864eb5 100755 --- a/internal/api/admin/func_listadminmenu.go +++ b/internal/api/admin/func_listadminmenu.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type listAdminMenuRequest struct { @@ -22,31 +21,32 @@ type listAdminMenuResponse struct { // @Summary 菜单授权列表 // @Description 菜单授权列表 // @Tags API.admin -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id path string true "hashId" // @Success 200 {object} listAdminMenuResponse // @Failure 400 {object} code.Failure -// @Router /api/admin/menu/:id [get] +// @Router /api/admin/menu/{id} [get] +// @Security LoginToken func (h *handler) ListAdminMenu() core.HandlerFunc { return func(c core.Context) { req := new(listAdminMenuRequest) res := new(listAdminMenuResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -57,10 +57,10 @@ func (h *handler) ListAdminMenu() core.HandlerFunc { info, err := h.adminService.Detail(c, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminMenuListError, - code.Text(code.AdminMenuListError)).WithErr(err), + code.Text(code.AdminMenuListError)).WithError(err), ) return } @@ -72,10 +72,10 @@ func (h *handler) ListAdminMenu() core.HandlerFunc { listData, err := h.adminService.ListMenu(c, searchData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminMenuListError, - code.Text(code.AdminMenuListError)).WithErr(err), + code.Text(code.AdminMenuListError)).WithError(err), ) return } diff --git a/internal/api/admin/func_login.go b/internal/api/admin/func_login.go index 8be2fbb..ab0e798 100644 --- a/internal/api/admin/func_login.go +++ b/internal/api/admin/func_login.go @@ -3,15 +3,14 @@ package admin import ( "encoding/json" "net/http" - "time" "github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/password" + "github.com/xinliangnote/go-gin-api/internal/proposal" "github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/errors" ) @@ -28,22 +27,23 @@ type loginResponse struct { // @Summary 管理员登录 // @Description 管理员登录 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param username formData string true "用户名" -// @Param password formData string true "密码" +// @Param password formData string true "MD5后的密码" // @Success 200 {object} loginResponse // @Failure 400 {object} code.Failure -// @Router /api/admin/login [post] +// @Router /api/login [post] +// @Security LoginToken func (h *handler) Login() core.HandlerFunc { return func(c core.Context) { req := new(loginRequest) res := new(loginResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -55,47 +55,38 @@ func (h *handler) Login() core.HandlerFunc { info, err := h.adminService.Detail(c, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } if info == nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(errors.New("未查询出符合条件的用户")), + code.Text(code.AdminLoginError)).WithError(errors.New("未查询出符合条件的用户")), ) return } token := password.GenerateLoginToken(info.Id) - adminCacheData := &struct { - Id int32 `json:"id"` // 主键ID - Username string `json:"username"` // 用户名 - Nickname string `json:"nickname"` // 昵称 - Mobile string `json:"mobile"` // 手机号 - }{ - Id: info.Id, - Username: info.Username, - Nickname: info.Nickname, - Mobile: info.Mobile, + // 用户信息 + sessionUserInfo := &proposal.SessionUserInfo{ + UserID: info.Id, + UserName: info.Username, } - // 用户信息 - adminJsonInfo, _ := json.Marshal(adminCacheData) - // 将用户信息记录到 Redis 中 - err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token, string(adminJsonInfo), time.Hour*24, redis.WithTrace(c.Trace())) + err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token, string(sessionUserInfo.Marshal()), configs.LoginSessionTTL, redis.WithTrace(c.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } @@ -104,10 +95,10 @@ func (h *handler) Login() core.HandlerFunc { searchMenuData.AdminId = info.Id menu, err := h.adminService.MyMenu(c, searchMenuData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } @@ -116,12 +107,12 @@ func (h *handler) Login() core.HandlerFunc { menuJsonInfo, _ := json.Marshal(menu) // 将菜单栏信息记录到 Redis 中 - err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":menu", string(menuJsonInfo), time.Hour*24, redis.WithTrace(c.Trace())) + err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":menu", string(menuJsonInfo), configs.LoginSessionTTL, redis.WithTrace(c.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } @@ -130,10 +121,10 @@ func (h *handler) Login() core.HandlerFunc { searchActionData.AdminId = info.Id action, err := h.adminService.MyAction(c, searchActionData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } @@ -142,12 +133,12 @@ func (h *handler) Login() core.HandlerFunc { actionJsonInfo, _ := json.Marshal(action) // 将可访问接口信息记录到 Redis 中 - err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":action", string(actionJsonInfo), time.Hour*24, redis.WithTrace(c.Trace())) + err = h.cache.Set(configs.RedisKeyPrefixLoginUser+token+":action", string(actionJsonInfo), configs.LoginSessionTTL, redis.WithTrace(c.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLoginError, - code.Text(code.AdminLoginError)).WithErr(err), + code.Text(code.AdminLoginError)).WithError(err), ) return } diff --git a/internal/api/admin/func_logout.go b/internal/api/admin/func_logout.go index ab52467..fbc9930 100644 --- a/internal/api/admin/func_logout.go +++ b/internal/api/admin/func_logout.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/errors" ) @@ -19,21 +18,22 @@ type logoutResponse struct { // @Summary 管理员登出 // @Description 管理员登出 // @Tags API.admin -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Success 200 {object} logoutResponse // @Failure 400 {object} code.Failure // @Router /api/admin/logout [post] +// @Security LoginToken func (h *handler) Logout() core.HandlerFunc { return func(c core.Context) { res := new(logoutResponse) - res.Username = c.UserName() + res.Username = c.SessionUserInfo().UserName if !h.cache.Del(configs.RedisKeyPrefixLoginUser+c.GetHeader(configs.HeaderLoginToken), redis.WithTrace(c.Trace())) { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminLogOutError, - code.Text(code.AdminLogOutError)).WithErr(errors.New("cache del err")), + code.Text(code.AdminLogOutError)).WithError(errors.New("cache del err")), ) return } diff --git a/internal/api/admin/func_modifypassword.go b/internal/api/admin/func_modifypassword.go index f455b03..6150f6f 100644 --- a/internal/api/admin/func_modifypassword.go +++ b/internal/api/admin/func_modifypassword.go @@ -7,9 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/password" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" - - "github.com/spf13/cast" ) type modifyPasswordRequest struct { @@ -25,53 +22,52 @@ type modifyPasswordResponse struct { // @Summary 修改密码 // @Description 修改密码 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param old_password formData string true "旧密码" // @Param new_password formData string true "新密码" // @Success 200 {object} modifyPasswordResponse // @Failure 400 {object} code.Failure // @Router /api/admin/modify_password [patch] +// @Security LoginToken func (h *handler) ModifyPassword() core.HandlerFunc { - return func(c core.Context) { + return func(ctx core.Context) { req := new(modifyPasswordRequest) res := new(modifyPasswordResponse) - if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + if err := ctx.ShouldBindForm(req); err != nil { + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } - userId := cast.ToInt32(c.UserID()) - searchOneData := new(admin.SearchOneData) - searchOneData.Id = userId + searchOneData.Id = ctx.SessionUserInfo().UserID searchOneData.Password = password.GeneratePassword(req.OldPassword) searchOneData.IsUsed = 1 - info, err := h.adminService.Detail(c, searchOneData) + info, err := h.adminService.Detail(ctx, searchOneData) if err != nil || info == nil { - c.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminModifyPasswordError, - code.Text(code.AdminModifyPasswordError)).WithErr(err), + code.Text(code.AdminModifyPasswordError)).WithError(err), ) return } - if err := h.adminService.ModifyPassword(c, userId, req.NewPassword); err != nil { - c.AbortWithError(errno.NewError( + if err := h.adminService.ModifyPassword(ctx, ctx.SessionUserInfo().UserID, req.NewPassword); err != nil { + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminModifyPasswordError, - code.Text(code.AdminModifyPasswordError)).WithErr(err), + code.Text(code.AdminModifyPasswordError)).WithError(err), ) return } - res.Username = c.UserName() - c.Payload(res) + res.Username = ctx.SessionUserInfo().UserName + ctx.Payload(res) } } diff --git a/internal/api/admin/func_modifypersonalinfo.go b/internal/api/admin/func_modifypersonalinfo.go index a3533f5..74db8c1 100644 --- a/internal/api/admin/func_modifypersonalinfo.go +++ b/internal/api/admin/func_modifypersonalinfo.go @@ -6,9 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" - - "github.com/spf13/cast" ) type modifyPersonalInfoRequest struct { @@ -24,42 +21,41 @@ type modifyPersonalInfoResponse struct { // @Summary 修改个人信息 // @Description 修改个人信息 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param nickname formData string true "昵称" // @Param mobile formData string true "手机号" // @Success 200 {object} modifyPersonalInfoResponse // @Failure 400 {object} code.Failure // @Router /api/admin/modify_personal_info [patch] +// @Security LoginToken func (h *handler) ModifyPersonalInfo() core.HandlerFunc { - return func(c core.Context) { + return func(ctx core.Context) { req := new(modifyPersonalInfoRequest) res := new(modifyPersonalInfoResponse) - if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + if err := ctx.ShouldBindForm(req); err != nil { + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } - userId := cast.ToInt32(c.UserID()) - modifyData := new(admin.ModifyData) modifyData.Nickname = req.Nickname modifyData.Mobile = req.Mobile - if err := h.adminService.ModifyPersonalInfo(c, userId, modifyData); err != nil { - c.AbortWithError(errno.NewError( + if err := h.adminService.ModifyPersonalInfo(ctx, ctx.SessionUserInfo().UserID, modifyData); err != nil { + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminModifyPersonalInfoError, - code.Text(code.AdminModifyPersonalInfoError)).WithErr(err), + code.Text(code.AdminModifyPersonalInfoError)).WithError(err), ) return } - res.Username = c.UserName() - c.Payload(res) + res.Username = ctx.SessionUserInfo().UserName + ctx.Payload(res) } } diff --git a/internal/api/admin/func_offline.go b/internal/api/admin/func_offline.go index 44ba9c0..8bc9f0e 100755 --- a/internal/api/admin/func_offline.go +++ b/internal/api/admin/func_offline.go @@ -8,7 +8,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/password" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type offlineRequest struct { @@ -23,31 +22,32 @@ type offlineResponse struct { // @Summary 下线管理员 // @Description 下线管理员 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id formData string true "Hashid" // @Success 200 {object} offlineResponse // @Failure 400 {object} code.Failure // @Router /api/admin/offline [patch] +// @Security LoginToken func (h *handler) Offline() core.HandlerFunc { return func(c core.Context) { req := new(offlineRequest) res := new(offlineResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -56,7 +56,7 @@ func (h *handler) Offline() core.HandlerFunc { b := h.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), redis.WithTrace(c.Trace())) if !b { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminOfflineError, code.Text(code.AdminOfflineError)), diff --git a/internal/api/admin/func_resetpassword.go b/internal/api/admin/func_resetpassword.go index 0764d33..046587f 100755 --- a/internal/api/admin/func_resetpassword.go +++ b/internal/api/admin/func_resetpassword.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type resetPasswordRequest struct { @@ -26,25 +25,26 @@ type resetPasswordResponse struct { // @Success 200 {object} resetPasswordResponse // @Failure 400 {object} code.Failure // @Router /api/admin/reset_password/{id} [patch] +// @Security LoginToken func (h *handler) ResetPassword() core.HandlerFunc { return func(c core.Context) { req := new(resetPasswordRequest) res := new(resetPasswordResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) ResetPassword() core.HandlerFunc { err = h.adminService.ResetPassword(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminResetPasswordError, - code.Text(code.AdminResetPasswordError)).WithErr(err), + code.Text(code.AdminResetPasswordError)).WithError(err), ) return } diff --git a/internal/api/admin/func_updateused.go b/internal/api/admin/func_updateused.go index 70e4052..feff8f7 100755 --- a/internal/api/admin/func_updateused.go +++ b/internal/api/admin/func_updateused.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type updateUsedRequest struct { @@ -21,32 +20,33 @@ type updateUsedResponse struct { // @Summary 更新管理员为启用/禁用 // @Description 更新管理员为启用/禁用 // @Tags API.admin -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id formData string true "Hashid" // @Param used formData int true "是否启用 1:是 -1:否" // @Success 200 {object} updateUsedResponse // @Failure 400 {object} code.Failure // @Router /api/admin/used [patch] +// @Security LoginToken func (h *handler) UpdateUsed() core.HandlerFunc { return func(c core.Context) { req := new(updateUsedRequest) res := new(updateUsedResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc { err = h.adminService.UpdateUsed(c, id, req.Used) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AdminUpdateError, - code.Text(code.AdminUpdateError)).WithErr(err), + code.Text(code.AdminUpdateError)).WithError(err), ) return } diff --git a/internal/api/admin/handler.go b/internal/api/admin/handler.go index 4d1a46e..ada7eda 100644 --- a/internal/api/admin/handler.go +++ b/internal/api/admin/handler.go @@ -18,7 +18,7 @@ type Handler interface { // Login 管理员登录 // @Tags API.admin - // @Router /api/admin/login [post] + // @Router /api/login [post] Login() core.HandlerFunc // Logout 管理员登出 @@ -78,7 +78,7 @@ type Handler interface { // ListAdminMenu 菜单授权列表 // @Tags API.admin - // @Router /api/admin/menu/:id [get] + // @Router /api/admin/menu/{id} [get] ListAdminMenu() core.HandlerFunc } diff --git a/internal/api/authorized/func_create.go b/internal/api/authorized/func_create.go index 8ceb194..4a888c7 100755 --- a/internal/api/authorized/func_create.go +++ b/internal/api/authorized/func_create.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/authorized" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createRequest struct { @@ -23,7 +22,7 @@ type createResponse struct { // @Summary 新增调用方 // @Description 新增调用方 // @Tags API.authorized -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param business_key formData string true "调用方key" // @Param business_developer formData string true "调用方对接人" @@ -31,15 +30,16 @@ type createResponse struct { // @Success 200 {object} createResponse // @Failure 400 {object} code.Failure // @Router /api/authorized [post] +// @Security LoginToken func (h *handler) Create() core.HandlerFunc { return func(c core.Context) { req := new(createRequest) res := new(createResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -51,10 +51,10 @@ func (h *handler) Create() core.HandlerFunc { id, err := h.authorizedService.Create(c, createData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedCreateError, - code.Text(code.AuthorizedCreateError)).WithErr(err), + code.Text(code.AuthorizedCreateError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_createapi.go b/internal/api/authorized/func_createapi.go index 69d7fab..20905df 100755 --- a/internal/api/authorized/func_createapi.go +++ b/internal/api/authorized/func_createapi.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/authorized" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createAPIRequest struct { @@ -23,7 +22,7 @@ type createAPIResponse struct { // @Summary 授权调用方接口地址 // @Description 授权调用方接口地址 // @Tags API.authorized -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id formData string true "HashID" // @Param method formData string true "请求方法" @@ -31,25 +30,26 @@ type createAPIResponse struct { // @Success 200 {object} createAPIResponse // @Failure 400 {object} code.Failure // @Router /api/authorized_api [post] +// @Security LoginToken func (h *handler) CreateAPI() core.HandlerFunc { return func(c core.Context) { req := new(createAPIRequest) res := new(createAPIResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -59,10 +59,10 @@ func (h *handler) CreateAPI() core.HandlerFunc { // 通过 id 查询出 business_key authorizedInfo, err := h.authorizedService.Detail(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedDetailError, - code.Text(code.AuthorizedDetailError)).WithErr(err), + code.Text(code.AuthorizedDetailError)).WithError(err), ) return } @@ -74,10 +74,10 @@ func (h *handler) CreateAPI() core.HandlerFunc { createId, err := h.authorizedService.CreateAPI(c, createAPIData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedCreateAPIError, - code.Text(code.AuthorizedCreateAPIError)).WithErr(err), + code.Text(code.AuthorizedCreateAPIError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_delete.go b/internal/api/authorized/func_delete.go index 4a6c6a8..41ac7df 100755 --- a/internal/api/authorized/func_delete.go +++ b/internal/api/authorized/func_delete.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type deleteRequest struct { @@ -26,25 +25,26 @@ type deleteResponse struct { // @Success 200 {object} deleteResponse // @Failure 400 {object} code.Failure // @Router /api/authorized/{id} [delete] +// @Security LoginToken func (h *handler) Delete() core.HandlerFunc { return func(c core.Context) { req := new(deleteRequest) res := new(deleteResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc { err = h.authorizedService.Delete(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedDeleteError, - code.Text(code.AuthorizedDeleteError)).WithErr(err), + code.Text(code.AuthorizedDeleteError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_deleteapi.go b/internal/api/authorized/func_deleteapi.go index c157a2b..97fccca 100755 --- a/internal/api/authorized/func_deleteapi.go +++ b/internal/api/authorized/func_deleteapi.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type deleteAPIRequest struct { @@ -26,25 +25,26 @@ type deleteAPIResponse struct { // @Success 200 {object} deleteAPIResponse // @Failure 400 {object} code.Failure // @Router /api/authorized_api/{id} [delete] +// @Security LoginToken func (h *handler) DeleteAPI() core.HandlerFunc { return func(c core.Context) { req := new(deleteAPIRequest) res := new(deleteAPIResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) DeleteAPI() core.HandlerFunc { err = h.authorizedService.DeleteAPI(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedDeleteAPIError, - code.Text(code.AuthorizedDeleteAPIError)).WithErr(err), + code.Text(code.AuthorizedDeleteAPIError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_list.go b/internal/api/authorized/func_list.go index 16f19ff..54dbb2c 100755 --- a/internal/api/authorized/func_list.go +++ b/internal/api/authorized/func_list.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/authorized" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/timeutil" "github.com/spf13/cast" @@ -48,10 +47,10 @@ type listResponse struct { // @Summary 调用方列表 // @Description 调用方列表 // @Tags API.authorized -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param page query int false "第几页" -// @Param page_size query string false "每页显示条数" +// @Param page query int true "第几页" default(1) +// @Param page_size query int true "每页显示条数" default(10) // @Param business_key query string false "调用方key" // @Param business_secret query string false "调用方secret" // @Param business_developer query string false "调用方对接人" @@ -59,15 +58,16 @@ type listResponse struct { // @Success 200 {object} listResponse // @Failure 400 {object} code.Failure // @Router /api/authorized [get] +// @Security LoginToken func (h *handler) List() core.HandlerFunc { return func(c core.Context) { req := new(listRequest) res := new(listResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -91,20 +91,20 @@ func (h *handler) List() core.HandlerFunc { resListData, err := h.authorizedService.PageList(c, searchData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedListError, - code.Text(code.AuthorizedListError)).WithErr(err), + code.Text(code.AuthorizedListError)).WithError(err), ) return } resCountData, err := h.authorizedService.PageListCount(c, searchData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedListError, - code.Text(code.AuthorizedListError)).WithErr(err), + code.Text(code.AuthorizedListError)).WithError(err), ) return } @@ -116,10 +116,10 @@ func (h *handler) List() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_listapi.go b/internal/api/authorized/func_listapi.go index f0a0cb8..723653d 100755 --- a/internal/api/authorized/func_listapi.go +++ b/internal/api/authorized/func_listapi.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/authorized" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -31,31 +30,32 @@ type listAPIResponse struct { // @Summary 调用方接口地址列表 // @Description 调用方接口地址列表 // @Tags API.authorized -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id query string true "hashID" // @Success 200 {object} listAPIResponse // @Failure 400 {object} code.Failure // @Router /api/authorized_api [get] +// @Security LoginToken func (h *handler) ListAPI() core.HandlerFunc { return func(c core.Context) { req := new(listAPIRequest) res := new(listAPIResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -65,10 +65,10 @@ func (h *handler) ListAPI() core.HandlerFunc { // 通过 id 查询出 business_key authorizedInfo, err := h.authorizedService.Detail(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedDetailError, - code.Text(code.AuthorizedDetailError)).WithErr(err), + code.Text(code.AuthorizedDetailError)).WithError(err), ) return } @@ -80,10 +80,10 @@ func (h *handler) ListAPI() core.HandlerFunc { resListData, err := h.authorizedService.ListAPI(c, searchAPIData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedListAPIError, - code.Text(code.AuthorizedListAPIError)).WithErr(err), + code.Text(code.AuthorizedListAPIError)).WithError(err), ) return } @@ -93,10 +93,10 @@ func (h *handler) ListAPI() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/authorized/func_updateused.go b/internal/api/authorized/func_updateused.go index 38d1a13..ffe6dda 100755 --- a/internal/api/authorized/func_updateused.go +++ b/internal/api/authorized/func_updateused.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type updateUsedRequest struct { @@ -21,32 +20,33 @@ type updateUsedResponse struct { // @Summary 更新调用方为启用/禁用 // @Description 更新调用方为启用/禁用 // @Tags API.authorized -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param id formData string true "Hashid" +// @Param id formData string true "hashID" // @Param used formData int true "是否启用 1:是 -1:否" // @Success 200 {object} updateUsedResponse // @Failure 400 {object} code.Failure // @Router /api/authorized/used [patch] +// @Security LoginToken func (h *handler) UpdateUsed() core.HandlerFunc { return func(c core.Context) { req := new(updateUsedRequest) res := new(updateUsedResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc { err = h.authorizedService.UpdateUsed(c, id, req.Used) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedUpdateError, - code.Text(code.AuthorizedUpdateError)).WithErr(err), + code.Text(code.AuthorizedUpdateError)).WithError(err), ) return } diff --git a/internal/api/config/func_email.go b/internal/api/config/func_email.go index b42e7be..0794862 100755 --- a/internal/api/config/func_email.go +++ b/internal/api/config/func_email.go @@ -8,7 +8,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/pkg/env" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/mail" "github.com/spf13/cast" @@ -31,7 +30,7 @@ type emailResponse struct { // @Summary 修改邮件配置 // @Description 修改邮件配置 // @Tags API.config -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param host formData string true "邮箱服务器" // @Param port formData string true "端口" @@ -41,15 +40,16 @@ type emailResponse struct { // @Success 200 {object} emailResponse // @Failure 400 {object} code.Failure // @Router /api/config/email [patch] +// @Security LoginToken func (h *handler) Email() core.HandlerFunc { return func(c core.Context) { req := new(emailRequest) res := new(emailResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -64,10 +64,10 @@ func (h *handler) Email() core.HandlerFunc { Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName, env.Active().Value()), } if err := mail.Send(options); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.SendEmailError, - code.Text(code.SendEmailError)+err.Error()).WithErr(err), + code.Text(code.SendEmailError)+err.Error()).WithError(err), ) return } @@ -80,10 +80,10 @@ func (h *handler) Email() core.HandlerFunc { err := viper.WriteConfig() if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.WriteConfigError, - code.Text(code.WriteConfigError)).WithErr(err), + code.Text(code.WriteConfigError)).WithError(err), ) return } diff --git a/internal/api/cron/func_create.go b/internal/api/cron/func_create.go index 812a06a..e57576c 100755 --- a/internal/api/cron/func_create.go +++ b/internal/api/cron/func_create.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/services/cron" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createRequest struct { @@ -35,7 +34,7 @@ type createResponse struct { // @Summary 创建任务 // @Description 创建任务 // @Tags API.cron -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param name formData string true "任务名称" // @Param spec formData string true "crontab 表达式" @@ -54,15 +53,16 @@ type createResponse struct { // @Success 200 {object} createResponse // @Failure 400 {object} code.Failure // @Router /api/cron [post] +// @Security LoginToken func (h *handler) Create() core.HandlerFunc { return func(ctx core.Context) { req := new(createRequest) res := new(createResponse) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } @@ -85,10 +85,10 @@ func (h *handler) Create() core.HandlerFunc { id, err := h.cronService.Create(ctx, createData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronCreateError, - code.Text(code.CronCreateError)).WithErr(err), + code.Text(code.CronCreateError)).WithError(err), ) return } diff --git a/internal/api/cron/func_detail.go b/internal/api/cron/func_detail.go index 211ab17..47c4a71 100755 --- a/internal/api/cron/func_detail.go +++ b/internal/api/cron/func_detail.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/services/cron" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -42,26 +41,27 @@ type detailResponse struct { // @Param id path string true "hashId" // @Success 200 {object} detailResponse // @Failure 400 {object} code.Failure -// @Router /api/cron/:id [get] +// @Router /api/cron/{id} [get] +// @Security LoginToken func (h *handler) Detail() core.HandlerFunc { return func(ctx core.Context) { req := new(detailRequest) res := new(detailResponse) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -71,10 +71,10 @@ func (h *handler) Detail() core.HandlerFunc { info, err := h.cronService.Detail(ctx, searchOneData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronDetailError, - code.Text(code.CronDetailError)).WithErr(err), + code.Text(code.CronDetailError)).WithError(err), ) return } diff --git a/internal/api/cron/func_execute.go b/internal/api/cron/func_execute.go index f373ed6..60f56d7 100755 --- a/internal/api/cron/func_execute.go +++ b/internal/api/cron/func_execute.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -28,36 +27,37 @@ type executeResponse struct { // @Param id path string true "hashId" // @Success 200 {object} detailResponse // @Failure 400 {object} code.Failure -// @Router /api/cron/:id [patch] +// @Router /api/cron/{id} [patch] +// @Security LoginToken func (h *handler) Execute() core.HandlerFunc { return func(ctx core.Context) { req := new(executeRequest) res := new(executeResponse) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } err = h.cronService.Execute(ctx, cast.ToInt32(ids[0])) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronExecuteError, - code.Text(code.CronExecuteError)).WithErr(err), + code.Text(code.CronExecuteError)).WithError(err), ) return } diff --git a/internal/api/cron/func_list.go b/internal/api/cron/func_list.go index 812db3d..0176c80 100755 --- a/internal/api/cron/func_list.go +++ b/internal/api/cron/func_list.go @@ -8,7 +8,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/repository/mysql/cron_task" "github.com/xinliangnote/go-gin-api/internal/services/cron" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/timeutil" "github.com/spf13/cast" @@ -58,25 +57,26 @@ type listResponse struct { // @Summary 任务列表 // @Description 任务列表 // @Tags API.cron -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param page query int false "第几页" -// @Param page_size query string false "每页显示条数" +// @Param page query int true "第几页" default(1) +// @Param page_size query int true "每页显示条数" default(10) // @Param name query string false "任务名称" // @Param protocol query int false "执行方式 1:shell 2:http" // @Param is_used query int false "是否启用 1:是 -1:否" // @Success 200 {object} listResponse // @Failure 400 {object} code.Failure // @Router /api/cron [get] +// @Security LoginToken func (h *handler) List() core.HandlerFunc { return func(ctx core.Context) { req := new(listRequest) res := new(listResponse) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } @@ -100,20 +100,20 @@ func (h *handler) List() core.HandlerFunc { resListData, err := h.cronService.PageList(ctx, searchData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronListError, - code.Text(code.CronListError)).WithErr(err), + code.Text(code.CronListError)).WithError(err), ) return } resCountData, err := h.cronService.PageListCount(ctx, searchData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronListError, - code.Text(code.CronListError)).WithErr(err), + code.Text(code.CronListError)).WithError(err), ) return } @@ -126,10 +126,10 @@ func (h *handler) List() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/cron/func_modify.go b/internal/api/cron/func_modify.go index 82e5014..bb3ab37 100755 --- a/internal/api/cron/func_modify.go +++ b/internal/api/cron/func_modify.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/services/cron" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type modifyRequest struct { @@ -36,9 +35,9 @@ type modifyResponse struct { // @Summary 编辑任务 // @Description 编辑任务 // @Tags API.cron -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param id formData string true "Hashid" +// @Param id formData string true "hashID" // @Param name formData string true "任务名称" // @Param spec formData string true "crontab 表达式" // @Param command formData string true "执行命令" @@ -56,25 +55,26 @@ type modifyResponse struct { // @Success 200 {object} modifyResponse // @Failure 400 {object} code.Failure // @Router /api/cron/{id} [post] +// @Security LoginToken func (h *handler) Modify() core.HandlerFunc { return func(ctx core.Context) { req := new(modifyRequest) res := new(modifyResponse) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -98,10 +98,10 @@ func (h *handler) Modify() core.HandlerFunc { modifyData.IsUsed = req.IsUsed if err := h.cronService.Modify(ctx, id, modifyData); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.CronUpdateError, - code.Text(code.CronUpdateError)).WithErr(err), + code.Text(code.CronUpdateError)).WithError(err), ) return } diff --git a/internal/api/cron/func_updateused.go b/internal/api/cron/func_updateused.go index 4772596..e81d5e1 100755 --- a/internal/api/cron/func_updateused.go +++ b/internal/api/cron/func_updateused.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type updateUsedRequest struct { @@ -22,32 +21,33 @@ type updateUsedResponse struct { // @Summary 更新任务为启用/禁用 // @Description 更新任务为启用/禁用 // @Tags API.cron -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param id formData string true "Hashid" +// @Param id formData string true "hashID" // @Param used formData int true "是否启用 1:是 -1:否" // @Success 200 {object} updateUsedResponse // @Failure 400 {object} code.Failure // @Router /api/cron/used [patch] +// @Security LoginToken func (h *handler) UpdateUsed() core.HandlerFunc { return func(ctx core.Context) { req := new(updateUsedRequest) res := new(updateUsedResponse) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -56,10 +56,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc { err = h.cronService.UpdateUsed(ctx, id, req.Used) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.AdminUpdateError, - code.Text(code.AdminUpdateError)).WithErr(err), + code.Text(code.AdminUpdateError)).WithError(err), ) return } diff --git a/internal/api/cron/handler.go b/internal/api/cron/handler.go index f4b692b..7b6c66d 100644 --- a/internal/api/cron/handler.go +++ b/internal/api/cron/handler.go @@ -39,12 +39,12 @@ type Handler interface { // Detail 获取单条任务详情 // @Tags API.cron - // @Router /api/cron/:id [get] + // @Router /api/cron/{id} [get] Detail() core.HandlerFunc // Execute 手动执行任务 // @Tags API.cron - // @Router /api/cron/:id [patch] + // @Router /api/cron/{id} [patch] Execute() core.HandlerFunc } diff --git a/internal/api/helper/func_md5.go b/internal/api/helper/func_md5.go new file mode 100755 index 0000000..caa2ebb --- /dev/null +++ b/internal/api/helper/func_md5.go @@ -0,0 +1,49 @@ +package helper + +import ( + "crypto/md5" + "encoding/hex" + "net/http" + + "github.com/xinliangnote/go-gin-api/internal/code" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" +) + +type md5Request struct { + Str string `uri:"str" binding:"required"` // 需要加密的字符串 +} + +type md5Response struct { + Md5Str string `json:"md5_str"` // MD5后的字符串 +} + +// Md5 加密 +// @Summary 加密 +// @Description 加密 +// @Tags Helper +// @Accept application/x-www-form-urlencoded +// @Produce json +// @Param str path string true "需要加密的字符串" +// @Success 200 {object} md5Response +// @Failure 400 {object} code.Failure +// @Router /helper/md5/{str} [get] +func (h *handler) Md5() core.HandlerFunc { + return func(ctx core.Context) { + req := new(md5Request) + res := new(md5Response) + + if err := ctx.ShouldBindURI(req); err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithError(err), + ) + return + } + + m := md5.New() + m.Write([]byte(req.Str)) + res.Md5Str = hex.EncodeToString(m.Sum(nil)) + ctx.Payload(res) + } +} diff --git a/internal/api/helper/func_sign.go b/internal/api/helper/func_sign.go new file mode 100755 index 0000000..c9fb3d9 --- /dev/null +++ b/internal/api/helper/func_sign.go @@ -0,0 +1,100 @@ +package helper + +import ( + "fmt" + "net/http" + "net/url" + + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/code" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/pkg/errors" + "github.com/xinliangnote/go-gin-api/pkg/signature" +) + +type signRequest struct { + Key string `form:"key" binding:"required"` // 调用方 KEY + Path string `form:"path" binding:"required"` // 请求路径 (不附带 querystring),例如:/api/login + Method string `form:"method" binding:"required"` // 请求方式,例如:POST + Params string `form:"params" binding:"required"` // 请求参数,例如:username=tom&password=123456 +} + +type signResponse struct { + Authorization string `json:"authorization"` // 签名信息-Authorization + AuthorizationDate string `json:"authorization_date"` // 签名信息-Authorization-Date +} + +// Sign 签名 +// @Summary 签名 +// @Description 签名 +// @Tags Helper +// @Accept application/x-www-form-urlencoded +// @Produce json +// @Param key formData string true "调用方 KEY" +// @Param path formData string true "请求路径 (不附带 querystring),例如:/api/login" +// @Param method formData string true "请求方式,例如:POST" +// @Param params formData string true "请求参数,例如:username=tom&password=123456" +// @Success 200 {object} signResponse +// @Failure 400 {object} code.Failure +// @Router /helper/sign [post] +func (h *handler) Sign() core.HandlerFunc { + return func(ctx core.Context) { + req := new(signRequest) + res := new(signResponse) + + if err := ctx.ShouldBindForm(req); err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.ParamBindError, + code.Text(code.ParamBindError)).WithError(err), + ) + return + } + + authorizedInfo, err := h.authorizedService.DetailByKey(ctx, req.Key) + if err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(err), + ) + return + } + + if authorizedInfo.IsUsed == -1 { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(errors.New(req.Key + " 已被禁止调用")), + ) + return + } + + fmt.Println(req.Params) + + params, err := url.ParseQuery(req.Params) + if err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.AuthorizationError, + "params 传递格式不正确"), + ) + return + } + + sign := signature.New(req.Key, authorizedInfo.Secret, configs.HeaderSignTokenTimeout) + authorized, date, err := sign.Generate(req.Path, req.Method, params) + if err != nil { + ctx.AbortWithError(core.Error( + http.StatusBadRequest, + code.AuthorizationError, + "sign 生成失败"), + ) + return + } + + res.Authorization = authorized + res.AuthorizationDate = date + ctx.Payload(res) + } +} diff --git a/internal/api/helper/handler.go b/internal/api/helper/handler.go new file mode 100644 index 0000000..4e59169 --- /dev/null +++ b/internal/api/helper/handler.go @@ -0,0 +1,42 @@ +package helper + +import ( + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/repository/mysql" + "github.com/xinliangnote/go-gin-api/internal/repository/redis" + "github.com/xinliangnote/go-gin-api/internal/services/authorized" + + "go.uber.org/zap" +) + +var _ Handler = (*handler)(nil) + +type Handler interface { + i() + + // Md5 加密 + // @Tags Helper + // @Router /helper/md5/{str} [get] + Md5() core.HandlerFunc + + // Sign 签名 + // @Tags Helper + // @Router /helper/sign [post] + Sign() core.HandlerFunc +} + +type handler struct { + logger *zap.Logger + db mysql.Repo + authorizedService authorized.Service +} + +func New(logger *zap.Logger, db mysql.Repo, cache redis.Repo) Handler { + return &handler{ + logger: logger, + db: db, + authorizedService: authorized.New(db, cache), + } +} + +func (h *handler) i() {} diff --git a/internal/api/menu/func_create.go b/internal/api/menu/func_create.go index 04a667c..27f0596 100755 --- a/internal/api/menu/func_create.go +++ b/internal/api/menu/func_create.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/menu" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -28,21 +27,22 @@ type createResponse struct { // @Summary 创建/编辑菜单 // @Description 创建/编辑菜单 // @Tags API.menu -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param Request body createRequest true "请求信息" // @Success 200 {object} createResponse // @Failure 400 {object} code.Failure // @Router /api/menu [post] +// @Security LoginToken func (h *handler) Create() core.HandlerFunc { return func(c core.Context) { req := new(createRequest) res := new(createResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -50,10 +50,10 @@ func (h *handler) Create() core.HandlerFunc { if req.Id != "" { // 编辑功能 ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -67,10 +67,10 @@ func (h *handler) Create() core.HandlerFunc { err = h.menuService.Modify(c, id, updateData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuUpdateError, - code.Text(code.MenuUpdateError)).WithErr(err), + code.Text(code.MenuUpdateError)).WithError(err), ) return } @@ -97,10 +97,10 @@ func (h *handler) Create() core.HandlerFunc { id, err := h.menuService.Create(c, createData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuCreateError, - code.Text(code.MenuCreateError)).WithErr(err), + code.Text(code.MenuCreateError)).WithError(err), ) return } diff --git a/internal/api/menu/func_createaction.go b/internal/api/menu/func_createaction.go index 30864a7..cdc7878 100755 --- a/internal/api/menu/func_createaction.go +++ b/internal/api/menu/func_createaction.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/menu" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type createActionRequest struct { @@ -23,7 +22,7 @@ type createActionResponse struct { // @Summary 创建功能权限 // @Description 创建功能权限 // @Tags API.menu -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id formData string true "HashID" // @Param method formData string true "请求方法" @@ -31,25 +30,26 @@ type createActionResponse struct { // @Success 200 {object} createActionResponse // @Failure 400 {object} code.Failure // @Router /api/menu_action [post] +// @Security LoginToken func (h *handler) CreateAction() core.HandlerFunc { return func(c core.Context) { req := new(createActionRequest) res := new(createActionResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -60,10 +60,10 @@ func (h *handler) CreateAction() core.HandlerFunc { searchOneData.Id = id menuInfo, err := h.menuService.Detail(c, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuDetailError, - code.Text(code.MenuDetailError)).WithErr(err), + code.Text(code.MenuDetailError)).WithError(err), ) return } @@ -75,10 +75,10 @@ func (h *handler) CreateAction() core.HandlerFunc { createId, err := h.menuService.CreateAction(c, createActionData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuCreateActionError, - code.Text(code.MenuCreateActionError)).WithErr(err), + code.Text(code.MenuCreateActionError)).WithError(err), ) return } diff --git a/internal/api/menu/func_delete.go b/internal/api/menu/func_delete.go index da4fcd6..fc8c4a0 100755 --- a/internal/api/menu/func_delete.go +++ b/internal/api/menu/func_delete.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type deleteRequest struct { @@ -26,25 +25,26 @@ type deleteResponse struct { // @Success 200 {object} deleteResponse // @Failure 400 {object} code.Failure // @Router /api/menu/{id} [delete] +// @Security LoginToken func (h *handler) Delete() core.HandlerFunc { return func(c core.Context) { req := new(deleteRequest) res := new(deleteResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc { err = h.menuService.Delete(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuDeleteError, - code.Text(code.MenuDeleteError)).WithErr(err), + code.Text(code.MenuDeleteError)).WithError(err), ) return } diff --git a/internal/api/menu/func_deleteaction.go b/internal/api/menu/func_deleteaction.go index 8ee1889..cd4675c 100755 --- a/internal/api/menu/func_deleteaction.go +++ b/internal/api/menu/func_deleteaction.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type deleteActionRequest struct { @@ -26,25 +25,26 @@ type deleteActionResponse struct { // @Success 200 {object} deleteActionResponse // @Failure 400 {object} code.Failure // @Router /api/menu_action/{id} [delete] +// @Security LoginToken func (h *handler) DeleteAction() core.HandlerFunc { return func(c core.Context) { req := new(deleteActionRequest) res := new(deleteActionResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) DeleteAction() core.HandlerFunc { err = h.menuService.DeleteAction(c, id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuDeleteActionError, - code.Text(code.MenuDeleteActionError)).WithErr(err), + code.Text(code.MenuDeleteActionError)).WithError(err), ) return } diff --git a/internal/api/menu/func_detail.go b/internal/api/menu/func_detail.go index d458853..c6f7b6b 100755 --- a/internal/api/menu/func_detail.go +++ b/internal/api/menu/func_detail.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/menu" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type detailRequest struct { @@ -25,31 +24,32 @@ type detailResponse struct { // @Summary 菜单详情 // @Description 菜单详情 // @Tags API.menu -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id path string true "hashId" // @Success 200 {object} detailResponse // @Failure 400 {object} code.Failure // @Router /api/menu/{id} [get] +// @Security LoginToken func (h *handler) Detail() core.HandlerFunc { return func(c core.Context) { req := new(detailRequest) res := new(detailResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -61,10 +61,10 @@ func (h *handler) Detail() core.HandlerFunc { info, err := h.menuService.Detail(c, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuDetailError, - code.Text(code.MenuDetailError)).WithErr(err), + code.Text(code.MenuDetailError)).WithError(err), ) return } diff --git a/internal/api/menu/func_list.go b/internal/api/menu/func_list.go index e546cff..777097c 100755 --- a/internal/api/menu/func_list.go +++ b/internal/api/menu/func_list.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/menu" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -30,20 +29,21 @@ type listResponse struct { // @Summary 菜单列表 // @Description 菜单列表 // @Tags API.menu -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Success 200 {object} listResponse // @Failure 400 {object} code.Failure // @Router /api/menu [get] +// @Security LoginToken func (h *handler) List() core.HandlerFunc { return func(c core.Context) { res := new(listResponse) resListData, err := h.menuService.List(c, new(menu.SearchData)) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuListError, - code.Text(code.MenuListError)).WithErr(err), + code.Text(code.MenuListError)).WithError(err), ) return } @@ -53,10 +53,10 @@ func (h *handler) List() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/menu/func_listaction.go b/internal/api/menu/func_listaction.go index 4bf9b78..bd463c5 100755 --- a/internal/api/menu/func_listaction.go +++ b/internal/api/menu/func_listaction.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/services/menu" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -31,31 +30,32 @@ type listActionResponse struct { // @Summary 功能权限列表 // @Description 功能权限列表 // @Tags API.menu -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id query string true "hashID" // @Success 200 {object} listActionResponse // @Failure 400 {object} code.Failure // @Router /api/menu_action [get] +// @Security LoginToken func (h *handler) ListAction() core.HandlerFunc { return func(c core.Context) { req := new(listActionRequest) res := new(listActionResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -67,10 +67,10 @@ func (h *handler) ListAction() core.HandlerFunc { menuInfo, err := h.menuService.Detail(c, searchOneData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuDetailError, - code.Text(code.MenuDetailError)).WithErr(err), + code.Text(code.MenuDetailError)).WithError(err), ) return } @@ -82,10 +82,10 @@ func (h *handler) ListAction() core.HandlerFunc { resListData, err := h.menuService.ListAction(c, searchListData) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizedListAPIError, - code.Text(code.AuthorizedListAPIError)).WithErr(err), + code.Text(code.AuthorizedListAPIError)).WithError(err), ) return } @@ -95,10 +95,10 @@ func (h *handler) ListAction() core.HandlerFunc { for k, v := range resListData { hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/menu/func_updatesort.go b/internal/api/menu/func_updatesort.go index 628e122..1e38f0c 100755 --- a/internal/api/menu/func_updatesort.go +++ b/internal/api/menu/func_updatesort.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type updateSortRequest struct { @@ -21,32 +20,33 @@ type updateSortResponse struct { // @Summary 更新菜单排序 // @Description 更新菜单排序 // @Tags API.menu -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param id formData string true "Hashid" +// @Param id formData string true "hashId" // @Param sort formData int true "排序" // @Success 200 {object} updateSortResponse // @Failure 400 {object} code.Failure // @Router /api/menu/sort [patch] +// @Security LoginToken func (h *handler) UpdateSort() core.HandlerFunc { return func(c core.Context) { req := new(updateSortRequest) res := new(updateSortResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -55,10 +55,10 @@ func (h *handler) UpdateSort() core.HandlerFunc { err = h.menuService.UpdateSort(c, id, req.Sort) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuUpdateError, - code.Text(code.MenuUpdateError)).WithErr(err), + code.Text(code.MenuUpdateError)).WithError(err), ) return } diff --git a/internal/api/menu/func_updateused.go b/internal/api/menu/func_updateused.go index ae4d9f9..ba8d686 100755 --- a/internal/api/menu/func_updateused.go +++ b/internal/api/menu/func_updateused.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type updateUsedRequest struct { @@ -21,32 +20,33 @@ type updateUsedResponse struct { // @Summary 更新菜单为启用/禁用 // @Description 更新菜单为启用/禁用 // @Tags API.menu -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json -// @Param id formData string true "Hashid" +// @Param id formData string true "hashId" // @Param used formData int true "是否启用 1:是 -1:否" // @Success 200 {object} updateUsedResponse // @Failure 400 {object} code.Failure // @Router /api/menu/used [patch] +// @Security LoginToken func (h *handler) UpdateUsed() core.HandlerFunc { return func(c core.Context) { req := new(updateUsedRequest) res := new(updateUsedResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } ids, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } @@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc { err = h.menuService.UpdateUsed(c, id, req.Used) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MenuUpdateError, - code.Text(code.MenuUpdateError)).WithErr(err), + code.Text(code.MenuUpdateError)).WithError(err), ) return } diff --git a/internal/api/tool/func_clearcache.go b/internal/api/tool/func_clearcache.go index 5b7355f..e804da6 100644 --- a/internal/api/tool/func_clearcache.go +++ b/internal/api/tool/func_clearcache.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type clearCacheRequest struct { @@ -21,27 +20,28 @@ type clearCacheResponse struct { // @Summary 清空缓存 // @Description 清空缓存 // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param redis_key formData string true "Redis Key" // @Success 200 {object} searchCacheResponse // @Failure 400 {object} code.Failure // @Router /api/tool/cache/clear [patch] +// @Security LoginToken func (h *handler) ClearCache() core.HandlerFunc { return func(c core.Context) { req := new(clearCacheRequest) res := new(clearCacheResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } if b := h.cache.Exists(req.RedisKey); b != true { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.CacheNotExist, code.Text(code.CacheNotExist)), @@ -51,7 +51,7 @@ func (h *handler) ClearCache() core.HandlerFunc { b := h.cache.Del(req.RedisKey, redis.WithTrace(c.Trace())) if b != true { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.CacheDelError, code.Text(code.CacheDelError)), diff --git a/internal/api/tool/func_dbs.go b/internal/api/tool/func_dbs.go index 0f97af6..118aead 100644 --- a/internal/api/tool/func_dbs.go +++ b/internal/api/tool/func_dbs.go @@ -17,11 +17,12 @@ type dbData struct { // @Summary 查询 DB // @Description 查询 DB // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Success 200 {object} dbsResponse // @Failure 400 {object} code.Failure // @Router /api/tool/data/dbs [get] +// @Security LoginToken func (h *handler) Dbs() core.HandlerFunc { return func(c core.Context) { res := new(dbsResponse) diff --git a/internal/api/tool/func_hashidsdecode.go b/internal/api/tool/func_hashidsdecode.go index 6bd2445..4863b66 100755 --- a/internal/api/tool/func_hashidsdecode.go +++ b/internal/api/tool/func_hashidsdecode.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type hashIdsDecodeRequest struct { @@ -20,31 +19,32 @@ type hashIdsDecodeResponse struct { // @Summary HashIds 解密 // @Description HashIds 解密 // @Tags API.tool -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id path string true "需解密的密文" // @Success 200 {object} hashIdsDecodeResponse // @Failure 400 {object} code.Failure // @Router /api/tool/hashids/decode/{id} [get] +// @Security LoginToken func (h *handler) HashIdsDecode() core.HandlerFunc { return func(c core.Context) { req := new(hashIdsDecodeRequest) res := new(hashIdsDecodeResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } hashId, err := h.hashids.HashidsDecode(req.Id) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsDecodeError, - code.Text(code.HashIdsDecodeError)).WithErr(err), + code.Text(code.HashIdsDecodeError)).WithError(err), ) return } diff --git a/internal/api/tool/func_hashidsencode.go b/internal/api/tool/func_hashidsencode.go index 49b6210..d52d78e 100755 --- a/internal/api/tool/func_hashidsencode.go +++ b/internal/api/tool/func_hashidsencode.go @@ -5,7 +5,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -22,31 +21,32 @@ type hashIdsEncodeResponse struct { // @Summary HashIds 加密 // @Description HashIds 加密 // @Tags API.tool -// @Accept json +// @Accept application/x-www-form-urlencoded // @Produce json // @Param id path string true "需加密的数字" // @Success 200 {object} hashIdsEncodeResponse // @Failure 400 {object} code.Failure // @Router /api/tool/hashids/encode/{id} [get] +// @Security LoginToken func (h *handler) HashIdsEncode() core.HandlerFunc { return func(c core.Context) { req := new(hashIdsEncodeRequest) res := new(hashIdsEncodeResponse) if err := c.ShouldBindURI(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(req.Id)}) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.HashIdsEncodeError, - code.Text(code.HashIdsEncodeError)).WithErr(err), + code.Text(code.HashIdsEncodeError)).WithError(err), ) return } diff --git a/internal/api/tool/func_searchcache.go b/internal/api/tool/func_searchcache.go index fd9af20..1f2643b 100644 --- a/internal/api/tool/func_searchcache.go +++ b/internal/api/tool/func_searchcache.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type searchCacheRequest struct { @@ -22,27 +21,28 @@ type searchCacheResponse struct { // @Summary 查询缓存 // @Description 查询缓存 // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param redis_key formData string true "Redis Key" // @Success 200 {object} searchCacheResponse // @Failure 400 {object} code.Failure // @Router /api/tool/cache/search [post] +// @Security LoginToken func (h *handler) SearchCache() core.HandlerFunc { return func(c core.Context) { req := new(searchCacheRequest) res := new(searchCacheResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } if b := h.cache.Exists(req.RedisKey); b != true { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.CacheNotExist, code.Text(code.CacheNotExist)), @@ -52,20 +52,20 @@ func (h *handler) SearchCache() core.HandlerFunc { val, err := h.cache.Get(req.RedisKey, redis.WithTrace(c.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.CacheGetError, - code.Text(code.CacheGetError)).WithErr(err), + code.Text(code.CacheGetError)).WithError(err), ) return } ttl, err := h.cache.TTL(req.RedisKey) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.CacheGetError, - code.Text(code.CacheGetError)).WithErr(err), + code.Text(code.CacheGetError)).WithError(err), ) return } diff --git a/internal/api/tool/func_searchmysql.go b/internal/api/tool/func_searchmysql.go index 922af41..f7795f4 100644 --- a/internal/api/tool/func_searchmysql.go +++ b/internal/api/tool/func_searchmysql.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/spf13/cast" ) @@ -72,7 +71,7 @@ var filterListKeyword = []string{ // @Summary 执行 SQL 语句 // @Description 执行 SQL 语句 // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param db_name formData string true "数据库名称" // @Param table_name formData string true "数据表名称" @@ -80,22 +79,23 @@ var filterListKeyword = []string{ // @Success 200 {object} searchMySQLResponse // @Failure 400 {object} code.Failure // @Router /api/tool/data/mysql [post] +// @Security LoginToken func (h *handler) SearchMySQL() core.HandlerFunc { return func(c core.Context) { req := new(searchMySQLRequest) res := new(searchMySQLResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } sql := strings.ToLower(strings.TrimSpace(req.SQL)) if sql == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, "SQL 语句不能为空!"), @@ -104,7 +104,7 @@ func (h *handler) SearchMySQL() core.HandlerFunc { } if preFilterList[string([]byte(sql)[:6])] { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, "SQL 语句不能以 "+string([]byte(sql)[:6])+" 开头!"), @@ -124,7 +124,7 @@ func (h *handler) SearchMySQL() core.HandlerFunc { } if !isWhiteList { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, "SQL 语句存在敏感词: "+f+"!"), @@ -142,10 +142,10 @@ func (h *handler) SearchMySQL() core.HandlerFunc { // TODO 后期支持查询多个数据库 rows, err := h.db.GetDbR().Raw(sql).Rows() if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - "MySQL "+err.Error()).WithErr(err), + "MySQL "+err.Error()).WithError(err), ) return } @@ -191,10 +191,10 @@ func (h *handler) SearchMySQL() core.HandlerFunc { rows, err = h.db.GetDbR().Raw(sqlTableColumn).Rows() if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - "MySQL "+err.Error()).WithErr(err), + "MySQL "+err.Error()).WithError(err), ) return } diff --git a/internal/api/tool/func_sendmessage.go b/internal/api/tool/func_sendmessage.go index 6d52b9e..ca20f30 100644 --- a/internal/api/tool/func_sendmessage.go +++ b/internal/api/tool/func_sendmessage.go @@ -8,7 +8,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/websocket/sysmessage" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/timeutil" ) @@ -24,12 +23,13 @@ type sendMessageResponse struct { // @Summary 发送消息 // @Description 发送消息 // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param message formData string true "消息内容" // @Success 200 {object} sendMessageResponse // @Failure 400 {object} code.Failure // @Router /api/tool/send_message [post] +// @Security LoginToken func (h *handler) SendMessage() core.HandlerFunc { type messageBody struct { Username string `json:"username"` @@ -41,45 +41,45 @@ func (h *handler) SendMessage() core.HandlerFunc { req := new(sendMessageRequest) res := new(sendMessageResponse) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - validation.Error(err)).WithErr(err), + validation.Error(err)).WithError(err), ) return } conn, err := sysmessage.GetConn() if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.SocketConnectError, - code.Text(code.SocketConnectError)).WithErr(err), + code.Text(code.SocketConnectError)).WithError(err), ) return } messageData := new(messageBody) - messageData.Username = ctx.UserName() + messageData.Username = ctx.SessionUserInfo().UserName messageData.Message = req.Message messageData.Time = timeutil.CSTLayoutString() messageJsonData, err := json.Marshal(messageData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.SocketSendError, - code.Text(code.SocketSendError)).WithErr(err), + code.Text(code.SocketSendError)).WithError(err), ) return } err = conn.OnSend(messageJsonData) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.SocketSendError, - code.Text(code.SocketSendError)).WithErr(err), + code.Text(code.SocketSendError)).WithError(err), ) return } diff --git a/internal/api/tool/func_tables.go b/internal/api/tool/func_tables.go index 9aecdfd..8f6cd4c 100644 --- a/internal/api/tool/func_tables.go +++ b/internal/api/tool/func_tables.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" ) type tablesRequest struct { @@ -26,21 +25,22 @@ type tableData struct { // @Summary 查询 Table // @Description 查询 Table // @Tags API.tool -// @Accept multipart/form-data +// @Accept application/x-www-form-urlencoded // @Produce json // @Param db_name formData string true "数据库名称" // @Success 200 {object} tablesResponse // @Failure 400 {object} code.Failure // @Router /api/tool/data/tables [post] +// @Security LoginToken func (h *handler) Tables() core.HandlerFunc { return func(c core.Context) { req := new(tablesRequest) res := new(tablesResponse) if err := c.ShouldBindForm(req); err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -50,10 +50,10 @@ func (h *handler) Tables() core.HandlerFunc { // TODO 后期支持查询多个数据库 rows, err := h.db.GetDbR().Raw(sqlTables).Rows() if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)).WithErr(err), + code.Text(code.MySQLExecError)).WithError(err), ) return } diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go new file mode 100644 index 0000000..a32b7d2 --- /dev/null +++ b/internal/metrics/metrics.go @@ -0,0 +1,26 @@ +package metrics + +import ( + "github.com/xinliangnote/go-gin-api/internal/proposal" + + "go.uber.org/zap" +) + +// RecordHandler 指标处理 +func RecordHandler(logger *zap.Logger) func(msg *proposal.MetricsMessage) { + if logger == nil { + panic("logger required") + } + + return func(msg *proposal.MetricsMessage) { + RecordMetrics( + msg.Method, + msg.Path, + msg.IsSuccess, + msg.HTTPCode, + msg.BusinessCode, + msg.CostSeconds, + msg.TraceID, + ) + } +} diff --git a/internal/pkg/metrics/metrics.go b/internal/metrics/prometheus.go similarity index 89% rename from internal/pkg/metrics/metrics.go rename to internal/metrics/prometheus.go index 34e586d..bd0e581 100644 --- a/internal/pkg/metrics/metrics.go +++ b/internal/metrics/prometheus.go @@ -37,15 +37,15 @@ func init() { } // RecordMetrics 记录指标 -func RecordMetrics(method, uri string, success bool, httpCode, businessCode int, costSeconds float64, traceId string) { +func RecordMetrics(method, path string, success bool, httpCode, businessCode int, costSeconds float64, traceId string) { metricsRequestsTotal.With(prometheus.Labels{ "method": method, - "path": uri, + "path": path, }).Inc() metricsRequestsCost.With(prometheus.Labels{ "method": method, - "path": uri, + "path": path, "success": cast.ToString(success), "http_code": cast.ToString(httpCode), "business_code": cast.ToString(businessCode), diff --git a/internal/pkg/core/context.go b/internal/pkg/core/context.go index 63b05cb..0d33ba9 100644 --- a/internal/pkg/core/context.go +++ b/internal/pkg/core/context.go @@ -9,7 +9,7 @@ import ( "strings" "sync" - "github.com/xinliangnote/go-gin-api/pkg/errno" + "github.com/xinliangnote/go-gin-api/internal/proposal" "github.com/xinliangnote/go-gin-api/pkg/trace" "github.com/gin-gonic/gin" @@ -28,9 +28,9 @@ const ( _BodyName = "_body_" _PayloadName = "_payload_" _GraphPayloadName = "_graph_payload_" - _UserID = "_user_id_" - _UserName = "_user_name_" + _SessionUserInfo = "_session_user_info" _AbortErrorName = "_abort_error_" + _IsRecordMetrics = "_is_record_metrics_" ) var contextPool = &sync.Pool{ @@ -101,8 +101,8 @@ type Context interface { HTML(name string, obj interface{}) // AbortWithError 错误返回 - AbortWithError(err errno.Error) - abortError() errno.Error + AbortWithError(err BusinessError) + abortError() BusinessError // Header 获取 Header 对象 Header() http.Header @@ -111,18 +111,19 @@ type Context interface { // SetHeader 设置 Header SetHeader(key, value string) - // UserID 获取 UserID - UserID() int64 - setUserID(userID int64) + // SessionUserInfo 当前用户信息 + SessionUserInfo() proposal.SessionUserInfo + setSessionUserInfo(info proposal.SessionUserInfo) - // UserName 获取 UserName - UserName() string - setUserName(userName string) - - // Alias 设置路由别名 for metrics uri + // Alias 设置路由别名 for metrics path Alias() string setAlias(path string) + // disableRecordMetrics 设置禁止记录指标 + disableRecordMetrics() + ableRecordMetrics() + isRecordMetrics() bool + // RequestInputParams 获取所有参数 RequestInputParams() url.Values // RequestPostFormParams 获取 PostForm 参数 @@ -279,35 +280,22 @@ func (c *context) SetHeader(key, value string) { c.ctx.Header(key, value) } -func (c *context) UserID() int64 { - val, ok := c.ctx.Get(_UserID) +func (c *context) SessionUserInfo() proposal.SessionUserInfo { + val, ok := c.ctx.Get(_SessionUserInfo) if !ok { - return 0 + return proposal.SessionUserInfo{} } - return val.(int64) + return val.(proposal.SessionUserInfo) } -func (c *context) setUserID(userID int64) { - c.ctx.Set(_UserID, userID) +func (c *context) setSessionUserInfo(info proposal.SessionUserInfo) { + c.ctx.Set(_SessionUserInfo, info) } -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) { +func (c *context) AbortWithError(err BusinessError) { if err != nil { - httpCode := err.GetHttpCode() + httpCode := err.HTTPCode() if httpCode == 0 { httpCode = http.StatusInternalServerError } @@ -317,9 +305,9 @@ func (c *context) AbortWithError(err errno.Error) { } } -func (c *context) abortError() errno.Error { +func (c *context) abortError() BusinessError { err, _ := c.ctx.Get(_AbortErrorName) - return err.(errno.Error) + return err.(BusinessError) } func (c *context) Alias() string { @@ -337,6 +325,23 @@ func (c *context) setAlias(path string) { } } +func (c *context) isRecordMetrics() bool { + isRecordMetrics, ok := c.ctx.Get(_IsRecordMetrics) + if !ok { + return false + } + + return isRecordMetrics.(bool) +} + +func (c *context) ableRecordMetrics() { + c.ctx.Set(_IsRecordMetrics, true) +} + +func (c *context) disableRecordMetrics() { + c.ctx.Set(_IsRecordMetrics, false) +} + // RequestInputParams 获取所有参数 func (c *context) RequestInputParams() url.Values { _ = c.ctx.Request.ParseForm() diff --git a/internal/pkg/core/core.go b/internal/pkg/core/core.go index 385b7a2..266f963 100644 --- a/internal/pkg/core/core.go +++ b/internal/pkg/core/core.go @@ -12,10 +12,10 @@ import ( "github.com/xinliangnote/go-gin-api/configs" _ "github.com/xinliangnote/go-gin-api/docs" "github.com/xinliangnote/go-gin-api/internal/code" + "github.com/xinliangnote/go-gin-api/internal/proposal" "github.com/xinliangnote/go-gin-api/pkg/browser" "github.com/xinliangnote/go-gin-api/pkg/color" "github.com/xinliangnote/go-gin-api/pkg/env" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/errors" "github.com/xinliangnote/go-gin-api/pkg/trace" @@ -46,20 +46,13 @@ type option struct { disablePProf bool disableSwagger bool disablePrometheus bool - panicNotify OnPanicNotify - recordMetrics RecordMetrics enableCors bool enableRate bool enableOpenBrowser string + alertNotify proposal.NotifyHandler + recordHandler proposal.RecordHandler } -// 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, traceId string) - // WithDisablePProf 禁用 pprof func WithDisablePProf() Option { return func(opt *option) { @@ -81,17 +74,17 @@ func WithDisablePrometheus() Option { } } -// WithPanicNotify 设置panic时的通知回调 -func WithPanicNotify(notify OnPanicNotify) Option { +// WithAlertNotify 设置告警通知 +func WithAlertNotify(notifyHandler proposal.NotifyHandler) Option { return func(opt *option) { - opt.panicNotify = notify + opt.alertNotify = notifyHandler } } -// WithRecordMetrics 设置记录prometheus记录指标回调 -func WithRecordMetrics(record RecordMetrics) Option { +// WithRecordMetrics 设置记录接口指标 +func WithRecordMetrics(recordHandler proposal.RecordHandler) Option { return func(opt *option) { - opt.recordMetrics = record + opt.recordHandler = recordHandler } } @@ -102,41 +95,48 @@ func WithEnableOpenBrowser(uri string) Option { } } -// WithEnableCors 开启CORS +// WithEnableCors 设置支持跨域 func WithEnableCors() Option { return func(opt *option) { opt.enableCors = true } } +// WithEnableRate 设置支持限流 func WithEnableRate() Option { return func(opt *option) { opt.enableRate = true } } -func DisableTrace(ctx Context) { +// DisableTraceLog 禁止记录日志 +func DisableTraceLog(ctx Context) { ctx.disableTrace() } -// AliasForRecordMetrics 对请求uri起个别名,用于prometheus记录指标。 -// 如:Get /user/:username 这样的uri,因为username会有非常多的情况,这样记录prometheus指标会非常的不有好。 +// DisableRecordMetrics 禁止记录指标 +func DisableRecordMetrics(ctx Context) { + ctx.disableRecordMetrics() +} + +// AliasForRecordMetrics 对请求路径起个别名,用于记录指标。 +// 如:Get /user/:username 这样的路径,因为 username 会有非常多的情况,这样记录指标非常不友好。 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 int64, userName string, err errno.Error)) HandlerFunc { +// WrapAuthHandler 用来处理 Auth 的入口 +func WrapAuthHandler(handler func(Context) (sessionUserInfo proposal.SessionUserInfo, err BusinessError)) HandlerFunc { return func(ctx Context) { - userID, userName, err := handler(ctx) + sessionUserInfo, err := handler(ctx) if err != nil { ctx.AbortWithError(err) return } - ctx.setUserID(userID) - ctx.setUserName(userName) + + ctx.setSessionUserInfo(sessionUserInfo) } } @@ -328,6 +328,11 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { }) mux.engine.Use(func(ctx *gin.Context) { + + if ctx.Writer.Status() == http.StatusNotFound { + return + } + ts := time.Now() context := newContext(ctx) @@ -335,6 +340,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { context.init() context.setLogger(logger) + context.ableRecordMetrics() if !withoutTracePaths[ctx.Request.URL.Path] { if traceId := context.GetHeader(trace.Header); traceId != "" { @@ -345,24 +351,6 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { } defer func() { - if err := recover(); err != nil { - stackInfo := string(debug.Stack()) - logger.Error("got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo)) - context.AbortWithError(errno.NewError( - http.StatusInternalServerError, - code.ServerError, - code.Text(code.ServerError)), - ) - - if notify := opt.panicNotify; notify != nil { - notify(context, err, stackInfo) - } - } - - if ctx.Writer.Status() == http.StatusNotFound { - return - } - var ( response interface{} businessCode int @@ -372,57 +360,103 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { graphResponse interface{} ) + if ct := context.Trace(); ct != nil { + context.SetHeader(trace.Header, ct.ID()) + traceId = ct.ID() + } + + // region 发生 Panic 异常发送告警提醒 + if err := recover(); err != nil { + stackInfo := string(debug.Stack()) + logger.Error("got panic", zap.String("panic", fmt.Sprintf("%+v", err)), zap.String("stack", stackInfo)) + context.AbortWithError(Error( + http.StatusInternalServerError, + code.ServerError, + code.Text(code.ServerError)), + ) + + if notifyHandler := opt.alertNotify; notifyHandler != nil { + notifyHandler(&proposal.AlertMessage{ + ProjectName: configs.ProjectName, + Env: env.Active().Value(), + TraceID: traceId, + HOST: context.Host(), + URI: context.URI(), + Method: context.Method(), + ErrorMessage: err, + ErrorStack: stackInfo, + Timestamp: time.Now(), + }) + } + } + // endregion + + // region 发生错误,进行返回 if ctx.IsAborted() { - for i := range ctx.Errors { // gin error + for i := range ctx.Errors { multierr.AppendInto(&abortErr, ctx.Errors[i]) } if err := context.abortError(); err != nil { // customer err - multierr.AppendInto(&abortErr, err.GetErr()) - response = err - businessCode = err.GetBusinessCode() - businessCodeMsg = err.GetMsg() - - if x := context.Trace(); x != nil { - context.SetHeader(trace.Header, x.ID()) - traceId = x.ID() + // 判断是否需要发送告警通知 + if err.IsAlert() { + if notifyHandler := opt.alertNotify; notifyHandler != nil { + notifyHandler(&proposal.AlertMessage{ + ProjectName: configs.ProjectName, + Env: env.Active().Value(), + TraceID: traceId, + HOST: context.Host(), + URI: context.URI(), + Method: context.Method(), + ErrorMessage: err.Message(), + ErrorStack: fmt.Sprintf("%+v", err.StackError()), + Timestamp: time.Now(), + }) + } } - ctx.JSON(err.GetHttpCode(), &code.Failure{ + multierr.AppendInto(&abortErr, err.StackError()) + businessCode = err.BusinessCode() + businessCodeMsg = err.Message() + response = &code.Failure{ Code: businessCode, Message: businessCodeMsg, - }) - } - } else { - response = context.getPayload() - if response != nil { - if x := context.Trace(); x != nil { - context.SetHeader(trace.Header, x.ID()) - traceId = x.ID() } - ctx.JSON(http.StatusOK, response) + ctx.JSON(err.HTTPCode(), response) } } + // endregion - graphResponse = context.getGraphPayload() + // region 正确返回 + response = context.getPayload() + if response != nil { + ctx.JSON(http.StatusOK, response) + } + // endregion - if opt.recordMetrics != nil { - uri := context.URI() + // region 记录指标 + if opt.recordHandler != nil && context.isRecordMetrics() { + path := context.Path() if alias := context.Alias(); alias != "" { - uri = alias + path = alias } - opt.recordMetrics( - context.Method(), - uri, - !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, - ctx.Writer.Status(), - businessCode, - time.Since(ts).Seconds(), - traceId, - ) + opt.recordHandler(&proposal.MetricsMessage{ + ProjectName: configs.ProjectName, + Env: env.Active().Value(), + TraceID: traceId, + HOST: context.Host(), + Path: path, + Method: context.Method(), + HTTPCode: ctx.Writer.Status(), + BusinessCode: businessCode, + CostSeconds: time.Since(ts).Seconds(), + IsSuccess: !ctx.IsAborted() && (ctx.Writer.Status() == http.StatusOK), + }) } + // endregion + // region 记录日志 var t *trace.Trace if x := context.Trace(); x != nil { t = x.(*trace.Trace) @@ -454,6 +488,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { responseBody = response } + graphResponse = context.getGraphPayload() if graphResponse != nil { responseBody = graphResponse } @@ -468,10 +503,10 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { CostSeconds: time.Since(ts).Seconds(), }) - t.Success = !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK + t.Success = !ctx.IsAborted() && (ctx.Writer.Status() == http.StatusOK) t.CostSeconds = time.Since(ts).Seconds() - logger.Info("core-interceptor", + logger.Info("trace-log", zap.Any("method", ctx.Request.Method), zap.Any("path", decodedURL), zap.Any("http_code", ctx.Writer.Status()), @@ -482,6 +517,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { zap.Any("trace_info", t), zap.Error(abortErr), ) + // endregion }() ctx.Next() @@ -494,7 +530,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { defer releaseContext(context) if !limiter.Allow() { - context.AbortWithError(errno.NewError( + context.AbortWithError(Error( http.StatusTooManyRequests, code.TooManyRequests, code.Text(code.TooManyRequests)), @@ -506,8 +542,9 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) { }) } - mux.engine.NoMethod(wrapHandlers(DisableTrace)...) - mux.engine.NoRoute(wrapHandlers(DisableTrace)...) + mux.engine.NoMethod(wrapHandlers(DisableTraceLog)...) + mux.engine.NoRoute(wrapHandlers(DisableTraceLog)...) + system := mux.Group("/system") { // 健康检查 diff --git a/internal/pkg/core/error.go b/internal/pkg/core/error.go new file mode 100644 index 0000000..254736d --- /dev/null +++ b/internal/pkg/core/error.go @@ -0,0 +1,82 @@ +package core + +import ( + "github.com/xinliangnote/go-gin-api/pkg/errors" +) + +var _ BusinessError = (*businessError)(nil) + +type BusinessError interface { + // i 为了避免被其他包实现 + i() + + // WithError 设置错误信息 + WithError(err error) BusinessError + + // WithAlert 设置告警通知 + WithAlert() BusinessError + + // BusinessCode 获取业务码 + BusinessCode() int + + // HTTPCode 获取 HTTP 状态码 + HTTPCode() int + + // Message 获取错误描述 + Message() string + + // StackError 获取带堆栈的错误信息 + StackError() error + + // IsAlert 是否开启告警通知 + IsAlert() bool +} + +type businessError struct { + httpCode int // HTTP 状态码 + businessCode int // 业务码 + message string // 错误描述 + stackError error // 含有堆栈信息的错误 + isAlert bool // 是否告警通知 +} + +func Error(httpCode, businessCode int, message string) BusinessError { + return &businessError{ + httpCode: httpCode, + businessCode: businessCode, + message: message, + isAlert: false, + } +} + +func (e *businessError) i() {} + +func (e *businessError) WithError(err error) BusinessError { + e.stackError = errors.WithStack(err) + return e +} + +func (e *businessError) WithAlert() BusinessError { + e.isAlert = true + return e +} + +func (e *businessError) HTTPCode() int { + return e.httpCode +} + +func (e *businessError) BusinessCode() int { + return e.businessCode +} + +func (e *businessError) Message() string { + return e.message +} + +func (e *businessError) StackError() error { + return e.stackError +} + +func (e *businessError) IsAlert() bool { + return e.isAlert +} diff --git a/internal/pkg/notify/notify.go b/internal/pkg/notify/notify.go deleted file mode 100644 index 6937ab4..0000000 --- a/internal/pkg/notify/notify.go +++ /dev/null @@ -1,52 +0,0 @@ -package notify - -import ( - "github.com/xinliangnote/go-gin-api/configs" - "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/mail" - - "go.uber.org/zap" -) - -// Email 发生 panic 时进行邮件通知 -func Email(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 - } - - tractID := "" - if ctx.Trace() != nil { - tractID = ctx.Trace().ID() - } - - subject, body, htmlErr := NewPanicHTMLEmail( - ctx.Method(), - ctx.Host(), - ctx.URI(), - tractID, - 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 -} diff --git a/internal/pkg/notify/template.go b/internal/pkg/notify/template.go deleted file mode 100644 index 283f126..0000000 --- a/internal/pkg/notify/template.go +++ /dev/null @@ -1,44 +0,0 @@ -package notify - -import ( - "bytes" - "fmt" - "html/template" - "time" - - "github.com/xinliangnote/go-gin-api/internal/pkg/notify/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 -} diff --git a/internal/proposal/alert.go b/internal/proposal/alert.go new file mode 100644 index 0000000..e8f0669 --- /dev/null +++ b/internal/proposal/alert.go @@ -0,0 +1,28 @@ +package proposal + +import ( + "encoding/json" + "time" +) + +// AlertMessage 告警信息 +type AlertMessage struct { + ProjectName string `json:"project_name"` // 项目名,用于区分不同项目告警信息 + Env string `json:"env"` // 运行环境 + TraceID string `json:"trace_id"` // 唯一ID,用于追踪关联 + HOST string `json:"host"` // 请求 HOST + URI string `json:"uri"` // 请求 URI + Method string `json:"method"` // 请求 Method + ErrorMessage interface{} `json:"error_message"` // 错误信息 + ErrorStack string `json:"error_stack"` // 堆栈信息 + Timestamp time.Time `json:"timestamp"` // 时间戳 +} + +// Marshal 序列化到JSON +func (a *AlertMessage) Marshal() (jsonRaw []byte) { + jsonRaw, _ = json.Marshal(a) + return +} + +// NotifyHandler 告警的发送句柄 +type NotifyHandler func(msg *AlertMessage) diff --git a/internal/proposal/metrics.go b/internal/proposal/metrics.go new file mode 100644 index 0000000..a887163 --- /dev/null +++ b/internal/proposal/metrics.go @@ -0,0 +1,28 @@ +package proposal + +import ( + "encoding/json" +) + +// MetricsMessage 指标信息 +type MetricsMessage struct { + ProjectName string `json:"project_name"` // 项目名,用于区分不同项目告警信息 + Env string `json:"env"` // 运行环境 + TraceID string `json:"trace_id"` // 唯一ID,用于追踪关联 + HOST string `json:"host"` // 请求 HOST + Path string `json:"path"` // 请求 Path + Method string `json:"method"` // 请求 Method + HTTPCode int `json:"http_code"` // HTTP 状态码 + BusinessCode int `json:"business_code"` // 业务码 + CostSeconds float64 `json:"cost_seconds"` // 耗时,单位:秒 + IsSuccess bool `json:"is_success"` // 状态,是否成功 +} + +// Marshal 序列化到JSON +func (m *MetricsMessage) Marshal() (jsonRaw []byte) { + jsonRaw, _ = json.Marshal(m) + return +} + +// RecordHandler 指标的记录句柄 +type RecordHandler func(msg *MetricsMessage) diff --git a/internal/proposal/session.go b/internal/proposal/session.go new file mode 100644 index 0000000..dc43c43 --- /dev/null +++ b/internal/proposal/session.go @@ -0,0 +1,15 @@ +package proposal + +import "encoding/json" + +// SessionUserInfo 当前用户会话信息 +type SessionUserInfo struct { + UserID int32 `json:"user_id"` // 用户ID + UserName string `json:"user_name"` // 用户名 +} + +// Marshal 序列化到JSON +func (user *SessionUserInfo) Marshal() (jsonRaw []byte) { + jsonRaw, _ = json.Marshal(user) + return +} diff --git a/internal/render/admin/admin.go b/internal/render/admin/admin.go index 24c54f8..7103bab 100644 --- a/internal/render/admin/admin.go +++ b/internal/render/admin/admin.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/mysql" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" "go.uber.org/zap" ) @@ -62,10 +61,10 @@ func (h *handler) AdminMenu() core.HandlerFunc { return func(ctx core.Context) { req := new(adminMenuRequest) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -89,10 +88,10 @@ func (h *handler) MenuAction() core.HandlerFunc { return func(ctx core.Context) { req := new(menuActionRequest) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } diff --git a/internal/render/authorized/authorized.go b/internal/render/authorized/authorized.go index dd0a50f..ac8b666 100644 --- a/internal/render/authorized/authorized.go +++ b/internal/render/authorized/authorized.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/mysql" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" "go.uber.org/zap" ) @@ -56,10 +55,10 @@ func (h *handler) Api() core.HandlerFunc { return func(ctx core.Context) { req := new(apiRequest) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } diff --git a/internal/render/cron/cron.go b/internal/render/cron/cron.go index dd862b1..29b2d5e 100644 --- a/internal/render/cron/cron.go +++ b/internal/render/cron/cron.go @@ -7,7 +7,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/mysql" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" "go.uber.org/zap" ) @@ -44,10 +43,10 @@ func (h *handler) Edit() core.HandlerFunc { return func(ctx core.Context) { req := new(editRequest) if err := ctx.ShouldBindURI(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } diff --git a/internal/render/install/execute.go b/internal/render/install/execute.go index 37e227f..9bf0e02 100644 --- a/internal/render/install/execute.go +++ b/internal/render/install/execute.go @@ -10,7 +10,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/proposal/tablesqls" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/go-redis/redis/v7" "github.com/spf13/cast" @@ -68,10 +67,10 @@ func (h *handler) Execute() core.HandlerFunc { return func(ctx core.Context) { req := new(initExecuteRequest) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -80,7 +79,7 @@ func (h *handler) Execute() core.HandlerFunc { versionStr := runtime.Version() version := cast.ToFloat32(versionStr[2:6]) if version < configs.MinGoVersion { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.GoVersionError, code.Text(code.GoVersionError)), @@ -101,10 +100,10 @@ func (h *handler) Execute() core.HandlerFunc { }) if err := redisClient.Ping().Err(); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.RedisConnectError, - code.Text(code.RedisConnectError)).WithErr(err), + code.Text(code.RedisConnectError)).WithError(err), ) return } @@ -131,10 +130,10 @@ func (h *handler) Execute() core.HandlerFunc { }) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLConnectError, - code.Text(code.MySQLConnectError)).WithErr(err), + code.Text(code.MySQLConnectError)).WithError(err), ) return } @@ -165,10 +164,10 @@ func (h *handler) Execute() core.HandlerFunc { viper.Set("mysql.write.name", req.MySQLName) if viper.WriteConfig() != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.WriteConfigError, - code.Text(code.WriteConfigError)).WithErr(err), + code.Text(code.WriteConfigError)).WithError(err), ) return } @@ -182,10 +181,10 @@ func (h *handler) Execute() core.HandlerFunc { if v["table_sql"] != "" { // region 初始化表结构 if err = db.Exec(v["table_sql"]).Error; err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), + code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err), ) return } @@ -196,10 +195,10 @@ func (h *handler) Execute() core.HandlerFunc { // region 初始化默认数据 if v["table_data_sql"] != "" { if err = db.Exec(v["table_data_sql"]).Error; err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), + code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err), ) return } @@ -214,10 +213,10 @@ func (h *handler) Execute() core.HandlerFunc { // region 生成 install 完成标识 f, err := os.Create(configs.ProjectInstallMark) if err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), + code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err), ) return } diff --git a/internal/render/upgrade/execute.go b/internal/render/upgrade/execute.go index 4ce1196..79f5cc4 100644 --- a/internal/render/upgrade/execute.go +++ b/internal/render/upgrade/execute.go @@ -6,7 +6,6 @@ import ( "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/proposal/tablesqls" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/errors" ) @@ -56,10 +55,10 @@ func (h *handler) UpgradeExecute() core.HandlerFunc { return func(ctx core.Context) { req := new(upgradeExecuteRequest) if err := ctx.ShouldBindForm(req); err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.ParamBindError, - code.Text(code.ParamBindError)).WithErr(err), + code.Text(code.ParamBindError)).WithError(err), ) return } @@ -68,29 +67,29 @@ func (h *handler) UpgradeExecute() core.HandlerFunc { db := h.db.GetDbW() if upgradeTableList[req.TableName] == nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)).WithErr(errors.New("数据表不存在")), + code.Text(code.MySQLExecError)).WithError(errors.New("数据表不存在")), ) return } if !upgradeTableOp[req.Op] { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)).WithErr(errors.New("非法操作")), + code.Text(code.MySQLExecError)).WithError(errors.New("非法操作")), ) return } if req.Op == "table" { if err := db.Exec(upgradeTableList[req.TableName]["table_sql"]).Error; err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), + code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err), ) return } @@ -98,10 +97,10 @@ func (h *handler) UpgradeExecute() core.HandlerFunc { outPutString = "初始化 MySQL 数据表:" + req.TableName + " 成功。" } else if req.Op == "table_data" { if err := db.Exec(upgradeTableList[req.TableName]["table_data_sql"]).Error; err != nil { - ctx.AbortWithError(errno.NewError( + ctx.AbortWithError(core.Error( http.StatusBadRequest, code.MySQLExecError, - code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), + code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err), ) return } diff --git a/internal/repository/mysql/authorized/model.go b/internal/repository/mysql/authorized/model.go new file mode 100644 index 0000000..c18c9d0 --- /dev/null +++ b/internal/repository/mysql/authorized/model.go @@ -0,0 +1,6 @@ +package authorized + +const ( + IsUsedYES = 1 // 启用 + IsUsedNo = -1 // 禁用 +) diff --git a/internal/repository/mysql/plugin.go b/internal/repository/mysql/plugin.go index 2c0906a..40ee7bd 100644 --- a/internal/repository/mysql/plugin.go +++ b/internal/repository/mysql/plugin.go @@ -74,7 +74,10 @@ func after(db *gorm.DB) { sqlInfo.Stack = utils.FileWithLineNum() sqlInfo.Rows = db.Statement.RowsAffected sqlInfo.CostSeconds = time.Since(ts).Seconds() - ctx.Trace.AppendSQL(sqlInfo) + + if ctx.Trace != nil { + ctx.Trace.AppendSQL(sqlInfo) + } return } diff --git a/internal/router/interceptor/check_login.go b/internal/router/interceptor/check_login.go new file mode 100644 index 0000000..fd509e2 --- /dev/null +++ b/internal/router/interceptor/check_login.go @@ -0,0 +1,56 @@ +package interceptor + +import ( + "encoding/json" + "net/http" + + "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/code" + "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/proposal" + "github.com/xinliangnote/go-gin-api/internal/repository/redis" + "github.com/xinliangnote/go-gin-api/pkg/errors" +) + +func (i *interceptor) CheckLogin(ctx core.Context) (sessionUserInfo proposal.SessionUserInfo, err core.BusinessError) { + token := ctx.GetHeader(configs.HeaderLoginToken) + if token == "" { + err = core.Error( + http.StatusUnauthorized, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Token 参数")) + + return + } + + if !i.cache.Exists(configs.RedisKeyPrefixLoginUser + token) { + err = core.Error( + http.StatusUnauthorized, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(errors.New("请先登录")) + + return + } + + cacheData, cacheErr := i.cache.Get(configs.RedisKeyPrefixLoginUser+token, redis.WithTrace(ctx.Trace())) + if cacheErr != nil { + err = core.Error( + http.StatusUnauthorized, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(cacheErr) + + return + } + + jsonErr := json.Unmarshal([]byte(cacheData), &sessionUserInfo) + if jsonErr != nil { + core.Error( + http.StatusUnauthorized, + code.AuthorizationError, + code.Text(code.AuthorizationError)).WithError(jsonErr) + + return + } + + return +} diff --git a/internal/router/middleware/middle_rbac.go b/internal/router/interceptor/check_rbac.go similarity index 58% rename from internal/router/middleware/middle_rbac.go rename to internal/router/interceptor/check_rbac.go index 71e7914..d118e41 100644 --- a/internal/router/middleware/middle_rbac.go +++ b/internal/router/interceptor/check_rbac.go @@ -1,4 +1,4 @@ -package middleware +package interceptor import ( "encoding/json" @@ -9,47 +9,46 @@ import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/services/admin" - "github.com/xinliangnote/go-gin-api/pkg/errno" "github.com/xinliangnote/go-gin-api/pkg/errors" "github.com/xinliangnote/go-gin-api/pkg/urltable" ) -func (m *middleware) RBAC() core.HandlerFunc { +func (i *interceptor) CheckRBAC() core.HandlerFunc { return func(c core.Context) { token := c.GetHeader("Token") if token == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusUnauthorized, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Token 参数")), + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Token 参数")), ) return } - if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token) { - c.AbortWithError(errno.NewError( + if !i.cache.Exists(configs.RedisKeyPrefixLoginUser + token) { + c.AbortWithError(core.Error( http.StatusUnauthorized, code.CacheGetError, - code.Text(code.CacheGetError)).WithErr(errors.New("请先登录 1")), + code.Text(code.CacheGetError)).WithError(errors.New("请先登录")), ) return } - if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token + ":action") { - c.AbortWithError(errno.NewError( + if !i.cache.Exists(configs.RedisKeyPrefixLoginUser + token + ":action") { + c.AbortWithError(core.Error( http.StatusUnauthorized, code.CacheGetError, - code.Text(code.CacheGetError)).WithErr(errors.New("请先登录 2")), + code.Text(code.CacheGetError)).WithError(errors.New("当前账号未配置 RBAC 权限")), ) return } - actionData, err := m.cache.Get(configs.RedisKeyPrefixLoginUser+token+":action", redis.WithTrace(c.Trace())) + actionData, err := i.cache.Get(configs.RedisKeyPrefixLoginUser+token+":action", redis.WithTrace(c.Trace())) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusUnauthorized, code.CacheGetError, - code.Text(code.CacheGetError)).WithErr(err), + code.Text(code.CacheGetError)).WithError(err), ) return } @@ -57,10 +56,10 @@ func (m *middleware) RBAC() core.HandlerFunc { var actions []admin.MyActionData err = json.Unmarshal([]byte(actionData), &actions) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusUnauthorized, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(err), + code.Text(code.AuthorizationError)).WithError(err), ) return } @@ -72,10 +71,10 @@ func (m *middleware) RBAC() core.HandlerFunc { } if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.RBACError, - code.Text(code.RBACError)).WithErr(errors.New(c.Method() + c.Path() + " 未进行 RBAC 授权")), + code.Text(code.RBACError)).WithError(errors.New(c.Method() + c.Path() + " 未进行 RBAC 授权")), ) return } diff --git a/internal/router/middleware/middle_signature.go b/internal/router/interceptor/check_signature.go similarity index 51% rename from internal/router/middleware/middle_signature.go rename to internal/router/interceptor/check_signature.go index b8a10f5..d375906 100644 --- a/internal/router/middleware/middle_signature.go +++ b/internal/router/interceptor/check_signature.go @@ -1,39 +1,36 @@ -package middleware +package interceptor import ( "net/http" "strings" - "time" "github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/internal/code" "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/pkg/errno" + "github.com/xinliangnote/go-gin-api/internal/repository/mysql/authorized" + "github.com/xinliangnote/go-gin-api/pkg/env" "github.com/xinliangnote/go-gin-api/pkg/errors" "github.com/xinliangnote/go-gin-api/pkg/signature" "github.com/xinliangnote/go-gin-api/pkg/urltable" ) -const ( - ttl = time.Minute * 2 // 签名超时时间 2 分钟 - minLength = 2 // split space 最小长度 - notUsed = -1 // -1 表示被禁用 - -) - var whiteListPath = map[string]bool{ "/login/web": true, } -func (m *middleware) Signature() core.HandlerFunc { +func (i *interceptor) CheckSignature() core.HandlerFunc { return func(c core.Context) { + if !env.Active().IsPro() { + return + } + // 签名信息 authorization := c.GetHeader(configs.HeaderSignToken) if authorization == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Authorization 参数")), + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Authorization 参数")), ) return } @@ -41,51 +38,51 @@ func (m *middleware) Signature() core.HandlerFunc { // 时间信息 date := c.GetHeader(configs.HeaderSignTokenDate) if date == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Date 参数")), + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Date 参数")), ) return } // 通过签名信息获取 key authorizationSplit := strings.Split(authorization, " ") - if len(authorizationSplit) < minLength { - c.AbortWithError(errno.NewError( + if len(authorizationSplit) < 2 { + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中 Authorization 格式错误")), + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中 Authorization 格式错误")), ) return } key := authorizationSplit[0] - data, err := m.authorizedService.DetailByKey(c, key) + data, err := i.authorizedService.DetailByKey(c, key) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(err), + code.Text(code.AuthorizationError)).WithError(err), ) return } - if data.IsUsed == notUsed { - c.AbortWithError(errno.NewError( + if data.IsUsed == authorized.IsUsedNo { + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New(key + " 已被禁止调用")), + code.Text(code.AuthorizationError)).WithError(errors.New(key + " 已被禁止调用")), ) return } if len(data.Apis) < 1 { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New(key + " 未进行接口授权")), + code.Text(code.AuthorizationError)).WithError(errors.New(key + " 未进行接口授权")), ) return } @@ -98,30 +95,30 @@ func (m *middleware) Signature() core.HandlerFunc { } if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New(c.Method() + c.Path() + " 未进行接口授权")), + code.Text(code.AuthorizationError)).WithError(errors.New(c.Method() + c.Path() + " 未进行接口授权")), ) return } } - ok, err := signature.New(key, data.Secret, ttl).Verify(authorization, date, c.Path(), c.Method(), c.RequestInputParams()) + ok, err := signature.New(key, data.Secret, configs.HeaderSignTokenTimeout).Verify(authorization, date, c.Path(), c.Method(), c.RequestInputParams()) if err != nil { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(err), + code.Text(code.AuthorizationError)).WithError(err), ) return } if !ok { - c.AbortWithError(errno.NewError( + c.AbortWithError(core.Error( http.StatusBadRequest, code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中 Authorization 信息错误")), + code.Text(code.AuthorizationError)).WithError(errors.New("Header 中 Authorization 信息错误")), ) return } diff --git a/internal/router/middleware/middlerware.go b/internal/router/interceptor/interceptor.go similarity index 54% rename from internal/router/middleware/middlerware.go rename to internal/router/interceptor/interceptor.go index 7b0fcf1..4f12616 100644 --- a/internal/router/middleware/middlerware.go +++ b/internal/router/interceptor/interceptor.go @@ -1,36 +1,33 @@ -package middleware +package interceptor import ( "github.com/xinliangnote/go-gin-api/internal/pkg/core" + "github.com/xinliangnote/go-gin-api/internal/proposal" "github.com/xinliangnote/go-gin-api/internal/repository/mysql" "github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/authorized" - "github.com/xinliangnote/go-gin-api/pkg/errno" "go.uber.org/zap" ) -var _ Middleware = (*middleware)(nil) +var _ Interceptor = (*interceptor)(nil) + +type Interceptor interface { + // CheckLogin 验证是否登录 + CheckLogin(ctx core.Context) (info proposal.SessionUserInfo, err core.BusinessError) + + // CheckRBAC 验证 RBAC 权限是否合法 + CheckRBAC() core.HandlerFunc + + // CheckSignature 验证签名是否合法,对用签名算法 pkg/signature + CheckSignature() core.HandlerFunc -type Middleware interface { // i 为了避免被其他包实现 i() - - // DisableLog 不记录日志 - DisableLog() core.HandlerFunc - - // Signature 签名验证,对用签名算法 pkg/signature - Signature() core.HandlerFunc - - // Token 签名验证,对登录用户的验证 - Token(ctx core.Context) (userId int64, userName string, err errno.Error) - - // RBAC 权限验证 - RBAC() core.HandlerFunc } -type middleware struct { +type interceptor struct { logger *zap.Logger cache redis.Repo db mysql.Repo @@ -38,8 +35,8 @@ type middleware struct { adminService admin.Service } -func New(logger *zap.Logger, cache redis.Repo, db mysql.Repo) Middleware { - return &middleware{ +func New(logger *zap.Logger, cache redis.Repo, db mysql.Repo) Interceptor { + return &interceptor{ logger: logger, cache: cache, db: db, @@ -48,4 +45,4 @@ func New(logger *zap.Logger, cache redis.Repo, db mysql.Repo) Middleware { } } -func (m *middleware) i() {} +func (i *interceptor) i() {} diff --git a/internal/router/middleware/middle_disablelog.go b/internal/router/middleware/middle_disablelog.go deleted file mode 100644 index 73035b8..0000000 --- a/internal/router/middleware/middle_disablelog.go +++ /dev/null @@ -1,9 +0,0 @@ -package middleware - -import "github.com/xinliangnote/go-gin-api/internal/pkg/core" - -func (m *middleware) DisableLog() core.HandlerFunc { - return func(c core.Context) { - core.DisableTrace(c) - } -} diff --git a/internal/router/middleware/middle_token.go b/internal/router/middleware/middle_token.go deleted file mode 100644 index fa6d670..0000000 --- a/internal/router/middleware/middle_token.go +++ /dev/null @@ -1,66 +0,0 @@ -package middleware - -import ( - "encoding/json" - "net/http" - - "github.com/xinliangnote/go-gin-api/configs" - "github.com/xinliangnote/go-gin-api/internal/code" - "github.com/xinliangnote/go-gin-api/internal/pkg/core" - "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/pkg/errno" - "github.com/xinliangnote/go-gin-api/pkg/errors" -) - -func (m *middleware) Token(ctx core.Context) (userId int64, userName string, err errno.Error) { - token := ctx.GetHeader(configs.HeaderLoginToken) - if token == "" { - err = errno.NewError( - http.StatusUnauthorized, - code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Token 参数")) - - return - } - - if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token) { - err = errno.NewError( - http.StatusUnauthorized, - code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(errors.New("请先登录")) - - return - } - - cacheData, cacheErr := m.cache.Get(configs.RedisKeyPrefixLoginUser+token, redis.WithTrace(ctx.Trace())) - if cacheErr != nil { - err = errno.NewError( - http.StatusUnauthorized, - code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(cacheErr) - - return - } - - type userInfo struct { - Id int64 `json:"id"` // 用户ID - Username string `json:"username"` // 用户名 - } - - var userData userInfo - - jsonErr := json.Unmarshal([]byte(cacheData), &userData) - if jsonErr != nil { - errno.NewError( - http.StatusUnauthorized, - code.AuthorizationError, - code.Text(code.AuthorizationError)).WithErr(jsonErr) - - return - } - - userId = userData.Id - userName = userData.Username - - return -} diff --git a/internal/router/router.go b/internal/router/router.go index e740bdc..ea08bf3 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -2,13 +2,13 @@ package router import ( "github.com/xinliangnote/go-gin-api/configs" + "github.com/xinliangnote/go-gin-api/internal/alert" + "github.com/xinliangnote/go-gin-api/internal/metrics" "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/repository/cron" "github.com/xinliangnote/go-gin-api/internal/repository/mysql" "github.com/xinliangnote/go-gin-api/internal/repository/redis" - "github.com/xinliangnote/go-gin-api/internal/router/middleware" + "github.com/xinliangnote/go-gin-api/internal/router/interceptor" "github.com/xinliangnote/go-gin-api/pkg/errors" "github.com/xinliangnote/go-gin-api/pkg/file" @@ -16,12 +16,12 @@ import ( ) type resource struct { - mux core.Mux - logger *zap.Logger - db mysql.Repo - cache redis.Repo - middles middleware.Middleware - cronServer cron.Server + mux core.Mux + logger *zap.Logger + db mysql.Repo + cache redis.Repo + interceptors interceptor.Interceptor + cronServer cron.Server } type Server struct { @@ -73,8 +73,8 @@ func NewHTTPServer(logger *zap.Logger, cronLogger *zap.Logger) (*Server, error) core.WithEnableOpenBrowser(openBrowserUri), core.WithEnableCors(), core.WithEnableRate(), - core.WithPanicNotify(notify.Email), - core.WithRecordMetrics(metrics.RecordMetrics), + core.WithAlertNotify(alert.NotifyHandler(logger)), + core.WithRecordMetrics(metrics.RecordHandler(logger)), ) if err != nil { @@ -82,7 +82,7 @@ func NewHTTPServer(logger *zap.Logger, cronLogger *zap.Logger) (*Server, error) } r.mux = mux - r.middles = middleware.New(logger, r.cache, r.db) + r.interceptors = interceptor.New(logger, r.cache, r.db) // 设置 Render 路由 setRenderRouter(r) diff --git a/internal/router/router_api.go b/internal/router/router_api.go index 1163e45..368f136 100644 --- a/internal/router/router_api.go +++ b/internal/router/router_api.go @@ -5,24 +5,33 @@ import ( "github.com/xinliangnote/go-gin-api/internal/api/authorized" "github.com/xinliangnote/go-gin-api/internal/api/config" "github.com/xinliangnote/go-gin-api/internal/api/cron" + "github.com/xinliangnote/go-gin-api/internal/api/helper" "github.com/xinliangnote/go-gin-api/internal/api/menu" "github.com/xinliangnote/go-gin-api/internal/api/tool" "github.com/xinliangnote/go-gin-api/internal/pkg/core" ) func setApiRouter(r *resource) { + // helper + helperHandler := helper.New(r.logger, r.db, r.cache) + + helpers := r.mux.Group("/helper") + { + helpers.GET("/md5/:str", helperHandler.Md5()) + helpers.POST("/sign", helperHandler.Sign()) + } // admin adminHandler := admin.New(r.logger, r.db, r.cache) // 需要签名验证,无需登录验证,无需 RBAC 权限验证 - login := r.mux.Group("/api", r.middles.Signature()) + login := r.mux.Group("/api", r.interceptors.CheckSignature()) { login.POST("/login", adminHandler.Login()) } // 需要签名验证、登录验证,无需 RBAC 权限验证 - notRBAC := r.mux.Group("/api", core.WrapAuthHandler(r.middles.Token), r.middles.Signature()) + notRBAC := r.mux.Group("/api", core.WrapAuthHandler(r.interceptors.CheckLogin), r.interceptors.CheckSignature()) { notRBAC.POST("/admin/logout", adminHandler.Logout()) notRBAC.PATCH("/admin/modify_password", adminHandler.ModifyPassword()) @@ -31,7 +40,7 @@ func setApiRouter(r *resource) { } // 需要签名验证、登录验证、RBAC 权限验证 - api := r.mux.Group("/api", core.WrapAuthHandler(r.middles.Token), r.middles.Signature(), r.middles.RBAC()) + api := r.mux.Group("/api", core.WrapAuthHandler(r.interceptors.CheckLogin), r.interceptors.CheckSignature(), r.interceptors.CheckRBAC()) { // authorized authorizedHandler := authorized.New(r.logger, r.db, r.cache) diff --git a/internal/router/router_render.go b/internal/router/router_render.go index bb0cc68..ff1cf03 100644 --- a/internal/router/router_render.go +++ b/internal/router/router_render.go @@ -1,6 +1,7 @@ package router import ( + "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/render/admin" "github.com/xinliangnote/go-gin-api/internal/render/authorized" "github.com/xinliangnote/go-gin-api/internal/render/config" @@ -27,7 +28,7 @@ func setRenderRouter(r *resource) { renderCron := cron.New(r.logger, r.db, r.cache) // 无需记录日志,无需 RBAC 权限验证 - notRBAC := r.mux.Group("", r.middles.DisableLog()) + notRBAC := r.mux.Group("", core.DisableTraceLog, core.DisableRecordMetrics) { // 首页 notRBAC.GET("", renderIndex.Index()) @@ -46,7 +47,7 @@ func setRenderRouter(r *resource) { } // 无需记录日志,需要 RBAC 权限验证 - render := r.mux.Group("", r.middles.DisableLog()) + render := r.mux.Group("", core.DisableTraceLog, core.DisableRecordMetrics) { // 配置信息 render.GET("/config/email", renderConfig.Email()) diff --git a/internal/router/router_socket.go b/internal/router/router_socket.go index e51b361..1217766 100644 --- a/internal/router/router_socket.go +++ b/internal/router/router_socket.go @@ -1,6 +1,7 @@ package router import ( + "github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/websocket/sysmessage" ) @@ -8,7 +9,7 @@ func setSocketRouter(r *resource) { systemMessage := sysmessage.New(r.logger, r.db, r.cache) // 无需记录日志 - socket := r.mux.Group("/socket", r.middles.DisableLog()) + socket := r.mux.Group("/socket", core.DisableTraceLog, core.DisableRecordMetrics) { // 系统消息 socket.GET("/system/message", systemMessage.Connect()) diff --git a/internal/services/admin/service_create.go b/internal/services/admin/service_create.go index 7a71df5..bd5e8a6 100644 --- a/internal/services/admin/service_create.go +++ b/internal/services/admin/service_create.go @@ -19,7 +19,7 @@ func (s *service) Create(ctx core.Context, adminData *CreateAdminData) (id int32 model.Password = password.GeneratePassword(adminData.Password) model.Nickname = adminData.Nickname model.Mobile = adminData.Mobile - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName model.IsUsed = 1 model.IsDeleted = -1 diff --git a/internal/services/admin/service_createmenu.go b/internal/services/admin/service_createmenu.go index 4210b99..0342639 100644 --- a/internal/services/admin/service_createmenu.go +++ b/internal/services/admin/service_createmenu.go @@ -27,7 +27,7 @@ func (s *service) CreateMenu(ctx core.Context, menuData *CreateMenuData) (err er createModel := admin_menu.NewModel() createModel.AdminId = menuData.AdminId createModel.MenuId = cast.ToInt32(v) - createModel.CreatedUser = ctx.UserName() + createModel.CreatedUser = ctx.SessionUserInfo().UserName _, err = createModel.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) if err != nil { diff --git a/internal/services/admin/service_delete.go b/internal/services/admin/service_delete.go index 0d3438b..7afc9cc 100644 --- a/internal/services/admin/service_delete.go +++ b/internal/services/admin/service_delete.go @@ -12,7 +12,7 @@ import ( func (s *service) Delete(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "is_deleted": 1, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := admin.NewQueryBuilder() diff --git a/internal/services/admin/service_modifypassword.go b/internal/services/admin/service_modifypassword.go index 6e3f473..be97c10 100644 --- a/internal/services/admin/service_modifypassword.go +++ b/internal/services/admin/service_modifypassword.go @@ -12,7 +12,7 @@ import ( func (s *service) ModifyPassword(ctx core.Context, id int32, newPassword string) (err error) { data := map[string]interface{}{ "password": password.GeneratePassword(newPassword), - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := admin.NewQueryBuilder() diff --git a/internal/services/admin/service_modifypersonalinfo.go b/internal/services/admin/service_modifypersonalinfo.go index fe79e13..b9d2998 100644 --- a/internal/services/admin/service_modifypersonalinfo.go +++ b/internal/services/admin/service_modifypersonalinfo.go @@ -15,7 +15,7 @@ func (s *service) ModifyPersonalInfo(ctx core.Context, id int32, modifyData *Mod data := map[string]interface{}{ "nickname": modifyData.Nickname, "mobile": modifyData.Mobile, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := admin.NewQueryBuilder() diff --git a/internal/services/admin/service_resetpassword.go b/internal/services/admin/service_resetpassword.go index 0caa919..a1aac26 100644 --- a/internal/services/admin/service_resetpassword.go +++ b/internal/services/admin/service_resetpassword.go @@ -12,7 +12,7 @@ import ( func (s *service) ResetPassword(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "password": password.ResetPassword(), - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := admin.NewQueryBuilder() diff --git a/internal/services/admin/service_updateused.go b/internal/services/admin/service_updateused.go index a4c4743..3c5b675 100644 --- a/internal/services/admin/service_updateused.go +++ b/internal/services/admin/service_updateused.go @@ -12,7 +12,7 @@ import ( func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) { data := map[string]interface{}{ "is_used": used, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := admin.NewQueryBuilder() diff --git a/internal/services/authorized/service_create.go b/internal/services/authorized/service_create.go index 6215ec6..e07b775 100644 --- a/internal/services/authorized/service_create.go +++ b/internal/services/authorized/service_create.go @@ -25,7 +25,7 @@ func (s *service) Create(ctx core.Context, authorizedData *CreateAuthorizedData) model.BusinessSecret = secret model.BusinessDeveloper = authorizedData.BusinessDeveloper model.Remark = authorizedData.Remark - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName model.IsUsed = 1 model.IsDeleted = -1 diff --git a/internal/services/authorized/service_createapi.go b/internal/services/authorized/service_createapi.go index c6614f0..22cdcbd 100644 --- a/internal/services/authorized/service_createapi.go +++ b/internal/services/authorized/service_createapi.go @@ -18,7 +18,7 @@ func (s *service) CreateAPI(ctx core.Context, authorizedAPIData *CreateAuthorize model.BusinessKey = authorizedAPIData.BusinessKey model.Method = authorizedAPIData.Method model.Api = authorizedAPIData.API - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName model.IsDeleted = -1 id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) diff --git a/internal/services/authorized/service_delete.go b/internal/services/authorized/service_delete.go index 24385ae..3cf8d23 100644 --- a/internal/services/authorized/service_delete.go +++ b/internal/services/authorized/service_delete.go @@ -23,7 +23,7 @@ func (s *service) Delete(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "is_deleted": 1, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := authorized.NewQueryBuilder() diff --git a/internal/services/authorized/service_deleteapi.go b/internal/services/authorized/service_deleteapi.go index 5b36aad..778c0ea 100644 --- a/internal/services/authorized/service_deleteapi.go +++ b/internal/services/authorized/service_deleteapi.go @@ -23,7 +23,7 @@ func (s *service) DeleteAPI(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "is_deleted": 1, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := authorized_api.NewQueryBuilder() diff --git a/internal/services/authorized/service_detailbykey.go b/internal/services/authorized/service_detailbykey.go index 672f161..d20891f 100644 --- a/internal/services/authorized/service_detailbykey.go +++ b/internal/services/authorized/service_detailbykey.go @@ -2,7 +2,6 @@ package authorized import ( "encoding/json" - "time" "github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/internal/pkg/core" @@ -68,7 +67,7 @@ func (s *service) DetailByKey(ctx core.Context, key string) (cacheData *CacheAut cacheDataByte, _ := json.Marshal(cacheData) - err = s.cache.Set(cacheKey, string(cacheDataByte), time.Hour*24, redis.WithTrace(ctx.Trace())) + err = s.cache.Set(cacheKey, string(cacheDataByte), configs.LoginSessionTTL, redis.WithTrace(ctx.Trace())) if err != nil { return nil, err } diff --git a/internal/services/authorized/service_updateused.go b/internal/services/authorized/service_updateused.go index ee60e37..6bbc158 100644 --- a/internal/services/authorized/service_updateused.go +++ b/internal/services/authorized/service_updateused.go @@ -22,7 +22,7 @@ func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) data := map[string]interface{}{ "is_used": used, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := authorized.NewQueryBuilder() diff --git a/internal/services/cron/service_create.go b/internal/services/cron/service_create.go index 58a5503..e7cc94a 100644 --- a/internal/services/cron/service_create.go +++ b/internal/services/cron/service_create.go @@ -38,7 +38,7 @@ func (s *service) Create(ctx core.Context, createData *CreateCronTaskData) (id i model.NotifyKeyword = createData.NotifyKeyword model.Remark = createData.Remark model.IsUsed = createData.IsUsed - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) if err != nil { diff --git a/internal/services/cron/service_modify.go b/internal/services/cron/service_modify.go index 04e8726..9561d17 100644 --- a/internal/services/cron/service_modify.go +++ b/internal/services/cron/service_modify.go @@ -41,7 +41,7 @@ func (s *service) Modify(ctx core.Context, id int32, modifyData *ModifyCronTaskD "notify_keyword": modifyData.NotifyKeyword, "remark": modifyData.Remark, "is_used": modifyData.IsUsed, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := cron_task.NewQueryBuilder() diff --git a/internal/services/cron/service_updateused.go b/internal/services/cron/service_updateused.go index 7e3a521..0c2510b 100644 --- a/internal/services/cron/service_updateused.go +++ b/internal/services/cron/service_updateused.go @@ -11,7 +11,7 @@ import ( func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) { data := map[string]interface{}{ "is_used": used, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := cron_task.NewQueryBuilder() diff --git a/internal/services/menu/service_create.go b/internal/services/menu/service_create.go index 21e4408..5582348 100644 --- a/internal/services/menu/service_create.go +++ b/internal/services/menu/service_create.go @@ -20,7 +20,7 @@ func (s *service) Create(ctx core.Context, menuData *CreateMenuData) (id int32, model.Link = menuData.Link model.Icon = menuData.Icon model.Level = menuData.Level - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName model.IsUsed = 1 model.IsDeleted = -1 diff --git a/internal/services/menu/service_createaction.go b/internal/services/menu/service_createaction.go index 18809a8..5846863 100644 --- a/internal/services/menu/service_createaction.go +++ b/internal/services/menu/service_createaction.go @@ -16,7 +16,7 @@ func (s *service) CreateAction(ctx core.Context, menuActionData *CreateMenuActio model.MenuId = menuActionData.MenuId model.Method = menuActionData.Method model.Api = menuActionData.API - model.CreatedUser = ctx.UserName() + model.CreatedUser = ctx.SessionUserInfo().UserName model.IsDeleted = -1 id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) diff --git a/internal/services/menu/service_delete.go b/internal/services/menu/service_delete.go index 55d1eb8..0581c01 100644 --- a/internal/services/menu/service_delete.go +++ b/internal/services/menu/service_delete.go @@ -9,7 +9,7 @@ import ( func (s *service) Delete(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "is_deleted": 1, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := menu.NewQueryBuilder() diff --git a/internal/services/menu/service_deleteaction.go b/internal/services/menu/service_deleteaction.go index 1f430bb..27bf220 100644 --- a/internal/services/menu/service_deleteaction.go +++ b/internal/services/menu/service_deleteaction.go @@ -21,7 +21,7 @@ func (s *service) DeleteAction(ctx core.Context, id int32) (err error) { data := map[string]interface{}{ "is_deleted": 1, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := menu_action.NewQueryBuilder() diff --git a/internal/services/menu/service_modify.go b/internal/services/menu/service_modify.go index f04afbe..2fd16b2 100644 --- a/internal/services/menu/service_modify.go +++ b/internal/services/menu/service_modify.go @@ -17,7 +17,7 @@ func (s *service) Modify(ctx core.Context, id int32, menuData *UpdateMenuData) ( "name": menuData.Name, "link": menuData.Link, "icon": menuData.Icon, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := menu.NewQueryBuilder() diff --git a/internal/services/menu/service_updatesort.go b/internal/services/menu/service_updatesort.go index dc2e414..87bb52d 100644 --- a/internal/services/menu/service_updatesort.go +++ b/internal/services/menu/service_updatesort.go @@ -9,7 +9,7 @@ import ( func (s *service) UpdateSort(ctx core.Context, id int32, sort int32) (err error) { data := map[string]interface{}{ "sort": sort, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := menu.NewQueryBuilder() diff --git a/internal/services/menu/service_updateused.go b/internal/services/menu/service_updateused.go index a4a5948..44609a4 100644 --- a/internal/services/menu/service_updateused.go +++ b/internal/services/menu/service_updateused.go @@ -9,7 +9,7 @@ import ( func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) { data := map[string]interface{}{ "is_used": used, - "updated_user": ctx.UserName(), + "updated_user": ctx.SessionUserInfo().UserName, } qb := menu.NewQueryBuilder() diff --git a/main.go b/main.go index 8764fa6..ec1573b 100644 --- a/main.go +++ b/main.go @@ -27,8 +27,11 @@ import ( // @license.name MIT // @license.url https://github.com/xinliangnote/go-gin-api/blob/master/LICENSE -// @host 127.0.0.1:9999 -// @BasePath +// @securityDefinitions.apikey LoginToken +// @in header +// @name token + +// @BasePath / func main() { // 初始化 access logger accessLogger, err := logger.NewJSONLogger( diff --git a/pkg/p/print.go b/pkg/debugs/print.go similarity index 98% rename from pkg/p/print.go rename to pkg/debugs/print.go index 2bebf22..eb0a158 100644 --- a/pkg/p/print.go +++ b/pkg/debugs/print.go @@ -1,4 +1,4 @@ -package p +package debugs import ( "fmt" diff --git a/pkg/errno/errno.go b/pkg/errno/errno.go deleted file mode 100644 index c52f70c..0000000 --- a/pkg/errno/errno.go +++ /dev/null @@ -1,80 +0,0 @@ -package errno - -import ( - "encoding/json" - - "github.com/xinliangnote/go-gin-api/pkg/errors" -) - -var _ Error = (*err)(nil) - -type Error interface { - // i 为了避免被其他包实现 - i() - // 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 -} - -type err struct { - HttpCode int // HTTP Code - BusinessCode int // Business Code - Message string // 描述信息 - Err error // 错误信息 -} - -func NewError(httpCode, businessCode int, msg string) Error { - return &err{ - HttpCode: httpCode, - BusinessCode: businessCode, - Message: msg, - } -} - -func (e *err) i() {} - -func (e *err) WithErr(err error) Error { - e.Err = errors.WithStack(err) - return e -} - -func (e *err) GetHttpCode() int { - return e.HttpCode -} - -func (e *err) GetBusinessCode() int { - return e.BusinessCode -} - -func (e *err) GetMsg() string { - return e.Message -} - -func (e *err) GetErr() error { - return e.Err -} - -// ToString 返回 JSON 格式的错误详情 -func (e *err) ToString() string { - err := &struct { - HttpCode int `json:"http_code"` - BusinessCode int `json:"business_code"` - Message string `json:"message"` - }{ - HttpCode: e.HttpCode, - BusinessCode: e.BusinessCode, - Message: e.Message, - } - - raw, _ := json.Marshal(err) - return string(raw) -} diff --git a/pkg/trace/README.md b/pkg/trace/README.md deleted file mode 100644 index 167349d..0000000 --- a/pkg/trace/README.md +++ /dev/null @@ -1,81 +0,0 @@ -## trace - -一个用于开发调试的辅助工具。 - -可以实时显示当前页面的操作的请求信息、运行情况、SQL执行、错误提示等。 - -- `trace.go` 主入口文件; -- `dialog.go` 处理 third_party_requests 记录; -- `debug.go` 处理 debug 记录; - -#### 数据格式 - -##### trace_id - -当前 trace 的 ID,例如:938ff86be98439c6c1a7,便于搜索使用。 - -##### request - -请求信息,会包括: - -- ttl 请求超时时间,例如:2s 或 un-limit -- method 请求方式,例如:GET 或 POST -- decoded_url 请求地址 -- header 请求头信息 -- body 请求体信息 - -##### response - -- header 响应头信息 -- body 响应提信息 -- business_code 业务码,例如:10010 -- business_code_msg 业务码信息,例如:签名错误 -- http_code HTTP 状态码,例如:200 -- http_code_msg HTTP 状态码信息,例如:OK -- cost_seconds 耗费时长:单位秒,比如 0.001105661 - -##### third_party_requests - -每一个第三方 http 请求都会生成如下的一组数据,多个请求会生成多组数据。 - -- request,同上 request 结构一致 -- response,同上 response 结构一致 -- success,是否成功,true 或 false -- cost_seconds,耗费时长:单位秒 - -注意:response 中的 business_code、business_code_msg 为空,因为各个第三方返回结构不同,这两个字段为空。 - -##### sqls - -执行的 SQL 信息,多个 SQL 会记录多组数据。 - -- timestamp,时间,格式:2006-01-02 15:04:05 -- stack,文件地址和行号 -- cost_seconds,执行时长,单位:秒 -- sql,SQL 语句 -- rows_affected,影响行数 - -##### debugs - -- key 打印的标示 -- value 打印的值 - -```cassandraql -// 调试时,使用这个方法: -p.Print("key", "value", p.WithTrace(c.Trace())) -``` - -只有参数中增加了 `p.WithTrace(c.Trace())`,才会记录到 `debugs` 中。 - -##### success - -是否成功,true 或 false - -```cassandraql -success = !ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK -``` - -##### cost_seconds - -耗费时长:单位秒,比如 0.001105661 -