feature(1.2.8): swagger 接口文档新增 Security

- 将 middleware 命名为 interceptor
- 将 deploy 命名为 deployments
- 移除 pkg/errno
- 使用 proposal 目录
- 优化代码
This commit is contained in:
新亮
2021-11-28 13:25:27 +08:00
parent fc7e58db04
commit 4c37a7e6b5
114 changed files with 2611 additions and 1478 deletions

View File

@@ -1,12 +1,12 @@
# FROM 基于 golang:1.15-alpine # FROM 基于 golang:1.16-alpine
FROM golang:1.15-alpine AS builder FROM golang:1.16-alpine AS builder
# ENV 设置环境变量 # ENV 设置环境变量
ENV GOPATH=/opt/repo ENV GOPATH=/opt/repo
ENV GO111MODULE=on ENV GO111MODULE=on
ENV GOPROXY=https://goproxy.io,direct ENV GOPROXY=https://goproxy.io,direct
# ADD 源路径 目标路径 # COPY 源路径 目标路径
COPY . $GOPATH/src/github.com/xinliangnote/go-gin-api COPY . $GOPATH/src/github.com/xinliangnote/go-gin-api
# RUN 执行 go build . # RUN 执行 go build .

View File

@@ -89,14 +89,14 @@ func main() {
funcContent += fmt.Sprintf("// @Description%s \n", nameArr[1]) funcContent += fmt.Sprintf("// @Description%s \n", nameArr[1])
// Tags // Tags
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[1]) 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("// @Produce json \n")
funcContent += fmt.Sprintf("// @Param Request body %sRequest true \"请求信息\" \n", Lcfirst(v.Names[0].String())) 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("// @Success 200 {object} %sResponse \n", Lcfirst(v.Names[0].String()))
funcContent += fmt.Sprintf("// @Failure 400 {object} code.Failure \n") funcContent += fmt.Sprintf("// @Failure 400 {object} code.Failure \n")
// Router // Router
funcContent += fmt.Sprintf("%s \n", v.Decorations().Start.All()[2]) 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.WriteString(funcContent)
funcFile.Close() funcFile.Close()

View File

@@ -1,5 +1,7 @@
package configs package configs
import "time"
const ( const (
// MinGoVersion 最小 Go 版本 // MinGoVersion 最小 Go 版本
MinGoVersion = 1.16 MinGoVersion = 1.16
@@ -28,12 +30,15 @@ const (
// HeaderLoginToken 登录验证 TokenHeader 中传递的参数 // HeaderLoginToken 登录验证 TokenHeader 中传递的参数
HeaderLoginToken = "Token" HeaderLoginToken = "Token"
// HeaderSignToken 签名验证 TokenHeader 中传递的参数 // HeaderSignToken 签名验证 AuthorizationHeader 中传递的参数
HeaderSignToken = "Authorization" HeaderSignToken = "Authorization"
// HeaderSignTokenDate 签名验证 DateHeader 中传递的参数 // HeaderSignTokenDate 签名验证 DateHeader 中传递的参数
HeaderSignTokenDate = "Authorization-Date" HeaderSignTokenDate = "Authorization-Date"
// HeaderSignTokenTimeout 签名有效期为 2 分钟
HeaderSignTokenTimeout = time.Minute * 2
// RedisKeyPrefixLoginUser Redis Key 前缀 - 登录用户信息 // RedisKeyPrefixLoginUser Redis Key 前缀 - 登录用户信息
RedisKeyPrefixLoginUser = ProjectName + ":login-user:" RedisKeyPrefixLoginUser = ProjectName + ":login-user:"
@@ -48,4 +53,7 @@ const (
// MaxRequestsPerSecond 每秒最大请求量 // MaxRequestsPerSecond 每秒最大请求量
MaxRequestsPerSecond = 10000 MaxRequestsPerSecond = 10000
// LoginSessionTTL 登录有效期为 24 小时
LoginSessionTTL = time.Hour * 24
) )

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,4 @@
basePath: /
definitions: definitions:
admin.ListMenuData: admin.ListMenuData:
properties: properties:
@@ -428,6 +429,21 @@ definitions:
description: 主键ID description: 主键ID
type: integer type: integer
type: object 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: menu.createActionResponse:
properties: properties:
id: id:
@@ -647,7 +663,6 @@ definitions:
$ref: '#/definitions/tool.tableData' $ref: '#/definitions/tool.tableData'
type: array type: array
type: object type: object
host: 127.0.0.1:9999
info: info:
contact: {} contact: {}
license: license:
@@ -659,17 +674,21 @@ paths:
/api/admin: /api/admin:
get: get:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 管理员列表 description: 管理员列表
parameters: parameters:
- description: 第几页 - default: 1
description: 第几页
in: query in: query
name: page name: page
required: true
type: integer type: integer
- description: 每页显示条数 - default: 10
description: 每页显示条数
in: query in: query
name: page_size name: page_size
type: string required: true
type: integer
- description: 用户名 - description: 用户名
in: query in: query
name: username name: username
@@ -693,12 +712,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 管理员列表 summary: 管理员列表
tags: tags:
- API.admin - API.admin
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 新增管理员 description: 新增管理员
parameters: parameters:
- description: 用户名 - description: 用户名
@@ -716,7 +737,7 @@ paths:
name: mobile name: mobile
required: true required: true
type: string type: string
- description: 密码 - description: MD5后的密码
in: formData in: formData
name: password name: password
required: true required: true
@@ -732,6 +753,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 新增管理员 summary: 新增管理员
tags: tags:
- API.admin - API.admin
@@ -757,13 +780,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 删除管理员 summary: 删除管理员
tags: tags:
- API.admin - API.admin
/api/admin/info: /api/admin/info:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 管理员详情 description: 管理员详情
produces: produces:
- application/json - application/json
@@ -776,43 +801,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 管理员详情 summary: 管理员详情
tags: tags:
- API.admin - 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: /api/admin/logout:
post: post:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 管理员登出 description: 管理员登出
produces: produces:
- application/json - application/json
@@ -825,13 +822,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 管理员登出 summary: 管理员登出
tags: tags:
- API.admin - API.admin
/api/admin/menu: /api/admin/menu:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 提交菜单授权 description: 提交菜单授权
parameters: parameters:
- description: Hashid - description: Hashid
@@ -855,13 +854,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 提交菜单授权 summary: 提交菜单授权
tags: tags:
- API.admin - API.admin
/api/admin/menu/:id: /api/admin/menu/{id}:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 菜单授权列表 description: 菜单授权列表
parameters: parameters:
- description: hashId - description: hashId
@@ -880,13 +881,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 菜单授权列表 summary: 菜单授权列表
tags: tags:
- API.admin - API.admin
/api/admin/modify_password: /api/admin/modify_password:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 修改密码 description: 修改密码
parameters: parameters:
- description: 旧密码 - description: 旧密码
@@ -910,13 +913,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 修改密码 summary: 修改密码
tags: tags:
- API.admin - API.admin
/api/admin/modify_personal_info: /api/admin/modify_personal_info:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 修改个人信息 description: 修改个人信息
parameters: parameters:
- description: 昵称 - description: 昵称
@@ -940,13 +945,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 修改个人信息 summary: 修改个人信息
tags: tags:
- API.admin - API.admin
/api/admin/offline: /api/admin/offline:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 下线管理员 description: 下线管理员
parameters: parameters:
- description: Hashid - description: Hashid
@@ -965,6 +972,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 下线管理员 summary: 下线管理员
tags: tags:
- API.admin - API.admin
@@ -990,13 +999,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 重置密码 summary: 重置密码
tags: tags:
- API.admin - API.admin
/api/admin/used: /api/admin/used:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 更新管理员为启用/禁用 description: 更新管理员为启用/禁用
parameters: parameters:
- description: Hashid - description: Hashid
@@ -1020,23 +1031,29 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 更新管理员为启用/禁用 summary: 更新管理员为启用/禁用
tags: tags:
- API.admin - API.admin
/api/authorized: /api/authorized:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 调用方列表 description: 调用方列表
parameters: parameters:
- description: 第几页 - default: 1
description: 第几页
in: query in: query
name: page name: page
required: true
type: integer type: integer
- description: 每页显示条数 - default: 10
description: 每页显示条数
in: query in: query
name: page_size name: page_size
type: string required: true
type: integer
- description: 调用方key - description: 调用方key
in: query in: query
name: business_key name: business_key
@@ -1064,12 +1081,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 调用方列表 summary: 调用方列表
tags: tags:
- API.authorized - API.authorized
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 新增调用方 description: 新增调用方
parameters: parameters:
- description: 调用方key - description: 调用方key
@@ -1098,6 +1117,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 新增调用方 summary: 新增调用方
tags: tags:
- API.authorized - API.authorized
@@ -1123,16 +1144,18 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 删除调用方 summary: 删除调用方
tags: tags:
- API.authorized - API.authorized
/api/authorized/used: /api/authorized/used:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 更新调用方为启用/禁用 description: 更新调用方为启用/禁用
parameters: parameters:
- description: Hashid - description: hashID
in: formData in: formData
name: id name: id
required: true required: true
@@ -1153,13 +1176,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 更新调用方为启用/禁用 summary: 更新调用方为启用/禁用
tags: tags:
- API.authorized - API.authorized
/api/authorized_api: /api/authorized_api:
get: get:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 调用方接口地址列表 description: 调用方接口地址列表
parameters: parameters:
- description: hashID - description: hashID
@@ -1178,12 +1203,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 调用方接口地址列表 summary: 调用方接口地址列表
tags: tags:
- API.authorized - API.authorized
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 授权调用方接口地址 description: 授权调用方接口地址
parameters: parameters:
- description: HashID - description: HashID
@@ -1212,6 +1239,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 授权调用方接口地址 summary: 授权调用方接口地址
tags: tags:
- API.authorized - API.authorized
@@ -1237,13 +1266,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 删除调用方接口地址 summary: 删除调用方接口地址
tags: tags:
- API.authorized - API.authorized
/api/config/email: /api/config/email:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 修改邮件配置 description: 修改邮件配置
parameters: parameters:
- description: 邮箱服务器 - description: 邮箱服务器
@@ -1282,23 +1313,29 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 修改邮件配置 summary: 修改邮件配置
tags: tags:
- API.config - API.config
/api/cron: /api/cron:
get: get:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 任务列表 description: 任务列表
parameters: parameters:
- description: 第几页 - default: 1
description: 第几页
in: query in: query
name: page name: page
required: true
type: integer type: integer
- description: 每页显示条数 - default: 10
description: 每页显示条数
in: query in: query
name: page_size name: page_size
type: string required: true
type: integer
- description: 任务名称 - description: 任务名称
in: query in: query
name: name name: name
@@ -1322,12 +1359,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 任务列表 summary: 任务列表
tags: tags:
- API.cron - API.cron
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 创建任务 description: 创建任务
parameters: parameters:
- description: 任务名称 - description: 任务名称
@@ -1406,10 +1445,12 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 创建任务 summary: 创建任务
tags: tags:
- API.cron - API.cron
/api/cron/:id: /api/cron/{id}:
get: get:
consumes: consumes:
- application/json - application/json
@@ -1431,6 +1472,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 获取单条任务详情 summary: 获取单条任务详情
tags: tags:
- API.cron - API.cron
@@ -1455,16 +1498,17 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 手动执行单条任务 summary: 手动执行单条任务
tags: tags:
- API.cron - API.cron
/api/cron/{id}:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 编辑任务 description: 编辑任务
parameters: parameters:
- description: Hashid - description: hashID
in: formData in: formData
name: id name: id
required: true required: true
@@ -1545,16 +1589,18 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 编辑任务 summary: 编辑任务
tags: tags:
- API.cron - API.cron
/api/cron/used: /api/cron/used:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 更新任务为启用/禁用 description: 更新任务为启用/禁用
parameters: parameters:
- description: Hashid - description: hashID
in: formData in: formData
name: id name: id
required: true required: true
@@ -1575,13 +1621,47 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 更新任务为启用/禁用 summary: 更新任务为启用/禁用
tags: tags:
- API.cron - 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: /api/menu:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 菜单列表 description: 菜单列表
produces: produces:
- application/json - application/json
@@ -1594,12 +1674,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 菜单列表 summary: 菜单列表
tags: tags:
- API.menu - API.menu
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 创建/编辑菜单 description: 创建/编辑菜单
parameters: parameters:
- description: 请求信息 - description: 请求信息
@@ -1619,6 +1701,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 创建/编辑菜单 summary: 创建/编辑菜单
tags: tags:
- API.menu - API.menu
@@ -1644,12 +1728,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 删除菜单 summary: 删除菜单
tags: tags:
- API.menu - API.menu
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: 菜单详情 description: 菜单详情
parameters: parameters:
- description: hashId - description: hashId
@@ -1668,16 +1754,18 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 菜单详情 summary: 菜单详情
tags: tags:
- API.menu - API.menu
/api/menu/sort: /api/menu/sort:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 更新菜单排序 description: 更新菜单排序
parameters: parameters:
- description: Hashid - description: hashId
in: formData in: formData
name: id name: id
required: true required: true
@@ -1698,16 +1786,18 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 更新菜单排序 summary: 更新菜单排序
tags: tags:
- API.menu - API.menu
/api/menu/used: /api/menu/used:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 更新菜单为启用/禁用 description: 更新菜单为启用/禁用
parameters: parameters:
- description: Hashid - description: hashId
in: formData in: formData
name: id name: id
required: true required: true
@@ -1728,13 +1818,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 更新菜单为启用/禁用 summary: 更新菜单为启用/禁用
tags: tags:
- API.menu - API.menu
/api/menu_action: /api/menu_action:
get: get:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 功能权限列表 description: 功能权限列表
parameters: parameters:
- description: hashID - description: hashID
@@ -1753,12 +1845,14 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 功能权限列表 summary: 功能权限列表
tags: tags:
- API.menu - API.menu
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 创建功能权限 description: 创建功能权限
parameters: parameters:
- description: HashID - description: HashID
@@ -1787,6 +1881,8 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 创建功能权限 summary: 创建功能权限
tags: tags:
- API.menu - API.menu
@@ -1812,13 +1908,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 删除功能权限 summary: 删除功能权限
tags: tags:
- API.menu - API.menu
/api/tool/cache/clear: /api/tool/cache/clear:
patch: patch:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 清空缓存 description: 清空缓存
parameters: parameters:
- description: Redis Key - description: Redis Key
@@ -1837,13 +1935,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 清空缓存 summary: 清空缓存
tags: tags:
- API.tool - API.tool
/api/tool/cache/search: /api/tool/cache/search:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 查询缓存 description: 查询缓存
parameters: parameters:
- description: Redis Key - description: Redis Key
@@ -1862,13 +1962,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 查询缓存 summary: 查询缓存
tags: tags:
- API.tool - API.tool
/api/tool/data/dbs: /api/tool/data/dbs:
get: get:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 查询 DB description: 查询 DB
produces: produces:
- application/json - application/json
@@ -1881,13 +1983,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 查询 DB summary: 查询 DB
tags: tags:
- API.tool - API.tool
/api/tool/data/mysql: /api/tool/data/mysql:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 执行 SQL 语句 description: 执行 SQL 语句
parameters: parameters:
- description: 数据库名称 - description: 数据库名称
@@ -1916,13 +2020,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 执行 SQL 语句 summary: 执行 SQL 语句
tags: tags:
- API.tool - API.tool
/api/tool/data/tables: /api/tool/data/tables:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 查询 Table description: 查询 Table
parameters: parameters:
- description: 数据库名称 - description: 数据库名称
@@ -1941,13 +2047,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 查询 Table summary: 查询 Table
tags: tags:
- API.tool - API.tool
/api/tool/hashids/decode/{id}: /api/tool/hashids/decode/{id}:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: HashIds 解密 description: HashIds 解密
parameters: parameters:
- description: 需解密的密文 - description: 需解密的密文
@@ -1966,13 +2074,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: HashIds 解密 summary: HashIds 解密
tags: tags:
- API.tool - API.tool
/api/tool/hashids/encode/{id}: /api/tool/hashids/encode/{id}:
get: get:
consumes: consumes:
- application/json - application/x-www-form-urlencoded
description: HashIds 加密 description: HashIds 加密
parameters: parameters:
- description: 需加密的数字 - description: 需加密的数字
@@ -1991,13 +2101,15 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: HashIds 加密 summary: HashIds 加密
tags: tags:
- API.tool - API.tool
/api/tool/send_message: /api/tool/send_message:
post: post:
consumes: consumes:
- multipart/form-data - application/x-www-form-urlencoded
description: 发送消息 description: 发送消息
parameters: parameters:
- description: 消息内容 - description: 消息内容
@@ -2016,7 +2128,79 @@ paths:
description: Bad Request description: Bad Request
schema: schema:
$ref: '#/definitions/code.Failure' $ref: '#/definitions/code.Failure'
security:
- LoginToken: []
summary: 发送消息 summary: 发送消息
tags: tags:
- API.tool - 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" swagger: "2.0"

53
internal/alert/alert.go Normal file
View File

@@ -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
}
}

View File

@@ -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 = `
<!DOCTYPE html> <!DOCTYPE html>
<html> <html>
@@ -27,7 +75,7 @@ const PanicMail = `
<!-- Logo --> <!-- Logo -->
<tr> <tr>
<td style="padding: 25px 0; text-align: center;"> <td style="padding: 25px 0; text-align: center;">
系统异常 系统告警
</td> </td>
</tr> </tr>

View File

@@ -7,14 +7,13 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/validation"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createRequest struct { type createRequest struct {
Username string `form:"username" binding:"required"` // 用户名 Username string `form:"username" binding:"required"` // 用户名
Nickname string `form:"nickname" binding:"required"` // 昵称 Nickname string `form:"nickname" binding:"required"` // 昵称
Mobile string `form:"mobile" binding:"required"` // 手机号 Mobile string `form:"mobile" binding:"required"` // 手机号
Password string `form:"password" binding:"required"` // 密码 Password string `form:"password" binding:"required"` // MD5后的密码
} }
type createResponse struct { type createResponse struct {
@@ -25,24 +24,25 @@ type createResponse struct {
// @Summary 新增管理员 // @Summary 新增管理员
// @Description 新增管理员 // @Description 新增管理员
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param username formData string true "用户名" // @Param username formData string true "用户名"
// @Param nickname formData string true "昵称" // @Param nickname formData string true "昵称"
// @Param mobile formData string true "手机号" // @Param mobile formData string true "手机号"
// @Param password formData string true "密码" // @Param password formData string true "MD5后的密码"
// @Success 200 {object} createResponse // @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin [post] // @Router /api/admin [post]
// @Security LoginToken
func (h *handler) Create() core.HandlerFunc { func (h *handler) Create() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createRequest) req := new(createRequest)
res := new(createResponse) res := new(createResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
@@ -55,10 +55,10 @@ func (h *handler) Create() core.HandlerFunc {
id, err := h.adminService.Create(c, createData) id, err := h.adminService.Create(c, createData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminCreateError, code.AdminCreateError,
code.Text(code.AdminCreateError)).WithErr(err), code.Text(code.AdminCreateError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createAdminMenuRequest struct { type createAdminMenuRequest struct {
@@ -22,32 +21,33 @@ type createAdminMenuResponse struct {
// @Summary 提交菜单授权 // @Summary 提交菜单授权
// @Description 提交菜单授权 // @Description 提交菜单授权
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "Hashid"
// @Param actions formData string true "功能权限ID,多个用,分割" // @Param actions formData string true "功能权限ID,多个用,分割"
// @Success 200 {object} createResponse // @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/menu [post] // @Router /api/admin/menu [post]
// @Security LoginToken
func (h *handler) CreateAdminMenu() core.HandlerFunc { func (h *handler) CreateAdminMenu() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createAdminMenuRequest) req := new(createAdminMenuRequest)
res := new(createAdminMenuResponse) res := new(createAdminMenuResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -58,10 +58,10 @@ func (h *handler) CreateAdminMenu() core.HandlerFunc {
err = h.adminService.CreateMenu(c, createData) err = h.adminService.CreateMenu(c, createData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminMenuCreateError, code.AdminMenuCreateError,
code.Text(code.AdminMenuCreateError)).WithErr(err), code.Text(code.AdminMenuCreateError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type deleteRequest struct { type deleteRequest struct {
@@ -26,25 +25,26 @@ type deleteResponse struct {
// @Success 200 {object} deleteResponse // @Success 200 {object} deleteResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/{id} [delete] // @Router /api/admin/{id} [delete]
// @Security LoginToken
func (h *handler) Delete() core.HandlerFunc { func (h *handler) Delete() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(deleteRequest) req := new(deleteRequest)
res := new(deleteResponse) res := new(deleteResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
err = h.adminService.Delete(c, id) err = h.adminService.Delete(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminDeleteError, code.AdminDeleteError,
code.Text(code.AdminDeleteError)).WithErr(err), code.Text(code.AdminDeleteError)).WithError(err),
) )
return return
} }

View File

@@ -10,9 +10,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/password" "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/repository/redis"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast"
) )
type detailResponse struct { type detailResponse struct {
@@ -26,35 +23,36 @@ type detailResponse struct {
// @Summary 管理员详情 // @Summary 管理员详情
// @Description 管理员详情 // @Description 管理员详情
// @Tags API.admin // @Tags API.admin
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Success 200 {object} detailResponse // @Success 200 {object} detailResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/info [get] // @Router /api/admin/info [get]
// @Security LoginToken
func (h *handler) Detail() core.HandlerFunc { func (h *handler) Detail() core.HandlerFunc {
return func(c core.Context) { return func(ctx core.Context) {
res := new(detailResponse) res := new(detailResponse)
searchOneData := new(admin.SearchOneData) searchOneData := new(admin.SearchOneData)
searchOneData.Id = cast.ToInt32(c.UserID()) searchOneData.Id = ctx.SessionUserInfo().UserID
searchOneData.IsUsed = 1 searchOneData.IsUsed = 1
info, err := h.adminService.Detail(c, searchOneData) info, err := h.adminService.Detail(ctx, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminDetailError, code.AdminDetailError,
code.Text(code.AdminDetailError)).WithErr(err), code.Text(code.AdminDetailError)).WithError(err),
) )
return 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 { if err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminDetailError, code.AdminDetailError,
code.Text(code.AdminDetailError)).WithErr(err), code.Text(code.AdminDetailError)).WithError(err),
) )
return return
} }
@@ -62,10 +60,10 @@ func (h *handler) Detail() core.HandlerFunc {
var menuData []admin.ListMyMenuData var menuData []admin.ListMyMenuData
err = json.Unmarshal([]byte(menuCacheData), &menuData) err = json.Unmarshal([]byte(menuCacheData), &menuData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminDetailError, code.AdminDetailError,
code.Text(code.AdminDetailError)).WithErr(err), code.Text(code.AdminDetailError)).WithError(err),
) )
return return
} }
@@ -74,6 +72,6 @@ func (h *handler) Detail() core.HandlerFunc {
res.Nickname = info.Nickname res.Nickname = info.Nickname
res.Mobile = info.Mobile res.Mobile = info.Mobile
res.Menu = menuData res.Menu = menuData
c.Payload(res) ctx.Payload(res)
} }
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/password"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "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/xinliangnote/go-gin-api/pkg/timeutil"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -49,25 +48,26 @@ type listResponse struct {
// @Summary 管理员列表 // @Summary 管理员列表
// @Description 管理员列表 // @Description 管理员列表
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param page query int false "第几页" // @Param page query int true "第几页" default(1)
// @Param page_size query string false "每页显示条数" // @Param page_size query int true "每页显示条数" default(10)
// @Param username query string false "用户名" // @Param username query string false "用户名"
// @Param nickname query string false "昵称" // @Param nickname query string false "昵称"
// @Param mobile query string false "手机号" // @Param mobile query string false "手机号"
// @Success 200 {object} listResponse // @Success 200 {object} listResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin [get] // @Router /api/admin [get]
// @Security LoginToken
func (h *handler) List() core.HandlerFunc { func (h *handler) List() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(listRequest) req := new(listRequest)
res := new(listResponse) res := new(listResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -91,20 +91,20 @@ func (h *handler) List() core.HandlerFunc {
resListData, err := h.adminService.PageList(c, searchData) resListData, err := h.adminService.PageList(c, searchData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminListError, code.AdminListError,
code.Text(code.AdminListError)).WithErr(err), code.Text(code.AdminListError)).WithError(err),
) )
return return
} }
resCountData, err := h.adminService.PageListCount(c, searchData) resCountData, err := h.adminService.PageListCount(c, searchData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminListError, code.AdminListError,
code.Text(code.AdminListError)).WithErr(err), code.Text(code.AdminListError)).WithError(err),
) )
return return
} }
@@ -116,10 +116,10 @@ func (h *handler) List() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type listAdminMenuRequest struct { type listAdminMenuRequest struct {
@@ -22,31 +21,32 @@ type listAdminMenuResponse struct {
// @Summary 菜单授权列表 // @Summary 菜单授权列表
// @Description 菜单授权列表 // @Description 菜单授权列表
// @Tags API.admin // @Tags API.admin
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id path string true "hashId" // @Param id path string true "hashId"
// @Success 200 {object} listAdminMenuResponse // @Success 200 {object} listAdminMenuResponse
// @Failure 400 {object} code.Failure // @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 { func (h *handler) ListAdminMenu() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(listAdminMenuRequest) req := new(listAdminMenuRequest)
res := new(listAdminMenuResponse) res := new(listAdminMenuResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -57,10 +57,10 @@ func (h *handler) ListAdminMenu() core.HandlerFunc {
info, err := h.adminService.Detail(c, searchOneData) info, err := h.adminService.Detail(c, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminMenuListError, code.AdminMenuListError,
code.Text(code.AdminMenuListError)).WithErr(err), code.Text(code.AdminMenuListError)).WithError(err),
) )
return return
} }
@@ -72,10 +72,10 @@ func (h *handler) ListAdminMenu() core.HandlerFunc {
listData, err := h.adminService.ListMenu(c, searchData) listData, err := h.adminService.ListMenu(c, searchData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminMenuListError, code.AdminMenuListError,
code.Text(code.AdminMenuListError)).WithErr(err), code.Text(code.AdminMenuListError)).WithError(err),
) )
return return
} }

View File

@@ -3,15 +3,14 @@ package admin
import ( import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"time"
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/password" "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/repository/redis"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "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/errors"
) )
@@ -28,22 +27,23 @@ type loginResponse struct {
// @Summary 管理员登录 // @Summary 管理员登录
// @Description 管理员登录 // @Description 管理员登录
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param username formData string true "用户名" // @Param username formData string true "用户名"
// @Param password formData string true "密码" // @Param password formData string true "MD5后的密码"
// @Success 200 {object} loginResponse // @Success 200 {object} loginResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/login [post] // @Router /api/login [post]
// @Security LoginToken
func (h *handler) Login() core.HandlerFunc { func (h *handler) Login() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(loginRequest) req := new(loginRequest)
res := new(loginResponse) res := new(loginResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -55,47 +55,38 @@ func (h *handler) Login() core.HandlerFunc {
info, err := h.adminService.Detail(c, searchOneData) info, err := h.adminService.Detail(c, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }
if info == nil { if info == nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(errors.New("未查询出符合条件的用户")), code.Text(code.AdminLoginError)).WithError(errors.New("未查询出符合条件的用户")),
) )
return return
} }
token := password.GenerateLoginToken(info.Id) token := password.GenerateLoginToken(info.Id)
adminCacheData := &struct { // 用户信息
Id int32 `json:"id"` // 主键ID sessionUserInfo := &proposal.SessionUserInfo{
Username string `json:"username"` // 用户名 UserID: info.Id,
Nickname string `json:"nickname"` // 昵称 UserName: info.Username,
Mobile string `json:"mobile"` // 手机号
}{
Id: info.Id,
Username: info.Username,
Nickname: info.Nickname,
Mobile: info.Mobile,
} }
// 用户信息
adminJsonInfo, _ := json.Marshal(adminCacheData)
// 将用户信息记录到 Redis 中 // 将用户信息记录到 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 { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }
@@ -104,10 +95,10 @@ func (h *handler) Login() core.HandlerFunc {
searchMenuData.AdminId = info.Id searchMenuData.AdminId = info.Id
menu, err := h.adminService.MyMenu(c, searchMenuData) menu, err := h.adminService.MyMenu(c, searchMenuData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }
@@ -116,12 +107,12 @@ func (h *handler) Login() core.HandlerFunc {
menuJsonInfo, _ := json.Marshal(menu) menuJsonInfo, _ := json.Marshal(menu)
// 将菜单栏信息记录到 Redis 中 // 将菜单栏信息记录到 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 { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }
@@ -130,10 +121,10 @@ func (h *handler) Login() core.HandlerFunc {
searchActionData.AdminId = info.Id searchActionData.AdminId = info.Id
action, err := h.adminService.MyAction(c, searchActionData) action, err := h.adminService.MyAction(c, searchActionData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }
@@ -142,12 +133,12 @@ func (h *handler) Login() core.HandlerFunc {
actionJsonInfo, _ := json.Marshal(action) actionJsonInfo, _ := json.Marshal(action)
// 将可访问接口信息记录到 Redis 中 // 将可访问接口信息记录到 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 { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLoginError, code.AdminLoginError,
code.Text(code.AdminLoginError)).WithErr(err), code.Text(code.AdminLoginError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "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" "github.com/xinliangnote/go-gin-api/pkg/errors"
) )
@@ -19,21 +18,22 @@ type logoutResponse struct {
// @Summary 管理员登出 // @Summary 管理员登出
// @Description 管理员登出 // @Description 管理员登出
// @Tags API.admin // @Tags API.admin
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Success 200 {object} logoutResponse // @Success 200 {object} logoutResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/logout [post] // @Router /api/admin/logout [post]
// @Security LoginToken
func (h *handler) Logout() core.HandlerFunc { func (h *handler) Logout() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
res := new(logoutResponse) 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())) { if !h.cache.Del(configs.RedisKeyPrefixLoginUser+c.GetHeader(configs.HeaderLoginToken), redis.WithTrace(c.Trace())) {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminLogOutError, code.AdminLogOutError,
code.Text(code.AdminLogOutError)).WithErr(errors.New("cache del err")), code.Text(code.AdminLogOutError)).WithError(errors.New("cache del err")),
) )
return return
} }

View File

@@ -7,9 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/password"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast"
) )
type modifyPasswordRequest struct { type modifyPasswordRequest struct {
@@ -25,53 +22,52 @@ type modifyPasswordResponse struct {
// @Summary 修改密码 // @Summary 修改密码
// @Description 修改密码 // @Description 修改密码
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param old_password formData string true "旧密码" // @Param old_password formData string true "旧密码"
// @Param new_password formData string true "新密码" // @Param new_password formData string true "新密码"
// @Success 200 {object} modifyPasswordResponse // @Success 200 {object} modifyPasswordResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/modify_password [patch] // @Router /api/admin/modify_password [patch]
// @Security LoginToken
func (h *handler) ModifyPassword() core.HandlerFunc { func (h *handler) ModifyPassword() core.HandlerFunc {
return func(c core.Context) { return func(ctx core.Context) {
req := new(modifyPasswordRequest) req := new(modifyPasswordRequest)
res := new(modifyPasswordResponse) res := new(modifyPasswordResponse)
if err := c.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
userId := cast.ToInt32(c.UserID())
searchOneData := new(admin.SearchOneData) searchOneData := new(admin.SearchOneData)
searchOneData.Id = userId searchOneData.Id = ctx.SessionUserInfo().UserID
searchOneData.Password = password.GeneratePassword(req.OldPassword) searchOneData.Password = password.GeneratePassword(req.OldPassword)
searchOneData.IsUsed = 1 searchOneData.IsUsed = 1
info, err := h.adminService.Detail(c, searchOneData) info, err := h.adminService.Detail(ctx, searchOneData)
if err != nil || info == nil { if err != nil || info == nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminModifyPasswordError, code.AdminModifyPasswordError,
code.Text(code.AdminModifyPasswordError)).WithErr(err), code.Text(code.AdminModifyPasswordError)).WithError(err),
) )
return return
} }
if err := h.adminService.ModifyPassword(c, userId, req.NewPassword); err != nil { if err := h.adminService.ModifyPassword(ctx, ctx.SessionUserInfo().UserID, req.NewPassword); err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminModifyPasswordError, code.AdminModifyPasswordError,
code.Text(code.AdminModifyPasswordError)).WithErr(err), code.Text(code.AdminModifyPasswordError)).WithError(err),
) )
return return
} }
res.Username = c.UserName() res.Username = ctx.SessionUserInfo().UserName
c.Payload(res) ctx.Payload(res)
} }
} }

View File

@@ -6,9 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "github.com/xinliangnote/go-gin-api/internal/services/admin"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast"
) )
type modifyPersonalInfoRequest struct { type modifyPersonalInfoRequest struct {
@@ -24,42 +21,41 @@ type modifyPersonalInfoResponse struct {
// @Summary 修改个人信息 // @Summary 修改个人信息
// @Description 修改个人信息 // @Description 修改个人信息
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param nickname formData string true "昵称" // @Param nickname formData string true "昵称"
// @Param mobile formData string true "手机号" // @Param mobile formData string true "手机号"
// @Success 200 {object} modifyPersonalInfoResponse // @Success 200 {object} modifyPersonalInfoResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/modify_personal_info [patch] // @Router /api/admin/modify_personal_info [patch]
// @Security LoginToken
func (h *handler) ModifyPersonalInfo() core.HandlerFunc { func (h *handler) ModifyPersonalInfo() core.HandlerFunc {
return func(c core.Context) { return func(ctx core.Context) {
req := new(modifyPersonalInfoRequest) req := new(modifyPersonalInfoRequest)
res := new(modifyPersonalInfoResponse) res := new(modifyPersonalInfoResponse)
if err := c.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
userId := cast.ToInt32(c.UserID())
modifyData := new(admin.ModifyData) modifyData := new(admin.ModifyData)
modifyData.Nickname = req.Nickname modifyData.Nickname = req.Nickname
modifyData.Mobile = req.Mobile modifyData.Mobile = req.Mobile
if err := h.adminService.ModifyPersonalInfo(c, userId, modifyData); err != nil { if err := h.adminService.ModifyPersonalInfo(ctx, ctx.SessionUserInfo().UserID, modifyData); err != nil {
c.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminModifyPersonalInfoError, code.AdminModifyPersonalInfoError,
code.Text(code.AdminModifyPersonalInfoError)).WithErr(err), code.Text(code.AdminModifyPersonalInfoError)).WithError(err),
) )
return return
} }
res.Username = c.UserName() res.Username = ctx.SessionUserInfo().UserName
c.Payload(res) ctx.Payload(res)
} }
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/password"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type offlineRequest struct { type offlineRequest struct {
@@ -23,31 +22,32 @@ type offlineResponse struct {
// @Summary 下线管理员 // @Summary 下线管理员
// @Description 下线管理员 // @Description 下线管理员
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "Hashid"
// @Success 200 {object} offlineResponse // @Success 200 {object} offlineResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/offline [patch] // @Router /api/admin/offline [patch]
// @Security LoginToken
func (h *handler) Offline() core.HandlerFunc { func (h *handler) Offline() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(offlineRequest) req := new(offlineRequest)
res := new(offlineResponse) res := new(offlineResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -56,7 +56,7 @@ func (h *handler) Offline() core.HandlerFunc {
b := h.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), redis.WithTrace(c.Trace())) b := h.cache.Del(configs.RedisKeyPrefixLoginUser+password.GenerateLoginToken(id), redis.WithTrace(c.Trace()))
if !b { if !b {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminOfflineError, code.AdminOfflineError,
code.Text(code.AdminOfflineError)), code.Text(code.AdminOfflineError)),

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type resetPasswordRequest struct { type resetPasswordRequest struct {
@@ -26,25 +25,26 @@ type resetPasswordResponse struct {
// @Success 200 {object} resetPasswordResponse // @Success 200 {object} resetPasswordResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/reset_password/{id} [patch] // @Router /api/admin/reset_password/{id} [patch]
// @Security LoginToken
func (h *handler) ResetPassword() core.HandlerFunc { func (h *handler) ResetPassword() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(resetPasswordRequest) req := new(resetPasswordRequest)
res := new(resetPasswordResponse) res := new(resetPasswordResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) ResetPassword() core.HandlerFunc {
err = h.adminService.ResetPassword(c, id) err = h.adminService.ResetPassword(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminResetPasswordError, code.AdminResetPasswordError,
code.Text(code.AdminResetPasswordError)).WithErr(err), code.Text(code.AdminResetPasswordError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type updateUsedRequest struct { type updateUsedRequest struct {
@@ -21,32 +20,33 @@ type updateUsedResponse struct {
// @Summary 更新管理员为启用/禁用 // @Summary 更新管理员为启用/禁用
// @Description 更新管理员为启用/禁用 // @Description 更新管理员为启用/禁用
// @Tags API.admin // @Tags API.admin
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "Hashid"
// @Param used formData int true "是否启用 1:是 -1:否" // @Param used formData int true "是否启用 1:是 -1:否"
// @Success 200 {object} updateUsedResponse // @Success 200 {object} updateUsedResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/admin/used [patch] // @Router /api/admin/used [patch]
// @Security LoginToken
func (h *handler) UpdateUsed() core.HandlerFunc { func (h *handler) UpdateUsed() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(updateUsedRequest) req := new(updateUsedRequest)
res := new(updateUsedResponse) res := new(updateUsedResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
err = h.adminService.UpdateUsed(c, id, req.Used) err = h.adminService.UpdateUsed(c, id, req.Used)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminUpdateError, code.AdminUpdateError,
code.Text(code.AdminUpdateError)).WithErr(err), code.Text(code.AdminUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -18,7 +18,7 @@ type Handler interface {
// Login 管理员登录 // Login 管理员登录
// @Tags API.admin // @Tags API.admin
// @Router /api/admin/login [post] // @Router /api/login [post]
Login() core.HandlerFunc Login() core.HandlerFunc
// Logout 管理员登出 // Logout 管理员登出
@@ -78,7 +78,7 @@ type Handler interface {
// ListAdminMenu 菜单授权列表 // ListAdminMenu 菜单授权列表
// @Tags API.admin // @Tags API.admin
// @Router /api/admin/menu/:id [get] // @Router /api/admin/menu/{id} [get]
ListAdminMenu() core.HandlerFunc ListAdminMenu() core.HandlerFunc
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/authorized" "github.com/xinliangnote/go-gin-api/internal/services/authorized"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createRequest struct { type createRequest struct {
@@ -23,7 +22,7 @@ type createResponse struct {
// @Summary 新增调用方 // @Summary 新增调用方
// @Description 新增调用方 // @Description 新增调用方
// @Tags API.authorized // @Tags API.authorized
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param business_key formData string true "调用方key" // @Param business_key formData string true "调用方key"
// @Param business_developer formData string true "调用方对接人" // @Param business_developer formData string true "调用方对接人"
@@ -31,15 +30,16 @@ type createResponse struct {
// @Success 200 {object} createResponse // @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized [post] // @Router /api/authorized [post]
// @Security LoginToken
func (h *handler) Create() core.HandlerFunc { func (h *handler) Create() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createRequest) req := new(createRequest)
res := new(createResponse) res := new(createResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -51,10 +51,10 @@ func (h *handler) Create() core.HandlerFunc {
id, err := h.authorizedService.Create(c, createData) id, err := h.authorizedService.Create(c, createData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedCreateError, code.AuthorizedCreateError,
code.Text(code.AuthorizedCreateError)).WithErr(err), code.Text(code.AuthorizedCreateError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/authorized" "github.com/xinliangnote/go-gin-api/internal/services/authorized"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createAPIRequest struct { type createAPIRequest struct {
@@ -23,7 +22,7 @@ type createAPIResponse struct {
// @Summary 授权调用方接口地址 // @Summary 授权调用方接口地址
// @Description 授权调用方接口地址 // @Description 授权调用方接口地址
// @Tags API.authorized // @Tags API.authorized
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "HashID" // @Param id formData string true "HashID"
// @Param method formData string true "请求方法" // @Param method formData string true "请求方法"
@@ -31,25 +30,26 @@ type createAPIResponse struct {
// @Success 200 {object} createAPIResponse // @Success 200 {object} createAPIResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized_api [post] // @Router /api/authorized_api [post]
// @Security LoginToken
func (h *handler) CreateAPI() core.HandlerFunc { func (h *handler) CreateAPI() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createAPIRequest) req := new(createAPIRequest)
res := new(createAPIResponse) res := new(createAPIResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -59,10 +59,10 @@ func (h *handler) CreateAPI() core.HandlerFunc {
// 通过 id 查询出 business_key // 通过 id 查询出 business_key
authorizedInfo, err := h.authorizedService.Detail(c, id) authorizedInfo, err := h.authorizedService.Detail(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedDetailError, code.AuthorizedDetailError,
code.Text(code.AuthorizedDetailError)).WithErr(err), code.Text(code.AuthorizedDetailError)).WithError(err),
) )
return return
} }
@@ -74,10 +74,10 @@ func (h *handler) CreateAPI() core.HandlerFunc {
createId, err := h.authorizedService.CreateAPI(c, createAPIData) createId, err := h.authorizedService.CreateAPI(c, createAPIData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedCreateAPIError, code.AuthorizedCreateAPIError,
code.Text(code.AuthorizedCreateAPIError)).WithErr(err), code.Text(code.AuthorizedCreateAPIError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type deleteRequest struct { type deleteRequest struct {
@@ -26,25 +25,26 @@ type deleteResponse struct {
// @Success 200 {object} deleteResponse // @Success 200 {object} deleteResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized/{id} [delete] // @Router /api/authorized/{id} [delete]
// @Security LoginToken
func (h *handler) Delete() core.HandlerFunc { func (h *handler) Delete() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(deleteRequest) req := new(deleteRequest)
res := new(deleteResponse) res := new(deleteResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
err = h.authorizedService.Delete(c, id) err = h.authorizedService.Delete(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedDeleteError, code.AuthorizedDeleteError,
code.Text(code.AuthorizedDeleteError)).WithErr(err), code.Text(code.AuthorizedDeleteError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type deleteAPIRequest struct { type deleteAPIRequest struct {
@@ -26,25 +25,26 @@ type deleteAPIResponse struct {
// @Success 200 {object} deleteAPIResponse // @Success 200 {object} deleteAPIResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized_api/{id} [delete] // @Router /api/authorized_api/{id} [delete]
// @Security LoginToken
func (h *handler) DeleteAPI() core.HandlerFunc { func (h *handler) DeleteAPI() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(deleteAPIRequest) req := new(deleteAPIRequest)
res := new(deleteAPIResponse) res := new(deleteAPIResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) DeleteAPI() core.HandlerFunc {
err = h.authorizedService.DeleteAPI(c, id) err = h.authorizedService.DeleteAPI(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedDeleteAPIError, code.AuthorizedDeleteAPIError,
code.Text(code.AuthorizedDeleteAPIError)).WithErr(err), code.Text(code.AuthorizedDeleteAPIError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/authorized" "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/xinliangnote/go-gin-api/pkg/timeutil"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -48,10 +47,10 @@ type listResponse struct {
// @Summary 调用方列表 // @Summary 调用方列表
// @Description 调用方列表 // @Description 调用方列表
// @Tags API.authorized // @Tags API.authorized
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param page query int false "第几页" // @Param page query int true "第几页" default(1)
// @Param page_size query string false "每页显示条数" // @Param page_size query int true "每页显示条数" default(10)
// @Param business_key query string false "调用方key" // @Param business_key query string false "调用方key"
// @Param business_secret query string false "调用方secret" // @Param business_secret query string false "调用方secret"
// @Param business_developer query string false "调用方对接人" // @Param business_developer query string false "调用方对接人"
@@ -59,15 +58,16 @@ type listResponse struct {
// @Success 200 {object} listResponse // @Success 200 {object} listResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized [get] // @Router /api/authorized [get]
// @Security LoginToken
func (h *handler) List() core.HandlerFunc { func (h *handler) List() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(listRequest) req := new(listRequest)
res := new(listResponse) res := new(listResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -91,20 +91,20 @@ func (h *handler) List() core.HandlerFunc {
resListData, err := h.authorizedService.PageList(c, searchData) resListData, err := h.authorizedService.PageList(c, searchData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedListError, code.AuthorizedListError,
code.Text(code.AuthorizedListError)).WithErr(err), code.Text(code.AuthorizedListError)).WithError(err),
) )
return return
} }
resCountData, err := h.authorizedService.PageListCount(c, searchData) resCountData, err := h.authorizedService.PageListCount(c, searchData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedListError, code.AuthorizedListError,
code.Text(code.AuthorizedListError)).WithErr(err), code.Text(code.AuthorizedListError)).WithError(err),
) )
return return
} }
@@ -116,10 +116,10 @@ func (h *handler) List() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/authorized" "github.com/xinliangnote/go-gin-api/internal/services/authorized"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -31,31 +30,32 @@ type listAPIResponse struct {
// @Summary 调用方接口地址列表 // @Summary 调用方接口地址列表
// @Description 调用方接口地址列表 // @Description 调用方接口地址列表
// @Tags API.authorized // @Tags API.authorized
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id query string true "hashID" // @Param id query string true "hashID"
// @Success 200 {object} listAPIResponse // @Success 200 {object} listAPIResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized_api [get] // @Router /api/authorized_api [get]
// @Security LoginToken
func (h *handler) ListAPI() core.HandlerFunc { func (h *handler) ListAPI() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(listAPIRequest) req := new(listAPIRequest)
res := new(listAPIResponse) res := new(listAPIResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -65,10 +65,10 @@ func (h *handler) ListAPI() core.HandlerFunc {
// 通过 id 查询出 business_key // 通过 id 查询出 business_key
authorizedInfo, err := h.authorizedService.Detail(c, id) authorizedInfo, err := h.authorizedService.Detail(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedDetailError, code.AuthorizedDetailError,
code.Text(code.AuthorizedDetailError)).WithErr(err), code.Text(code.AuthorizedDetailError)).WithError(err),
) )
return return
} }
@@ -80,10 +80,10 @@ func (h *handler) ListAPI() core.HandlerFunc {
resListData, err := h.authorizedService.ListAPI(c, searchAPIData) resListData, err := h.authorizedService.ListAPI(c, searchAPIData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedListAPIError, code.AuthorizedListAPIError,
code.Text(code.AuthorizedListAPIError)).WithErr(err), code.Text(code.AuthorizedListAPIError)).WithError(err),
) )
return return
} }
@@ -93,10 +93,10 @@ func (h *handler) ListAPI() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type updateUsedRequest struct { type updateUsedRequest struct {
@@ -21,32 +20,33 @@ type updateUsedResponse struct {
// @Summary 更新调用方为启用/禁用 // @Summary 更新调用方为启用/禁用
// @Description 更新调用方为启用/禁用 // @Description 更新调用方为启用/禁用
// @Tags API.authorized // @Tags API.authorized
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "hashID"
// @Param used formData int true "是否启用 1:是 -1:否" // @Param used formData int true "是否启用 1:是 -1:否"
// @Success 200 {object} updateUsedResponse // @Success 200 {object} updateUsedResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/authorized/used [patch] // @Router /api/authorized/used [patch]
// @Security LoginToken
func (h *handler) UpdateUsed() core.HandlerFunc { func (h *handler) UpdateUsed() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(updateUsedRequest) req := new(updateUsedRequest)
res := new(updateUsedResponse) res := new(updateUsedResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
err = h.authorizedService.UpdateUsed(c, id, req.Used) err = h.authorizedService.UpdateUsed(c, id, req.Used)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedUpdateError, code.AuthorizedUpdateError,
code.Text(code.AuthorizedUpdateError)).WithErr(err), code.Text(code.AuthorizedUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/env" "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/xinliangnote/go-gin-api/pkg/mail"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -31,7 +30,7 @@ type emailResponse struct {
// @Summary 修改邮件配置 // @Summary 修改邮件配置
// @Description 修改邮件配置 // @Description 修改邮件配置
// @Tags API.config // @Tags API.config
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param host formData string true "邮箱服务器" // @Param host formData string true "邮箱服务器"
// @Param port formData string true "端口" // @Param port formData string true "端口"
@@ -41,15 +40,16 @@ type emailResponse struct {
// @Success 200 {object} emailResponse // @Success 200 {object} emailResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/config/email [patch] // @Router /api/config/email [patch]
// @Security LoginToken
func (h *handler) Email() core.HandlerFunc { func (h *handler) Email() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(emailRequest) req := new(emailRequest)
res := new(emailResponse) res := new(emailResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -64,10 +64,10 @@ func (h *handler) Email() core.HandlerFunc {
Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName, env.Active().Value()), Body: fmt.Sprintf("%s[%s] 已添加您为系统告警通知人。", configs.ProjectName, env.Active().Value()),
} }
if err := mail.Send(options); err != nil { if err := mail.Send(options); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.SendEmailError, code.SendEmailError,
code.Text(code.SendEmailError)+err.Error()).WithErr(err), code.Text(code.SendEmailError)+err.Error()).WithError(err),
) )
return return
} }
@@ -80,10 +80,10 @@ func (h *handler) Email() core.HandlerFunc {
err := viper.WriteConfig() err := viper.WriteConfig()
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.WriteConfigError, code.WriteConfigError,
code.Text(code.WriteConfigError)).WithErr(err), code.Text(code.WriteConfigError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/validation"
"github.com/xinliangnote/go-gin-api/internal/services/cron" "github.com/xinliangnote/go-gin-api/internal/services/cron"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createRequest struct { type createRequest struct {
@@ -35,7 +34,7 @@ type createResponse struct {
// @Summary 创建任务 // @Summary 创建任务
// @Description 创建任务 // @Description 创建任务
// @Tags API.cron // @Tags API.cron
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param name formData string true "任务名称" // @Param name formData string true "任务名称"
// @Param spec formData string true "crontab 表达式" // @Param spec formData string true "crontab 表达式"
@@ -54,15 +53,16 @@ type createResponse struct {
// @Success 200 {object} createResponse // @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron [post] // @Router /api/cron [post]
// @Security LoginToken
func (h *handler) Create() core.HandlerFunc { func (h *handler) Create() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(createRequest) req := new(createRequest)
res := new(createResponse) res := new(createResponse)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
@@ -85,10 +85,10 @@ func (h *handler) Create() core.HandlerFunc {
id, err := h.cronService.Create(ctx, createData) id, err := h.cronService.Create(ctx, createData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronCreateError, code.CronCreateError,
code.Text(code.CronCreateError)).WithErr(err), code.Text(code.CronCreateError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/validation"
"github.com/xinliangnote/go-gin-api/internal/services/cron" "github.com/xinliangnote/go-gin-api/internal/services/cron"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -42,26 +41,27 @@ type detailResponse struct {
// @Param id path string true "hashId" // @Param id path string true "hashId"
// @Success 200 {object} detailResponse // @Success 200 {object} detailResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron/:id [get] // @Router /api/cron/{id} [get]
// @Security LoginToken
func (h *handler) Detail() core.HandlerFunc { func (h *handler) Detail() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(detailRequest) req := new(detailRequest)
res := new(detailResponse) res := new(detailResponse)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -71,10 +71,10 @@ func (h *handler) Detail() core.HandlerFunc {
info, err := h.cronService.Detail(ctx, searchOneData) info, err := h.cronService.Detail(ctx, searchOneData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronDetailError, code.CronDetailError,
code.Text(code.CronDetailError)).WithErr(err), code.Text(code.CronDetailError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/pkg/validation"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -28,36 +27,37 @@ type executeResponse struct {
// @Param id path string true "hashId" // @Param id path string true "hashId"
// @Success 200 {object} detailResponse // @Success 200 {object} detailResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron/:id [patch] // @Router /api/cron/{id} [patch]
// @Security LoginToken
func (h *handler) Execute() core.HandlerFunc { func (h *handler) Execute() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(executeRequest) req := new(executeRequest)
res := new(executeResponse) res := new(executeResponse)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
err = h.cronService.Execute(ctx, cast.ToInt32(ids[0])) err = h.cronService.Execute(ctx, cast.ToInt32(ids[0]))
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronExecuteError, code.CronExecuteError,
code.Text(code.CronExecuteError)).WithErr(err), code.Text(code.CronExecuteError)).WithError(err),
) )
return return
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/validation" "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/repository/mysql/cron_task"
"github.com/xinliangnote/go-gin-api/internal/services/cron" "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/xinliangnote/go-gin-api/pkg/timeutil"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -58,25 +57,26 @@ type listResponse struct {
// @Summary 任务列表 // @Summary 任务列表
// @Description 任务列表 // @Description 任务列表
// @Tags API.cron // @Tags API.cron
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param page query int false "第几页" // @Param page query int true "第几页" default(1)
// @Param page_size query string false "每页显示条数" // @Param page_size query int true "每页显示条数" default(10)
// @Param name query string false "任务名称" // @Param name query string false "任务名称"
// @Param protocol query int false "执行方式 1:shell 2:http" // @Param protocol query int false "执行方式 1:shell 2:http"
// @Param is_used query int false "是否启用 1:是 -1:否" // @Param is_used query int false "是否启用 1:是 -1:否"
// @Success 200 {object} listResponse // @Success 200 {object} listResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron [get] // @Router /api/cron [get]
// @Security LoginToken
func (h *handler) List() core.HandlerFunc { func (h *handler) List() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(listRequest) req := new(listRequest)
res := new(listResponse) res := new(listResponse)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
@@ -100,20 +100,20 @@ func (h *handler) List() core.HandlerFunc {
resListData, err := h.cronService.PageList(ctx, searchData) resListData, err := h.cronService.PageList(ctx, searchData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronListError, code.CronListError,
code.Text(code.CronListError)).WithErr(err), code.Text(code.CronListError)).WithError(err),
) )
return return
} }
resCountData, err := h.cronService.PageListCount(ctx, searchData) resCountData, err := h.cronService.PageListCount(ctx, searchData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronListError, code.CronListError,
code.Text(code.CronListError)).WithErr(err), code.Text(code.CronListError)).WithError(err),
) )
return return
} }
@@ -126,10 +126,10 @@ func (h *handler) List() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/validation"
"github.com/xinliangnote/go-gin-api/internal/services/cron" "github.com/xinliangnote/go-gin-api/internal/services/cron"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type modifyRequest struct { type modifyRequest struct {
@@ -36,9 +35,9 @@ type modifyResponse struct {
// @Summary 编辑任务 // @Summary 编辑任务
// @Description 编辑任务 // @Description 编辑任务
// @Tags API.cron // @Tags API.cron
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "hashID"
// @Param name formData string true "任务名称" // @Param name formData string true "任务名称"
// @Param spec formData string true "crontab 表达式" // @Param spec formData string true "crontab 表达式"
// @Param command formData string true "执行命令" // @Param command formData string true "执行命令"
@@ -56,25 +55,26 @@ type modifyResponse struct {
// @Success 200 {object} modifyResponse // @Success 200 {object} modifyResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron/{id} [post] // @Router /api/cron/{id} [post]
// @Security LoginToken
func (h *handler) Modify() core.HandlerFunc { func (h *handler) Modify() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(modifyRequest) req := new(modifyRequest)
res := new(modifyResponse) res := new(modifyResponse)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -98,10 +98,10 @@ func (h *handler) Modify() core.HandlerFunc {
modifyData.IsUsed = req.IsUsed modifyData.IsUsed = req.IsUsed
if err := h.cronService.Modify(ctx, id, modifyData); err != nil { if err := h.cronService.Modify(ctx, id, modifyData); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CronUpdateError, code.CronUpdateError,
code.Text(code.CronUpdateError)).WithErr(err), code.Text(code.CronUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/pkg/validation" "github.com/xinliangnote/go-gin-api/internal/pkg/validation"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type updateUsedRequest struct { type updateUsedRequest struct {
@@ -22,32 +21,33 @@ type updateUsedResponse struct {
// @Summary 更新任务为启用/禁用 // @Summary 更新任务为启用/禁用
// @Description 更新任务为启用/禁用 // @Description 更新任务为启用/禁用
// @Tags API.cron // @Tags API.cron
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "hashID"
// @Param used formData int true "是否启用 1:是 -1:否" // @Param used formData int true "是否启用 1:是 -1:否"
// @Success 200 {object} updateUsedResponse // @Success 200 {object} updateUsedResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/cron/used [patch] // @Router /api/cron/used [patch]
// @Security LoginToken
func (h *handler) UpdateUsed() core.HandlerFunc { func (h *handler) UpdateUsed() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(updateUsedRequest) req := new(updateUsedRequest)
res := new(updateUsedResponse) res := new(updateUsedResponse)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -56,10 +56,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
err = h.cronService.UpdateUsed(ctx, id, req.Used) err = h.cronService.UpdateUsed(ctx, id, req.Used)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AdminUpdateError, code.AdminUpdateError,
code.Text(code.AdminUpdateError)).WithErr(err), code.Text(code.AdminUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -39,12 +39,12 @@ type Handler interface {
// Detail 获取单条任务详情 // Detail 获取单条任务详情
// @Tags API.cron // @Tags API.cron
// @Router /api/cron/:id [get] // @Router /api/cron/{id} [get]
Detail() core.HandlerFunc Detail() core.HandlerFunc
// Execute 手动执行任务 // Execute 手动执行任务
// @Tags API.cron // @Tags API.cron
// @Router /api/cron/:id [patch] // @Router /api/cron/{id} [patch]
Execute() core.HandlerFunc Execute() core.HandlerFunc
} }

49
internal/api/helper/func_md5.go Executable file
View File

@@ -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)
}
}

100
internal/api/helper/func_sign.go Executable file
View File

@@ -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)
}
}

View File

@@ -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() {}

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/menu" "github.com/xinliangnote/go-gin-api/internal/services/menu"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -28,21 +27,22 @@ type createResponse struct {
// @Summary 创建/编辑菜单 // @Summary 创建/编辑菜单
// @Description 创建/编辑菜单 // @Description 创建/编辑菜单
// @Tags API.menu // @Tags API.menu
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param Request body createRequest true "请求信息" // @Param Request body createRequest true "请求信息"
// @Success 200 {object} createResponse // @Success 200 {object} createResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu [post] // @Router /api/menu [post]
// @Security LoginToken
func (h *handler) Create() core.HandlerFunc { func (h *handler) Create() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createRequest) req := new(createRequest)
res := new(createResponse) res := new(createResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -50,10 +50,10 @@ func (h *handler) Create() core.HandlerFunc {
if req.Id != "" { // 编辑功能 if req.Id != "" { // 编辑功能
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -67,10 +67,10 @@ func (h *handler) Create() core.HandlerFunc {
err = h.menuService.Modify(c, id, updateData) err = h.menuService.Modify(c, id, updateData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuUpdateError, code.MenuUpdateError,
code.Text(code.MenuUpdateError)).WithErr(err), code.Text(code.MenuUpdateError)).WithError(err),
) )
return return
} }
@@ -97,10 +97,10 @@ func (h *handler) Create() core.HandlerFunc {
id, err := h.menuService.Create(c, createData) id, err := h.menuService.Create(c, createData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuCreateError, code.MenuCreateError,
code.Text(code.MenuCreateError)).WithErr(err), code.Text(code.MenuCreateError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/menu" "github.com/xinliangnote/go-gin-api/internal/services/menu"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type createActionRequest struct { type createActionRequest struct {
@@ -23,7 +22,7 @@ type createActionResponse struct {
// @Summary 创建功能权限 // @Summary 创建功能权限
// @Description 创建功能权限 // @Description 创建功能权限
// @Tags API.menu // @Tags API.menu
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "HashID" // @Param id formData string true "HashID"
// @Param method formData string true "请求方法" // @Param method formData string true "请求方法"
@@ -31,25 +30,26 @@ type createActionResponse struct {
// @Success 200 {object} createActionResponse // @Success 200 {object} createActionResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu_action [post] // @Router /api/menu_action [post]
// @Security LoginToken
func (h *handler) CreateAction() core.HandlerFunc { func (h *handler) CreateAction() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(createActionRequest) req := new(createActionRequest)
res := new(createActionResponse) res := new(createActionResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -60,10 +60,10 @@ func (h *handler) CreateAction() core.HandlerFunc {
searchOneData.Id = id searchOneData.Id = id
menuInfo, err := h.menuService.Detail(c, searchOneData) menuInfo, err := h.menuService.Detail(c, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuDetailError, code.MenuDetailError,
code.Text(code.MenuDetailError)).WithErr(err), code.Text(code.MenuDetailError)).WithError(err),
) )
return return
} }
@@ -75,10 +75,10 @@ func (h *handler) CreateAction() core.HandlerFunc {
createId, err := h.menuService.CreateAction(c, createActionData) createId, err := h.menuService.CreateAction(c, createActionData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuCreateActionError, code.MenuCreateActionError,
code.Text(code.MenuCreateActionError)).WithErr(err), code.Text(code.MenuCreateActionError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type deleteRequest struct { type deleteRequest struct {
@@ -26,25 +25,26 @@ type deleteResponse struct {
// @Success 200 {object} deleteResponse // @Success 200 {object} deleteResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu/{id} [delete] // @Router /api/menu/{id} [delete]
// @Security LoginToken
func (h *handler) Delete() core.HandlerFunc { func (h *handler) Delete() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(deleteRequest) req := new(deleteRequest)
res := new(deleteResponse) res := new(deleteResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) Delete() core.HandlerFunc {
err = h.menuService.Delete(c, id) err = h.menuService.Delete(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuDeleteError, code.MenuDeleteError,
code.Text(code.MenuDeleteError)).WithErr(err), code.Text(code.MenuDeleteError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type deleteActionRequest struct { type deleteActionRequest struct {
@@ -26,25 +25,26 @@ type deleteActionResponse struct {
// @Success 200 {object} deleteActionResponse // @Success 200 {object} deleteActionResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu_action/{id} [delete] // @Router /api/menu_action/{id} [delete]
// @Security LoginToken
func (h *handler) DeleteAction() core.HandlerFunc { func (h *handler) DeleteAction() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(deleteActionRequest) req := new(deleteActionRequest)
res := new(deleteActionResponse) res := new(deleteActionResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) DeleteAction() core.HandlerFunc {
err = h.menuService.DeleteAction(c, id) err = h.menuService.DeleteAction(c, id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuDeleteActionError, code.MenuDeleteActionError,
code.Text(code.MenuDeleteActionError)).WithErr(err), code.Text(code.MenuDeleteActionError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/menu" "github.com/xinliangnote/go-gin-api/internal/services/menu"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type detailRequest struct { type detailRequest struct {
@@ -25,31 +24,32 @@ type detailResponse struct {
// @Summary 菜单详情 // @Summary 菜单详情
// @Description 菜单详情 // @Description 菜单详情
// @Tags API.menu // @Tags API.menu
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id path string true "hashId" // @Param id path string true "hashId"
// @Success 200 {object} detailResponse // @Success 200 {object} detailResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu/{id} [get] // @Router /api/menu/{id} [get]
// @Security LoginToken
func (h *handler) Detail() core.HandlerFunc { func (h *handler) Detail() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(detailRequest) req := new(detailRequest)
res := new(detailResponse) res := new(detailResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -61,10 +61,10 @@ func (h *handler) Detail() core.HandlerFunc {
info, err := h.menuService.Detail(c, searchOneData) info, err := h.menuService.Detail(c, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuDetailError, code.MenuDetailError,
code.Text(code.MenuDetailError)).WithErr(err), code.Text(code.MenuDetailError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/menu" "github.com/xinliangnote/go-gin-api/internal/services/menu"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -30,20 +29,21 @@ type listResponse struct {
// @Summary 菜单列表 // @Summary 菜单列表
// @Description 菜单列表 // @Description 菜单列表
// @Tags API.menu // @Tags API.menu
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Success 200 {object} listResponse // @Success 200 {object} listResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu [get] // @Router /api/menu [get]
// @Security LoginToken
func (h *handler) List() core.HandlerFunc { func (h *handler) List() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
res := new(listResponse) res := new(listResponse)
resListData, err := h.menuService.List(c, new(menu.SearchData)) resListData, err := h.menuService.List(c, new(menu.SearchData))
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuListError, code.MenuListError,
code.Text(code.MenuListError)).WithErr(err), code.Text(code.MenuListError)).WithError(err),
) )
return return
} }
@@ -53,10 +53,10 @@ func (h *handler) List() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/services/menu" "github.com/xinliangnote/go-gin-api/internal/services/menu"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -31,31 +30,32 @@ type listActionResponse struct {
// @Summary 功能权限列表 // @Summary 功能权限列表
// @Description 功能权限列表 // @Description 功能权限列表
// @Tags API.menu // @Tags API.menu
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id query string true "hashID" // @Param id query string true "hashID"
// @Success 200 {object} listActionResponse // @Success 200 {object} listActionResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu_action [get] // @Router /api/menu_action [get]
// @Security LoginToken
func (h *handler) ListAction() core.HandlerFunc { func (h *handler) ListAction() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(listActionRequest) req := new(listActionRequest)
res := new(listActionResponse) res := new(listActionResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -67,10 +67,10 @@ func (h *handler) ListAction() core.HandlerFunc {
menuInfo, err := h.menuService.Detail(c, searchOneData) menuInfo, err := h.menuService.Detail(c, searchOneData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuDetailError, code.MenuDetailError,
code.Text(code.MenuDetailError)).WithErr(err), code.Text(code.MenuDetailError)).WithError(err),
) )
return return
} }
@@ -82,10 +82,10 @@ func (h *handler) ListAction() core.HandlerFunc {
resListData, err := h.menuService.ListAction(c, searchListData) resListData, err := h.menuService.ListAction(c, searchListData)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizedListAPIError, code.AuthorizedListAPIError,
code.Text(code.AuthorizedListAPIError)).WithErr(err), code.Text(code.AuthorizedListAPIError)).WithError(err),
) )
return return
} }
@@ -95,10 +95,10 @@ func (h *handler) ListAction() core.HandlerFunc {
for k, v := range resListData { for k, v := range resListData {
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(v.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type updateSortRequest struct { type updateSortRequest struct {
@@ -21,32 +20,33 @@ type updateSortResponse struct {
// @Summary 更新菜单排序 // @Summary 更新菜单排序
// @Description 更新菜单排序 // @Description 更新菜单排序
// @Tags API.menu // @Tags API.menu
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "hashId"
// @Param sort formData int true "排序" // @Param sort formData int true "排序"
// @Success 200 {object} updateSortResponse // @Success 200 {object} updateSortResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu/sort [patch] // @Router /api/menu/sort [patch]
// @Security LoginToken
func (h *handler) UpdateSort() core.HandlerFunc { func (h *handler) UpdateSort() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(updateSortRequest) req := new(updateSortRequest)
res := new(updateSortResponse) res := new(updateSortResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -55,10 +55,10 @@ func (h *handler) UpdateSort() core.HandlerFunc {
err = h.menuService.UpdateSort(c, id, req.Sort) err = h.menuService.UpdateSort(c, id, req.Sort)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuUpdateError, code.MenuUpdateError,
code.Text(code.MenuUpdateError)).WithErr(err), code.Text(code.MenuUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type updateUsedRequest struct { type updateUsedRequest struct {
@@ -21,32 +20,33 @@ type updateUsedResponse struct {
// @Summary 更新菜单为启用/禁用 // @Summary 更新菜单为启用/禁用
// @Description 更新菜单为启用/禁用 // @Description 更新菜单为启用/禁用
// @Tags API.menu // @Tags API.menu
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id formData string true "Hashid" // @Param id formData string true "hashId"
// @Param used formData int true "是否启用 1:是 -1:否" // @Param used formData int true "是否启用 1:是 -1:否"
// @Success 200 {object} updateUsedResponse // @Success 200 {object} updateUsedResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/menu/used [patch] // @Router /api/menu/used [patch]
// @Security LoginToken
func (h *handler) UpdateUsed() core.HandlerFunc { func (h *handler) UpdateUsed() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(updateUsedRequest) req := new(updateUsedRequest)
res := new(updateUsedResponse) res := new(updateUsedResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
ids, err := h.hashids.HashidsDecode(req.Id) ids, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }
@@ -55,10 +55,10 @@ func (h *handler) UpdateUsed() core.HandlerFunc {
err = h.menuService.UpdateUsed(c, id, req.Used) err = h.menuService.UpdateUsed(c, id, req.Used)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MenuUpdateError, code.MenuUpdateError,
code.Text(code.MenuUpdateError)).WithErr(err), code.Text(code.MenuUpdateError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type clearCacheRequest struct { type clearCacheRequest struct {
@@ -21,27 +20,28 @@ type clearCacheResponse struct {
// @Summary 清空缓存 // @Summary 清空缓存
// @Description 清空缓存 // @Description 清空缓存
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param redis_key formData string true "Redis Key" // @Param redis_key formData string true "Redis Key"
// @Success 200 {object} searchCacheResponse // @Success 200 {object} searchCacheResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/cache/clear [patch] // @Router /api/tool/cache/clear [patch]
// @Security LoginToken
func (h *handler) ClearCache() core.HandlerFunc { func (h *handler) ClearCache() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(clearCacheRequest) req := new(clearCacheRequest)
res := new(clearCacheResponse) res := new(clearCacheResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
if b := h.cache.Exists(req.RedisKey); b != true { if b := h.cache.Exists(req.RedisKey); b != true {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CacheNotExist, code.CacheNotExist,
code.Text(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())) b := h.cache.Del(req.RedisKey, redis.WithTrace(c.Trace()))
if b != true { if b != true {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CacheDelError, code.CacheDelError,
code.Text(code.CacheDelError)), code.Text(code.CacheDelError)),

View File

@@ -17,11 +17,12 @@ type dbData struct {
// @Summary 查询 DB // @Summary 查询 DB
// @Description 查询 DB // @Description 查询 DB
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Success 200 {object} dbsResponse // @Success 200 {object} dbsResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/data/dbs [get] // @Router /api/tool/data/dbs [get]
// @Security LoginToken
func (h *handler) Dbs() core.HandlerFunc { func (h *handler) Dbs() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
res := new(dbsResponse) res := new(dbsResponse)

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type hashIdsDecodeRequest struct { type hashIdsDecodeRequest struct {
@@ -20,31 +19,32 @@ type hashIdsDecodeResponse struct {
// @Summary HashIds 解密 // @Summary HashIds 解密
// @Description HashIds 解密 // @Description HashIds 解密
// @Tags API.tool // @Tags API.tool
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id path string true "需解密的密文" // @Param id path string true "需解密的密文"
// @Success 200 {object} hashIdsDecodeResponse // @Success 200 {object} hashIdsDecodeResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/hashids/decode/{id} [get] // @Router /api/tool/hashids/decode/{id} [get]
// @Security LoginToken
func (h *handler) HashIdsDecode() core.HandlerFunc { func (h *handler) HashIdsDecode() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(hashIdsDecodeRequest) req := new(hashIdsDecodeRequest)
res := new(hashIdsDecodeResponse) res := new(hashIdsDecodeResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
hashId, err := h.hashids.HashidsDecode(req.Id) hashId, err := h.hashids.HashidsDecode(req.Id)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsDecodeError, code.HashIdsDecodeError,
code.Text(code.HashIdsDecodeError)).WithErr(err), code.Text(code.HashIdsDecodeError)).WithError(err),
) )
return return
} }

View File

@@ -5,7 +5,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -22,31 +21,32 @@ type hashIdsEncodeResponse struct {
// @Summary HashIds 加密 // @Summary HashIds 加密
// @Description HashIds 加密 // @Description HashIds 加密
// @Tags API.tool // @Tags API.tool
// @Accept json // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param id path string true "需加密的数字" // @Param id path string true "需加密的数字"
// @Success 200 {object} hashIdsEncodeResponse // @Success 200 {object} hashIdsEncodeResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/hashids/encode/{id} [get] // @Router /api/tool/hashids/encode/{id} [get]
// @Security LoginToken
func (h *handler) HashIdsEncode() core.HandlerFunc { func (h *handler) HashIdsEncode() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(hashIdsEncodeRequest) req := new(hashIdsEncodeRequest)
res := new(hashIdsEncodeResponse) res := new(hashIdsEncodeResponse)
if err := c.ShouldBindURI(req); err != nil { if err := c.ShouldBindURI(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(req.Id)}) hashId, err := h.hashids.HashidsEncode([]int{cast.ToInt(req.Id)})
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.HashIdsEncodeError, code.HashIdsEncodeError,
code.Text(code.HashIdsEncodeError)).WithErr(err), code.Text(code.HashIdsEncodeError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type searchCacheRequest struct { type searchCacheRequest struct {
@@ -22,27 +21,28 @@ type searchCacheResponse struct {
// @Summary 查询缓存 // @Summary 查询缓存
// @Description 查询缓存 // @Description 查询缓存
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param redis_key formData string true "Redis Key" // @Param redis_key formData string true "Redis Key"
// @Success 200 {object} searchCacheResponse // @Success 200 {object} searchCacheResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/cache/search [post] // @Router /api/tool/cache/search [post]
// @Security LoginToken
func (h *handler) SearchCache() core.HandlerFunc { func (h *handler) SearchCache() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(searchCacheRequest) req := new(searchCacheRequest)
res := new(searchCacheResponse) res := new(searchCacheResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
if b := h.cache.Exists(req.RedisKey); b != true { if b := h.cache.Exists(req.RedisKey); b != true {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CacheNotExist, code.CacheNotExist,
code.Text(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())) val, err := h.cache.Get(req.RedisKey, redis.WithTrace(c.Trace()))
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CacheGetError, code.CacheGetError,
code.Text(code.CacheGetError)).WithErr(err), code.Text(code.CacheGetError)).WithError(err),
) )
return return
} }
ttl, err := h.cache.TTL(req.RedisKey) ttl, err := h.cache.TTL(req.RedisKey)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.CacheGetError, code.CacheGetError,
code.Text(code.CacheGetError)).WithErr(err), code.Text(code.CacheGetError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"github.com/spf13/cast" "github.com/spf13/cast"
) )
@@ -72,7 +71,7 @@ var filterListKeyword = []string{
// @Summary 执行 SQL 语句 // @Summary 执行 SQL 语句
// @Description 执行 SQL 语句 // @Description 执行 SQL 语句
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param db_name formData string true "数据库名称" // @Param db_name formData string true "数据库名称"
// @Param table_name formData string true "数据表名称" // @Param table_name formData string true "数据表名称"
@@ -80,22 +79,23 @@ var filterListKeyword = []string{
// @Success 200 {object} searchMySQLResponse // @Success 200 {object} searchMySQLResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/data/mysql [post] // @Router /api/tool/data/mysql [post]
// @Security LoginToken
func (h *handler) SearchMySQL() core.HandlerFunc { func (h *handler) SearchMySQL() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(searchMySQLRequest) req := new(searchMySQLRequest)
res := new(searchMySQLResponse) res := new(searchMySQLResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
sql := strings.ToLower(strings.TrimSpace(req.SQL)) sql := strings.ToLower(strings.TrimSpace(req.SQL))
if sql == "" { if sql == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
"SQL 语句不能为空!"), "SQL 语句不能为空!"),
@@ -104,7 +104,7 @@ func (h *handler) SearchMySQL() core.HandlerFunc {
} }
if preFilterList[string([]byte(sql)[:6])] { if preFilterList[string([]byte(sql)[:6])] {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
"SQL 语句不能以 "+string([]byte(sql)[:6])+" 开头!"), "SQL 语句不能以 "+string([]byte(sql)[:6])+" 开头!"),
@@ -124,7 +124,7 @@ func (h *handler) SearchMySQL() core.HandlerFunc {
} }
if !isWhiteList { if !isWhiteList {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
"SQL 语句存在敏感词: "+f+""), "SQL 语句存在敏感词: "+f+""),
@@ -142,10 +142,10 @@ func (h *handler) SearchMySQL() core.HandlerFunc {
// TODO 后期支持查询多个数据库 // TODO 后期支持查询多个数据库
rows, err := h.db.GetDbR().Raw(sql).Rows() rows, err := h.db.GetDbR().Raw(sql).Rows()
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
"MySQL "+err.Error()).WithErr(err), "MySQL "+err.Error()).WithError(err),
) )
return return
} }
@@ -191,10 +191,10 @@ func (h *handler) SearchMySQL() core.HandlerFunc {
rows, err = h.db.GetDbR().Raw(sqlTableColumn).Rows() rows, err = h.db.GetDbR().Raw(sqlTableColumn).Rows()
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
"MySQL "+err.Error()).WithErr(err), "MySQL "+err.Error()).WithError(err),
) )
return return
} }

View File

@@ -8,7 +8,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/pkg/validation"
"github.com/xinliangnote/go-gin-api/internal/websocket/sysmessage" "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" "github.com/xinliangnote/go-gin-api/pkg/timeutil"
) )
@@ -24,12 +23,13 @@ type sendMessageResponse struct {
// @Summary 发送消息 // @Summary 发送消息
// @Description 发送消息 // @Description 发送消息
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param message formData string true "消息内容" // @Param message formData string true "消息内容"
// @Success 200 {object} sendMessageResponse // @Success 200 {object} sendMessageResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/send_message [post] // @Router /api/tool/send_message [post]
// @Security LoginToken
func (h *handler) SendMessage() core.HandlerFunc { func (h *handler) SendMessage() core.HandlerFunc {
type messageBody struct { type messageBody struct {
Username string `json:"username"` Username string `json:"username"`
@@ -41,45 +41,45 @@ func (h *handler) SendMessage() core.HandlerFunc {
req := new(sendMessageRequest) req := new(sendMessageRequest)
res := new(sendMessageResponse) res := new(sendMessageResponse)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
validation.Error(err)).WithErr(err), validation.Error(err)).WithError(err),
) )
return return
} }
conn, err := sysmessage.GetConn() conn, err := sysmessage.GetConn()
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.SocketConnectError, code.SocketConnectError,
code.Text(code.SocketConnectError)).WithErr(err), code.Text(code.SocketConnectError)).WithError(err),
) )
return return
} }
messageData := new(messageBody) messageData := new(messageBody)
messageData.Username = ctx.UserName() messageData.Username = ctx.SessionUserInfo().UserName
messageData.Message = req.Message messageData.Message = req.Message
messageData.Time = timeutil.CSTLayoutString() messageData.Time = timeutil.CSTLayoutString()
messageJsonData, err := json.Marshal(messageData) messageJsonData, err := json.Marshal(messageData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.SocketSendError, code.SocketSendError,
code.Text(code.SocketSendError)).WithErr(err), code.Text(code.SocketSendError)).WithError(err),
) )
return return
} }
err = conn.OnSend(messageJsonData) err = conn.OnSend(messageJsonData)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.SocketSendError, code.SocketSendError,
code.Text(code.SocketSendError)).WithErr(err), code.Text(code.SocketSendError)).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/pkg/errno"
) )
type tablesRequest struct { type tablesRequest struct {
@@ -26,21 +25,22 @@ type tableData struct {
// @Summary 查询 Table // @Summary 查询 Table
// @Description 查询 Table // @Description 查询 Table
// @Tags API.tool // @Tags API.tool
// @Accept multipart/form-data // @Accept application/x-www-form-urlencoded
// @Produce json // @Produce json
// @Param db_name formData string true "数据库名称" // @Param db_name formData string true "数据库名称"
// @Success 200 {object} tablesResponse // @Success 200 {object} tablesResponse
// @Failure 400 {object} code.Failure // @Failure 400 {object} code.Failure
// @Router /api/tool/data/tables [post] // @Router /api/tool/data/tables [post]
// @Security LoginToken
func (h *handler) Tables() core.HandlerFunc { func (h *handler) Tables() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
req := new(tablesRequest) req := new(tablesRequest)
res := new(tablesResponse) res := new(tablesResponse)
if err := c.ShouldBindForm(req); err != nil { if err := c.ShouldBindForm(req); err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -50,10 +50,10 @@ func (h *handler) Tables() core.HandlerFunc {
// TODO 后期支持查询多个数据库 // TODO 后期支持查询多个数据库
rows, err := h.db.GetDbR().Raw(sqlTables).Rows() rows, err := h.db.GetDbR().Raw(sqlTables).Rows()
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)).WithErr(err), code.Text(code.MySQLExecError)).WithError(err),
) )
return return
} }

View File

@@ -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,
)
}
}

View File

@@ -37,15 +37,15 @@ func init() {
} }
// RecordMetrics 记录指标 // 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{ metricsRequestsTotal.With(prometheus.Labels{
"method": method, "method": method,
"path": uri, "path": path,
}).Inc() }).Inc()
metricsRequestsCost.With(prometheus.Labels{ metricsRequestsCost.With(prometheus.Labels{
"method": method, "method": method,
"path": uri, "path": path,
"success": cast.ToString(success), "success": cast.ToString(success),
"http_code": cast.ToString(httpCode), "http_code": cast.ToString(httpCode),
"business_code": cast.ToString(businessCode), "business_code": cast.ToString(businessCode),

View File

@@ -9,7 +9,7 @@ import (
"strings" "strings"
"sync" "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/xinliangnote/go-gin-api/pkg/trace"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -28,9 +28,9 @@ const (
_BodyName = "_body_" _BodyName = "_body_"
_PayloadName = "_payload_" _PayloadName = "_payload_"
_GraphPayloadName = "_graph_payload_" _GraphPayloadName = "_graph_payload_"
_UserID = "_user_id_" _SessionUserInfo = "_session_user_info"
_UserName = "_user_name_"
_AbortErrorName = "_abort_error_" _AbortErrorName = "_abort_error_"
_IsRecordMetrics = "_is_record_metrics_"
) )
var contextPool = &sync.Pool{ var contextPool = &sync.Pool{
@@ -101,8 +101,8 @@ type Context interface {
HTML(name string, obj interface{}) HTML(name string, obj interface{})
// AbortWithError 错误返回 // AbortWithError 错误返回
AbortWithError(err errno.Error) AbortWithError(err BusinessError)
abortError() errno.Error abortError() BusinessError
// Header 获取 Header 对象 // Header 获取 Header 对象
Header() http.Header Header() http.Header
@@ -111,18 +111,19 @@ type Context interface {
// SetHeader 设置 Header // SetHeader 设置 Header
SetHeader(key, value string) SetHeader(key, value string)
// UserID 获取 UserID // SessionUserInfo 当前用户信息
UserID() int64 SessionUserInfo() proposal.SessionUserInfo
setUserID(userID int64) setSessionUserInfo(info proposal.SessionUserInfo)
// UserName 获取 UserName // Alias 设置路由别名 for metrics path
UserName() string
setUserName(userName string)
// Alias 设置路由别名 for metrics uri
Alias() string Alias() string
setAlias(path string) setAlias(path string)
// disableRecordMetrics 设置禁止记录指标
disableRecordMetrics()
ableRecordMetrics()
isRecordMetrics() bool
// RequestInputParams 获取所有参数 // RequestInputParams 获取所有参数
RequestInputParams() url.Values RequestInputParams() url.Values
// RequestPostFormParams 获取 PostForm 参数 // RequestPostFormParams 获取 PostForm 参数
@@ -279,35 +280,22 @@ func (c *context) SetHeader(key, value string) {
c.ctx.Header(key, value) c.ctx.Header(key, value)
} }
func (c *context) UserID() int64 { func (c *context) SessionUserInfo() proposal.SessionUserInfo {
val, ok := c.ctx.Get(_UserID) val, ok := c.ctx.Get(_SessionUserInfo)
if !ok { if !ok {
return 0 return proposal.SessionUserInfo{}
} }
return val.(int64) return val.(proposal.SessionUserInfo)
} }
func (c *context) setUserID(userID int64) { func (c *context) setSessionUserInfo(info proposal.SessionUserInfo) {
c.ctx.Set(_UserID, userID) c.ctx.Set(_SessionUserInfo, info)
} }
func (c *context) UserName() string { func (c *context) AbortWithError(err BusinessError) {
val, ok := c.ctx.Get(_UserName)
if !ok {
return ""
}
return val.(string)
}
func (c *context) setUserName(userName string) {
c.ctx.Set(_UserName, userName)
}
func (c *context) AbortWithError(err errno.Error) {
if err != nil { if err != nil {
httpCode := err.GetHttpCode() httpCode := err.HTTPCode()
if httpCode == 0 { if httpCode == 0 {
httpCode = http.StatusInternalServerError 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) err, _ := c.ctx.Get(_AbortErrorName)
return err.(errno.Error) return err.(BusinessError)
} }
func (c *context) Alias() string { 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 获取所有参数 // RequestInputParams 获取所有参数
func (c *context) RequestInputParams() url.Values { func (c *context) RequestInputParams() url.Values {
_ = c.ctx.Request.ParseForm() _ = c.ctx.Request.ParseForm()

View File

@@ -12,10 +12,10 @@ import (
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
_ "github.com/xinliangnote/go-gin-api/docs" _ "github.com/xinliangnote/go-gin-api/docs"
"github.com/xinliangnote/go-gin-api/internal/code" "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/browser"
"github.com/xinliangnote/go-gin-api/pkg/color" "github.com/xinliangnote/go-gin-api/pkg/color"
"github.com/xinliangnote/go-gin-api/pkg/env" "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/errors"
"github.com/xinliangnote/go-gin-api/pkg/trace" "github.com/xinliangnote/go-gin-api/pkg/trace"
@@ -46,20 +46,13 @@ type option struct {
disablePProf bool disablePProf bool
disableSwagger bool disableSwagger bool
disablePrometheus bool disablePrometheus bool
panicNotify OnPanicNotify
recordMetrics RecordMetrics
enableCors bool enableCors bool
enableRate bool enableRate bool
enableOpenBrowser string 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 // WithDisablePProf 禁用 pprof
func WithDisablePProf() Option { func WithDisablePProf() Option {
return func(opt *option) { return func(opt *option) {
@@ -81,17 +74,17 @@ func WithDisablePrometheus() Option {
} }
} }
// WithPanicNotify 设置panic时的通知回调 // WithAlertNotify 设置告警通知
func WithPanicNotify(notify OnPanicNotify) Option { func WithAlertNotify(notifyHandler proposal.NotifyHandler) Option {
return func(opt *option) { return func(opt *option) {
opt.panicNotify = notify opt.alertNotify = notifyHandler
} }
} }
// WithRecordMetrics 设置记录prometheus记录指标回调 // WithRecordMetrics 设置记录接口指标
func WithRecordMetrics(record RecordMetrics) Option { func WithRecordMetrics(recordHandler proposal.RecordHandler) Option {
return func(opt *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 { func WithEnableCors() Option {
return func(opt *option) { return func(opt *option) {
opt.enableCors = true opt.enableCors = true
} }
} }
// WithEnableRate 设置支持限流
func WithEnableRate() Option { func WithEnableRate() Option {
return func(opt *option) { return func(opt *option) {
opt.enableRate = true opt.enableRate = true
} }
} }
func DisableTrace(ctx Context) { // DisableTraceLog 禁止记录日志
func DisableTraceLog(ctx Context) {
ctx.disableTrace() ctx.disableTrace()
} }
// AliasForRecordMetrics 对请求uri起个别名用于prometheus记录指标 // DisableRecordMetrics 禁止记录指标
// 如Get /user/:username 这样的uri因为username会有非常多的情况这样记录prometheus指标会非常的不有好。 func DisableRecordMetrics(ctx Context) {
ctx.disableRecordMetrics()
}
// AliasForRecordMetrics 对请求路径起个别名,用于记录指标。
// 如Get /user/:username 这样的路径,因为 username 会有非常多的情况,这样记录指标非常不友好。
func AliasForRecordMetrics(path string) HandlerFunc { func AliasForRecordMetrics(path string) HandlerFunc {
return func(ctx Context) { return func(ctx Context) {
ctx.setAlias(path) ctx.setAlias(path)
} }
} }
// WrapAuthHandler 用来处理 Auth 的入口在之后的handler中只需 ctx.UserID() ctx.UserName() 即可。 // WrapAuthHandler 用来处理 Auth 的入口
func WrapAuthHandler(handler func(Context) (userID int64, userName string, err errno.Error)) HandlerFunc { func WrapAuthHandler(handler func(Context) (sessionUserInfo proposal.SessionUserInfo, err BusinessError)) HandlerFunc {
return func(ctx Context) { return func(ctx Context) {
userID, userName, err := handler(ctx) sessionUserInfo, err := handler(ctx)
if err != nil { if err != nil {
ctx.AbortWithError(err) ctx.AbortWithError(err)
return 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) { mux.engine.Use(func(ctx *gin.Context) {
if ctx.Writer.Status() == http.StatusNotFound {
return
}
ts := time.Now() ts := time.Now()
context := newContext(ctx) context := newContext(ctx)
@@ -335,6 +340,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
context.init() context.init()
context.setLogger(logger) context.setLogger(logger)
context.ableRecordMetrics()
if !withoutTracePaths[ctx.Request.URL.Path] { if !withoutTracePaths[ctx.Request.URL.Path] {
if traceId := context.GetHeader(trace.Header); traceId != "" { if traceId := context.GetHeader(trace.Header); traceId != "" {
@@ -345,24 +351,6 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
} }
defer func() { 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 ( var (
response interface{} response interface{}
businessCode int businessCode int
@@ -372,57 +360,103 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
graphResponse interface{} 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() { if ctx.IsAborted() {
for i := range ctx.Errors { // gin error for i := range ctx.Errors {
multierr.AppendInto(&abortErr, ctx.Errors[i]) multierr.AppendInto(&abortErr, ctx.Errors[i])
} }
if err := context.abortError(); err != nil { // customer err if err := context.abortError(); err != nil { // customer err
multierr.AppendInto(&abortErr, err.GetErr()) // 判断是否需要发送告警通知
response = err if err.IsAlert() {
businessCode = err.GetBusinessCode() if notifyHandler := opt.alertNotify; notifyHandler != nil {
businessCodeMsg = err.GetMsg() notifyHandler(&proposal.AlertMessage{
ProjectName: configs.ProjectName,
if x := context.Trace(); x != nil { Env: env.Active().Value(),
context.SetHeader(trace.Header, x.ID()) TraceID: traceId,
traceId = x.ID() 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, Code: businessCode,
Message: businessCodeMsg, 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 { // region 记录指标
uri := context.URI() if opt.recordHandler != nil && context.isRecordMetrics() {
path := context.Path()
if alias := context.Alias(); alias != "" { if alias := context.Alias(); alias != "" {
uri = alias path = alias
} }
opt.recordMetrics( opt.recordHandler(&proposal.MetricsMessage{
context.Method(), ProjectName: configs.ProjectName,
uri, Env: env.Active().Value(),
!ctx.IsAborted() && ctx.Writer.Status() == http.StatusOK, TraceID: traceId,
ctx.Writer.Status(), HOST: context.Host(),
businessCode, Path: path,
time.Since(ts).Seconds(), Method: context.Method(),
traceId, 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 var t *trace.Trace
if x := context.Trace(); x != nil { if x := context.Trace(); x != nil {
t = x.(*trace.Trace) t = x.(*trace.Trace)
@@ -454,6 +488,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
responseBody = response responseBody = response
} }
graphResponse = context.getGraphPayload()
if graphResponse != nil { if graphResponse != nil {
responseBody = graphResponse responseBody = graphResponse
} }
@@ -468,10 +503,10 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
CostSeconds: time.Since(ts).Seconds(), 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() t.CostSeconds = time.Since(ts).Seconds()
logger.Info("core-interceptor", logger.Info("trace-log",
zap.Any("method", ctx.Request.Method), zap.Any("method", ctx.Request.Method),
zap.Any("path", decodedURL), zap.Any("path", decodedURL),
zap.Any("http_code", ctx.Writer.Status()), 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.Any("trace_info", t),
zap.Error(abortErr), zap.Error(abortErr),
) )
// endregion
}() }()
ctx.Next() ctx.Next()
@@ -494,7 +530,7 @@ func New(logger *zap.Logger, options ...Option) (Mux, error) {
defer releaseContext(context) defer releaseContext(context)
if !limiter.Allow() { if !limiter.Allow() {
context.AbortWithError(errno.NewError( context.AbortWithError(Error(
http.StatusTooManyRequests, http.StatusTooManyRequests,
code.TooManyRequests, code.TooManyRequests,
code.Text(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.NoMethod(wrapHandlers(DisableTraceLog)...)
mux.engine.NoRoute(wrapHandlers(DisableTrace)...) mux.engine.NoRoute(wrapHandlers(DisableTraceLog)...)
system := mux.Group("/system") system := mux.Group("/system")
{ {
// 健康检查 // 健康检查

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/mysql"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -62,10 +61,10 @@ func (h *handler) AdminMenu() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(adminMenuRequest) req := new(adminMenuRequest)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -89,10 +88,10 @@ func (h *handler) MenuAction() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(menuActionRequest) req := new(menuActionRequest)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/mysql"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -56,10 +55,10 @@ func (h *handler) Api() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(apiRequest) req := new(apiRequest)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }

View File

@@ -7,7 +7,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/mysql"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "github.com/xinliangnote/go-gin-api/internal/repository/redis"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap" "go.uber.org/zap"
) )
@@ -44,10 +43,10 @@ func (h *handler) Edit() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(editRequest) req := new(editRequest)
if err := ctx.ShouldBindURI(req); err != nil { if err := ctx.ShouldBindURI(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }

View File

@@ -10,7 +10,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/proposal/tablesqls" "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/go-redis/redis/v7"
"github.com/spf13/cast" "github.com/spf13/cast"
@@ -68,10 +67,10 @@ func (h *handler) Execute() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(initExecuteRequest) req := new(initExecuteRequest)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -80,7 +79,7 @@ func (h *handler) Execute() core.HandlerFunc {
versionStr := runtime.Version() versionStr := runtime.Version()
version := cast.ToFloat32(versionStr[2:6]) version := cast.ToFloat32(versionStr[2:6])
if version < configs.MinGoVersion { if version < configs.MinGoVersion {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.GoVersionError, code.GoVersionError,
code.Text(code.GoVersionError)), code.Text(code.GoVersionError)),
@@ -101,10 +100,10 @@ func (h *handler) Execute() core.HandlerFunc {
}) })
if err := redisClient.Ping().Err(); err != nil { if err := redisClient.Ping().Err(); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.RedisConnectError, code.RedisConnectError,
code.Text(code.RedisConnectError)).WithErr(err), code.Text(code.RedisConnectError)).WithError(err),
) )
return return
} }
@@ -131,10 +130,10 @@ func (h *handler) Execute() core.HandlerFunc {
}) })
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLConnectError, code.MySQLConnectError,
code.Text(code.MySQLConnectError)).WithErr(err), code.Text(code.MySQLConnectError)).WithError(err),
) )
return return
} }
@@ -165,10 +164,10 @@ func (h *handler) Execute() core.HandlerFunc {
viper.Set("mysql.write.name", req.MySQLName) viper.Set("mysql.write.name", req.MySQLName)
if viper.WriteConfig() != nil { if viper.WriteConfig() != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.WriteConfigError, code.WriteConfigError,
code.Text(code.WriteConfigError)).WithErr(err), code.Text(code.WriteConfigError)).WithError(err),
) )
return return
} }
@@ -182,10 +181,10 @@ func (h *handler) Execute() core.HandlerFunc {
if v["table_sql"] != "" { if v["table_sql"] != "" {
// region 初始化表结构 // region 初始化表结构
if err = db.Exec(v["table_sql"]).Error; err != nil { if err = db.Exec(v["table_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err),
) )
return return
} }
@@ -196,10 +195,10 @@ func (h *handler) Execute() core.HandlerFunc {
// region 初始化默认数据 // region 初始化默认数据
if v["table_data_sql"] != "" { if v["table_data_sql"] != "" {
if err = db.Exec(v["table_data_sql"]).Error; err != nil { if err = db.Exec(v["table_data_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err),
) )
return return
} }
@@ -214,10 +213,10 @@ func (h *handler) Execute() core.HandlerFunc {
// region 生成 install 完成标识 // region 生成 install 完成标识
f, err := os.Create(configs.ProjectInstallMark) f, err := os.Create(configs.ProjectInstallMark)
if err != nil { if err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err),
) )
return return
} }

View File

@@ -6,7 +6,6 @@ import (
"github.com/xinliangnote/go-gin-api/internal/code" "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/core"
"github.com/xinliangnote/go-gin-api/internal/proposal/tablesqls" "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" "github.com/xinliangnote/go-gin-api/pkg/errors"
) )
@@ -56,10 +55,10 @@ func (h *handler) UpgradeExecute() core.HandlerFunc {
return func(ctx core.Context) { return func(ctx core.Context) {
req := new(upgradeExecuteRequest) req := new(upgradeExecuteRequest)
if err := ctx.ShouldBindForm(req); err != nil { if err := ctx.ShouldBindForm(req); err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.ParamBindError, code.ParamBindError,
code.Text(code.ParamBindError)).WithErr(err), code.Text(code.ParamBindError)).WithError(err),
) )
return return
} }
@@ -68,29 +67,29 @@ func (h *handler) UpgradeExecute() core.HandlerFunc {
db := h.db.GetDbW() db := h.db.GetDbW()
if upgradeTableList[req.TableName] == nil { if upgradeTableList[req.TableName] == nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)).WithErr(errors.New("数据表不存在")), code.Text(code.MySQLExecError)).WithError(errors.New("数据表不存在")),
) )
return return
} }
if !upgradeTableOp[req.Op] { if !upgradeTableOp[req.Op] {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)).WithErr(errors.New("非法操作")), code.Text(code.MySQLExecError)).WithError(errors.New("非法操作")),
) )
return return
} }
if req.Op == "table" { if req.Op == "table" {
if err := db.Exec(upgradeTableList[req.TableName]["table_sql"]).Error; err != nil { if err := db.Exec(upgradeTableList[req.TableName]["table_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err),
) )
return return
} }
@@ -98,10 +97,10 @@ func (h *handler) UpgradeExecute() core.HandlerFunc {
outPutString = "初始化 MySQL 数据表:" + req.TableName + " 成功。" outPutString = "初始化 MySQL 数据表:" + req.TableName + " 成功。"
} else if req.Op == "table_data" { } else if req.Op == "table_data" {
if err := db.Exec(upgradeTableList[req.TableName]["table_data_sql"]).Error; err != nil { if err := db.Exec(upgradeTableList[req.TableName]["table_data_sql"]).Error; err != nil {
ctx.AbortWithError(errno.NewError( ctx.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.MySQLExecError, code.MySQLExecError,
code.Text(code.MySQLExecError)+" "+err.Error()).WithErr(err), code.Text(code.MySQLExecError)+" "+err.Error()).WithError(err),
) )
return return
} }

View File

@@ -0,0 +1,6 @@
package authorized
const (
IsUsedYES = 1 // 启用
IsUsedNo = -1 // 禁用
)

View File

@@ -74,7 +74,10 @@ func after(db *gorm.DB) {
sqlInfo.Stack = utils.FileWithLineNum() sqlInfo.Stack = utils.FileWithLineNum()
sqlInfo.Rows = db.Statement.RowsAffected sqlInfo.Rows = db.Statement.RowsAffected
sqlInfo.CostSeconds = time.Since(ts).Seconds() sqlInfo.CostSeconds = time.Since(ts).Seconds()
ctx.Trace.AppendSQL(sqlInfo)
if ctx.Trace != nil {
ctx.Trace.AppendSQL(sqlInfo)
}
return return
} }

View File

@@ -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
}

View File

@@ -1,4 +1,4 @@
package middleware package interceptor
import ( import (
"encoding/json" "encoding/json"
@@ -9,47 +9,46 @@ import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/repository/redis"
"github.com/xinliangnote/go-gin-api/internal/services/admin" "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/errors"
"github.com/xinliangnote/go-gin-api/pkg/urltable" "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) { return func(c core.Context) {
token := c.GetHeader("Token") token := c.GetHeader("Token")
if token == "" { if token == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusUnauthorized, http.StatusUnauthorized,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Token 参数")), code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Token 参数")),
) )
return return
} }
if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token) { if !i.cache.Exists(configs.RedisKeyPrefixLoginUser + token) {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusUnauthorized, http.StatusUnauthorized,
code.CacheGetError, code.CacheGetError,
code.Text(code.CacheGetError)).WithErr(errors.New("请先登录 1")), code.Text(code.CacheGetError)).WithError(errors.New("请先登录")),
) )
return return
} }
if !m.cache.Exists(configs.RedisKeyPrefixLoginUser + token + ":action") { if !i.cache.Exists(configs.RedisKeyPrefixLoginUser + token + ":action") {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusUnauthorized, http.StatusUnauthorized,
code.CacheGetError, code.CacheGetError,
code.Text(code.CacheGetError)).WithErr(errors.New("请先登录 2")), code.Text(code.CacheGetError)).WithError(errors.New("当前账号未配置 RBAC 权限")),
) )
return 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 { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusUnauthorized, http.StatusUnauthorized,
code.CacheGetError, code.CacheGetError,
code.Text(code.CacheGetError)).WithErr(err), code.Text(code.CacheGetError)).WithError(err),
) )
return return
} }
@@ -57,10 +56,10 @@ func (m *middleware) RBAC() core.HandlerFunc {
var actions []admin.MyActionData var actions []admin.MyActionData
err = json.Unmarshal([]byte(actionData), &actions) err = json.Unmarshal([]byte(actionData), &actions)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusUnauthorized, http.StatusUnauthorized,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err), code.Text(code.AuthorizationError)).WithError(err),
) )
return return
} }
@@ -72,10 +71,10 @@ func (m *middleware) RBAC() core.HandlerFunc {
} }
if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" { if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.RBACError, 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 return
} }

View File

@@ -1,39 +1,36 @@
package middleware package interceptor
import ( import (
"net/http" "net/http"
"strings" "strings"
"time"
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/code" "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/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/errors"
"github.com/xinliangnote/go-gin-api/pkg/signature" "github.com/xinliangnote/go-gin-api/pkg/signature"
"github.com/xinliangnote/go-gin-api/pkg/urltable" "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{ var whiteListPath = map[string]bool{
"/login/web": true, "/login/web": true,
} }
func (m *middleware) Signature() core.HandlerFunc { func (i *interceptor) CheckSignature() core.HandlerFunc {
return func(c core.Context) { return func(c core.Context) {
if !env.Active().IsPro() {
return
}
// 签名信息 // 签名信息
authorization := c.GetHeader(configs.HeaderSignToken) authorization := c.GetHeader(configs.HeaderSignToken)
if authorization == "" { if authorization == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Authorization 参数")), code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Authorization 参数")),
) )
return return
} }
@@ -41,51 +38,51 @@ func (m *middleware) Signature() core.HandlerFunc {
// 时间信息 // 时间信息
date := c.GetHeader(configs.HeaderSignTokenDate) date := c.GetHeader(configs.HeaderSignTokenDate)
if date == "" { if date == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中缺少 Date 参数")), code.Text(code.AuthorizationError)).WithError(errors.New("Header 中缺少 Date 参数")),
) )
return return
} }
// 通过签名信息获取 key // 通过签名信息获取 key
authorizationSplit := strings.Split(authorization, " ") authorizationSplit := strings.Split(authorization, " ")
if len(authorizationSplit) < minLength { if len(authorizationSplit) < 2 {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中 Authorization 格式错误")), code.Text(code.AuthorizationError)).WithError(errors.New("Header 中 Authorization 格式错误")),
) )
return return
} }
key := authorizationSplit[0] key := authorizationSplit[0]
data, err := m.authorizedService.DetailByKey(c, key) data, err := i.authorizedService.DetailByKey(c, key)
if err != nil { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err), code.Text(code.AuthorizationError)).WithError(err),
) )
return return
} }
if data.IsUsed == notUsed { if data.IsUsed == authorized.IsUsedNo {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New(key + " 已被禁止调用")), code.Text(code.AuthorizationError)).WithError(errors.New(key + " 已被禁止调用")),
) )
return return
} }
if len(data.Apis) < 1 { if len(data.Apis) < 1 {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New(key + " 未进行接口授权")), code.Text(code.AuthorizationError)).WithError(errors.New(key + " 未进行接口授权")),
) )
return return
} }
@@ -98,30 +95,30 @@ func (m *middleware) Signature() core.HandlerFunc {
} }
if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" { if pattern, _ := table.Mapping(c.Method() + c.Path()); pattern == "" {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, 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 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 { if err != nil {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(err), code.Text(code.AuthorizationError)).WithError(err),
) )
return return
} }
if !ok { if !ok {
c.AbortWithError(errno.NewError( c.AbortWithError(core.Error(
http.StatusBadRequest, http.StatusBadRequest,
code.AuthorizationError, code.AuthorizationError,
code.Text(code.AuthorizationError)).WithErr(errors.New("Header 中 Authorization 信息错误")), code.Text(code.AuthorizationError)).WithError(errors.New("Header 中 Authorization 信息错误")),
) )
return return
} }

View File

@@ -1,36 +1,33 @@
package middleware package interceptor
import ( import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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/mysql"
"github.com/xinliangnote/go-gin-api/internal/repository/redis" "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/admin"
"github.com/xinliangnote/go-gin-api/internal/services/authorized" "github.com/xinliangnote/go-gin-api/internal/services/authorized"
"github.com/xinliangnote/go-gin-api/pkg/errno"
"go.uber.org/zap" "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 为了避免被其他包实现
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 logger *zap.Logger
cache redis.Repo cache redis.Repo
db mysql.Repo db mysql.Repo
@@ -38,8 +35,8 @@ type middleware struct {
adminService admin.Service adminService admin.Service
} }
func New(logger *zap.Logger, cache redis.Repo, db mysql.Repo) Middleware { func New(logger *zap.Logger, cache redis.Repo, db mysql.Repo) Interceptor {
return &middleware{ return &interceptor{
logger: logger, logger: logger,
cache: cache, cache: cache,
db: db, 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() {}

View File

@@ -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)
}
}

View File

@@ -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
}

View File

@@ -2,13 +2,13 @@ package router
import ( import (
"github.com/xinliangnote/go-gin-api/configs" "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/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/cron"
"github.com/xinliangnote/go-gin-api/internal/repository/mysql" "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/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/errors"
"github.com/xinliangnote/go-gin-api/pkg/file" "github.com/xinliangnote/go-gin-api/pkg/file"
@@ -16,12 +16,12 @@ import (
) )
type resource struct { type resource struct {
mux core.Mux mux core.Mux
logger *zap.Logger logger *zap.Logger
db mysql.Repo db mysql.Repo
cache redis.Repo cache redis.Repo
middles middleware.Middleware interceptors interceptor.Interceptor
cronServer cron.Server cronServer cron.Server
} }
type Server struct { type Server struct {
@@ -73,8 +73,8 @@ func NewHTTPServer(logger *zap.Logger, cronLogger *zap.Logger) (*Server, error)
core.WithEnableOpenBrowser(openBrowserUri), core.WithEnableOpenBrowser(openBrowserUri),
core.WithEnableCors(), core.WithEnableCors(),
core.WithEnableRate(), core.WithEnableRate(),
core.WithPanicNotify(notify.Email), core.WithAlertNotify(alert.NotifyHandler(logger)),
core.WithRecordMetrics(metrics.RecordMetrics), core.WithRecordMetrics(metrics.RecordHandler(logger)),
) )
if err != nil { if err != nil {
@@ -82,7 +82,7 @@ func NewHTTPServer(logger *zap.Logger, cronLogger *zap.Logger) (*Server, error)
} }
r.mux = mux r.mux = mux
r.middles = middleware.New(logger, r.cache, r.db) r.interceptors = interceptor.New(logger, r.cache, r.db)
// 设置 Render 路由 // 设置 Render 路由
setRenderRouter(r) setRenderRouter(r)

View File

@@ -5,24 +5,33 @@ import (
"github.com/xinliangnote/go-gin-api/internal/api/authorized" "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/config"
"github.com/xinliangnote/go-gin-api/internal/api/cron" "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/menu"
"github.com/xinliangnote/go-gin-api/internal/api/tool" "github.com/xinliangnote/go-gin-api/internal/api/tool"
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "github.com/xinliangnote/go-gin-api/internal/pkg/core"
) )
func setApiRouter(r *resource) { 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 // admin
adminHandler := admin.New(r.logger, r.db, r.cache) adminHandler := admin.New(r.logger, r.db, r.cache)
// 需要签名验证,无需登录验证,无需 RBAC 权限验证 // 需要签名验证,无需登录验证,无需 RBAC 权限验证
login := r.mux.Group("/api", r.middles.Signature()) login := r.mux.Group("/api", r.interceptors.CheckSignature())
{ {
login.POST("/login", adminHandler.Login()) login.POST("/login", adminHandler.Login())
} }
// 需要签名验证、登录验证,无需 RBAC 权限验证 // 需要签名验证、登录验证,无需 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.POST("/admin/logout", adminHandler.Logout())
notRBAC.PATCH("/admin/modify_password", adminHandler.ModifyPassword()) notRBAC.PATCH("/admin/modify_password", adminHandler.ModifyPassword())
@@ -31,7 +40,7 @@ func setApiRouter(r *resource) {
} }
// 需要签名验证、登录验证、RBAC 权限验证 // 需要签名验证、登录验证、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 // authorized
authorizedHandler := authorized.New(r.logger, r.db, r.cache) authorizedHandler := authorized.New(r.logger, r.db, r.cache)

View File

@@ -1,6 +1,7 @@
package router package router
import ( 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/admin"
"github.com/xinliangnote/go-gin-api/internal/render/authorized" "github.com/xinliangnote/go-gin-api/internal/render/authorized"
"github.com/xinliangnote/go-gin-api/internal/render/config" "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) renderCron := cron.New(r.logger, r.db, r.cache)
// 无需记录日志,无需 RBAC 权限验证 // 无需记录日志,无需 RBAC 权限验证
notRBAC := r.mux.Group("", r.middles.DisableLog()) notRBAC := r.mux.Group("", core.DisableTraceLog, core.DisableRecordMetrics)
{ {
// 首页 // 首页
notRBAC.GET("", renderIndex.Index()) notRBAC.GET("", renderIndex.Index())
@@ -46,7 +47,7 @@ func setRenderRouter(r *resource) {
} }
// 无需记录日志,需要 RBAC 权限验证 // 无需记录日志,需要 RBAC 权限验证
render := r.mux.Group("", r.middles.DisableLog()) render := r.mux.Group("", core.DisableTraceLog, core.DisableRecordMetrics)
{ {
// 配置信息 // 配置信息
render.GET("/config/email", renderConfig.Email()) render.GET("/config/email", renderConfig.Email())

View File

@@ -1,6 +1,7 @@
package router package router
import ( import (
"github.com/xinliangnote/go-gin-api/internal/pkg/core"
"github.com/xinliangnote/go-gin-api/internal/websocket/sysmessage" "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) 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()) socket.GET("/system/message", systemMessage.Connect())

View File

@@ -19,7 +19,7 @@ func (s *service) Create(ctx core.Context, adminData *CreateAdminData) (id int32
model.Password = password.GeneratePassword(adminData.Password) model.Password = password.GeneratePassword(adminData.Password)
model.Nickname = adminData.Nickname model.Nickname = adminData.Nickname
model.Mobile = adminData.Mobile model.Mobile = adminData.Mobile
model.CreatedUser = ctx.UserName() model.CreatedUser = ctx.SessionUserInfo().UserName
model.IsUsed = 1 model.IsUsed = 1
model.IsDeleted = -1 model.IsDeleted = -1

View File

@@ -27,7 +27,7 @@ func (s *service) CreateMenu(ctx core.Context, menuData *CreateMenuData) (err er
createModel := admin_menu.NewModel() createModel := admin_menu.NewModel()
createModel.AdminId = menuData.AdminId createModel.AdminId = menuData.AdminId
createModel.MenuId = cast.ToInt32(v) createModel.MenuId = cast.ToInt32(v)
createModel.CreatedUser = ctx.UserName() createModel.CreatedUser = ctx.SessionUserInfo().UserName
_, err = createModel.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) _, err = createModel.Create(s.db.GetDbW().WithContext(ctx.RequestContext()))
if err != nil { if err != nil {

View File

@@ -12,7 +12,7 @@ import (
func (s *service) Delete(ctx core.Context, id int32) (err error) { func (s *service) Delete(ctx core.Context, id int32) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"is_deleted": 1, "is_deleted": 1,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := admin.NewQueryBuilder() qb := admin.NewQueryBuilder()

View File

@@ -12,7 +12,7 @@ import (
func (s *service) ModifyPassword(ctx core.Context, id int32, newPassword string) (err error) { func (s *service) ModifyPassword(ctx core.Context, id int32, newPassword string) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"password": password.GeneratePassword(newPassword), "password": password.GeneratePassword(newPassword),
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := admin.NewQueryBuilder() qb := admin.NewQueryBuilder()

View File

@@ -15,7 +15,7 @@ func (s *service) ModifyPersonalInfo(ctx core.Context, id int32, modifyData *Mod
data := map[string]interface{}{ data := map[string]interface{}{
"nickname": modifyData.Nickname, "nickname": modifyData.Nickname,
"mobile": modifyData.Mobile, "mobile": modifyData.Mobile,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := admin.NewQueryBuilder() qb := admin.NewQueryBuilder()

View File

@@ -12,7 +12,7 @@ import (
func (s *service) ResetPassword(ctx core.Context, id int32) (err error) { func (s *service) ResetPassword(ctx core.Context, id int32) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"password": password.ResetPassword(), "password": password.ResetPassword(),
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := admin.NewQueryBuilder() qb := admin.NewQueryBuilder()

View File

@@ -12,7 +12,7 @@ import (
func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) { func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"is_used": used, "is_used": used,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := admin.NewQueryBuilder() qb := admin.NewQueryBuilder()

View File

@@ -25,7 +25,7 @@ func (s *service) Create(ctx core.Context, authorizedData *CreateAuthorizedData)
model.BusinessSecret = secret model.BusinessSecret = secret
model.BusinessDeveloper = authorizedData.BusinessDeveloper model.BusinessDeveloper = authorizedData.BusinessDeveloper
model.Remark = authorizedData.Remark model.Remark = authorizedData.Remark
model.CreatedUser = ctx.UserName() model.CreatedUser = ctx.SessionUserInfo().UserName
model.IsUsed = 1 model.IsUsed = 1
model.IsDeleted = -1 model.IsDeleted = -1

View File

@@ -18,7 +18,7 @@ func (s *service) CreateAPI(ctx core.Context, authorizedAPIData *CreateAuthorize
model.BusinessKey = authorizedAPIData.BusinessKey model.BusinessKey = authorizedAPIData.BusinessKey
model.Method = authorizedAPIData.Method model.Method = authorizedAPIData.Method
model.Api = authorizedAPIData.API model.Api = authorizedAPIData.API
model.CreatedUser = ctx.UserName() model.CreatedUser = ctx.SessionUserInfo().UserName
model.IsDeleted = -1 model.IsDeleted = -1
id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext())) id, err = model.Create(s.db.GetDbW().WithContext(ctx.RequestContext()))

View File

@@ -23,7 +23,7 @@ func (s *service) Delete(ctx core.Context, id int32) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"is_deleted": 1, "is_deleted": 1,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := authorized.NewQueryBuilder() qb := authorized.NewQueryBuilder()

View File

@@ -23,7 +23,7 @@ func (s *service) DeleteAPI(ctx core.Context, id int32) (err error) {
data := map[string]interface{}{ data := map[string]interface{}{
"is_deleted": 1, "is_deleted": 1,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := authorized_api.NewQueryBuilder() qb := authorized_api.NewQueryBuilder()

View File

@@ -2,7 +2,6 @@ package authorized
import ( import (
"encoding/json" "encoding/json"
"time"
"github.com/xinliangnote/go-gin-api/configs" "github.com/xinliangnote/go-gin-api/configs"
"github.com/xinliangnote/go-gin-api/internal/pkg/core" "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) 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 { if err != nil {
return nil, err return nil, err
} }

View File

@@ -22,7 +22,7 @@ func (s *service) UpdateUsed(ctx core.Context, id int32, used int32) (err error)
data := map[string]interface{}{ data := map[string]interface{}{
"is_used": used, "is_used": used,
"updated_user": ctx.UserName(), "updated_user": ctx.SessionUserInfo().UserName,
} }
qb := authorized.NewQueryBuilder() qb := authorized.NewQueryBuilder()

Some files were not shown because too many files have changed in this diff Show More