【更新】更新

This commit is contained in:
PandaGoAdmin
2022-08-02 17:19:14 +08:00
parent 791a23306c
commit 0555922a90
50 changed files with 678 additions and 450 deletions

View File

@@ -8,6 +8,8 @@ import (
"path/filepath"
)
var GConfig *Config
func InitConfig(configFilePath string) *Config {
// 获取启动参数中,配置文件的绝对路径
path, _ := filepath.Abs(configFilePath)
@@ -19,6 +21,7 @@ func InitConfig(configFilePath string) *Config {
}
// 校验配置文件内容信息
yc.Valid()
GConfig = yc
return yc
}

View File

@@ -24,6 +24,7 @@ func BindQuery(g *gin.Context, data any) {
panic(any(biz.NewBizErr(err.Error())))
}
}
func ParamsToAny(g *gin.Context, in any) {
vars := make(map[string]any)
for _, v := range g.Params {

View File

@@ -1,4 +1,4 @@
package ctx
package ginx
import (
"encoding/json"

View File

@@ -1,9 +1,10 @@
package ctx
package ginx
import (
"github.com/dgrijalva/jwt-go"
"pandax/base/biz"
"pandax/base/casbin"
"pandax/base/token"
"pandax/pkg/global"
"strconv"
)
@@ -37,7 +38,7 @@ func PermissionHandler(rc *ReqCtx) error {
if tokenStr == "" {
return biz.PermissionErr
}
j := NewJWT("", []byte(global.Conf.Jwt.Key), jwt.SigningMethodHS256)
j := token.NewJWT("", []byte(global.Conf.Jwt.Key), jwt.SigningMethodHS256)
loginAccount, err := j.ParseToken(tokenStr)
if err != nil || loginAccount == nil {
return biz.PermissionErr

124
base/ginx/req_ctx.go Normal file
View File

@@ -0,0 +1,124 @@
package ginx
import (
"pandax/base/biz"
"pandax/base/token"
"time"
"github.com/gin-gonic/gin"
)
// 处理函数
type HandlerFunc func(*ReqCtx)
type ReqCtx struct {
GinCtx *gin.Context // gin context
// NeedToken bool // 是否需要token
RequiredPermission *Permission // 需要的权限信息默认为nil需要校验token
LoginAccount *token.Claims // 登录账号信息只有校验token后才会有值
LogInfo *LogInfo // 日志相关信息
ReqParam any // 请求参数,主要用于记录日志
ResData any // 响应结果
Err any // 请求错误
timed int64 // 执行时间
noRes bool // 无需返回结果,即文件下载等
}
func (rc *ReqCtx) Handle(handler HandlerFunc) {
ginCtx := rc.GinCtx
defer func() {
var err any
err = recover()
if err != nil {
rc.Err = err
ErrorRes(ginCtx, err)
}
// 应用所有请求后置处理器
ApplyHandlerInterceptor(afterHandlers, rc)
}()
biz.IsTrue(ginCtx != nil, "ginContext == nil")
// 默认为不记录请求参数可在handler回调函数中覆盖赋值
rc.ReqParam = 0
// 默认响应结果为nil可在handler中赋值
rc.ResData = nil
// 调用请求前所有处理器
err := ApplyHandlerInterceptor(beforeHandlers, rc)
if err != nil {
panic(err)
}
begin := time.Now()
handler(rc)
rc.timed = time.Now().Sub(begin).Milliseconds()
if !rc.noRes {
SuccessRes(ginCtx, rc.ResData)
}
}
func (rc *ReqCtx) Download(filename string) {
rc.noRes = true
Download(rc.GinCtx, filename)
}
// 新建请求上下文默认需要校验token
func NewReqCtx(g *gin.Context) *ReqCtx {
return &ReqCtx{GinCtx: g, LogInfo: NewLogInfo("默认日志信息"), RequiredPermission: &Permission{NeedToken: true, NeedCasbin: true}}
}
// 调用该方法设置请求描述,则默认记录日志,并不记录响应结果
func (r *ReqCtx) WithLog(model string) *ReqCtx {
r.LogInfo.Description = model
return r
}
// 设置请求上下文需要的权限信息
func (r *ReqCtx) WithRequiredPermission(permission *Permission) *ReqCtx {
r.RequiredPermission = permission
return r
}
// 是否需要token
func (r *ReqCtx) WithNeedToken(needToken bool) *ReqCtx {
r.RequiredPermission.NeedToken = needToken
return r
}
// 是否需要Casbin
func (r *ReqCtx) WithNeedCasbin(needCasbin bool) *ReqCtx {
r.RequiredPermission.NeedCasbin = needCasbin
return r
}
// 处理器拦截器函数
type HandlerInterceptorFunc func(*ReqCtx) error
type HandlerInterceptors []HandlerInterceptorFunc
var (
beforeHandlers HandlerInterceptors
afterHandlers HandlerInterceptors
)
// 使用前置处理器函数
func UseBeforeHandlerInterceptor(b HandlerInterceptorFunc) {
beforeHandlers = append(beforeHandlers, b)
}
// 使用后置处理器函数
func UseAfterHandlerInterceptor(b HandlerInterceptorFunc) {
afterHandlers = append(afterHandlers, b)
}
// 应用指定处理器拦截器,如果有一个错误则直接返回错误
func ApplyHandlerInterceptor(his HandlerInterceptors, rc *ReqCtx) any {
for _, handler := range his {
if err := handler(rc); err != nil {
return err
}
}
return nil
}

View File

@@ -10,8 +10,10 @@ import (
"github.com/sirupsen/logrus"
)
var Log *logrus.Logger
func InitLog(logConf *config.Log) *logrus.Logger {
var Log = logrus.New()
Log = logrus.New()
Log.SetFormatter(new(LogFormatter))
Log.SetReportCaller(true)

View File

@@ -0,0 +1,84 @@
package restfulx
import (
"encoding/json"
"fmt"
"pandax/base/biz"
"pandax/base/utils"
"pandax/pkg/global"
"reflect"
"runtime/debug"
"github.com/sirupsen/logrus"
)
type LogInfo struct {
LogResp bool // 是否记录返回结果
Description string // 请求描述
}
func NewLogInfo(description string) *LogInfo {
return &LogInfo{Description: description, LogResp: false}
}
func (i *LogInfo) WithLogResp(logResp bool) *LogInfo {
i.LogResp = logResp
return i
}
func LogHandler(rc *ReqCtx) error {
li := rc.LogInfo
if li == nil {
return nil
}
lfs := logrus.Fields{}
if la := rc.LoginAccount; la != nil {
lfs["uid"] = la.UserId
lfs["uname"] = la.UserName
}
req := rc.GinCtx.Request
lfs[req.Method] = req.URL.Path
if err := rc.Err; err != nil {
global.Log.WithFields(lfs).Error(getErrMsg(rc, err))
return nil
}
global.Log.WithFields(lfs).Info(getLogMsg(rc))
return nil
}
func getLogMsg(rc *ReqCtx) string {
msg := rc.LogInfo.Description + fmt.Sprintf(" ->%dms", rc.timed)
if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
rb, _ := json.Marshal(rc.ReqParam)
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
}
// 返回结果不为空,则记录返回结果
if rc.LogInfo.LogResp && !utils.IsBlank(reflect.ValueOf(rc.ResData)) {
respB, _ := json.Marshal(rc.ResData)
msg = msg + fmt.Sprintf("\n<-- %s", string(respB))
}
return msg
}
func getErrMsg(rc *ReqCtx, err any) string {
msg := rc.LogInfo.Description
if !utils.IsBlank(reflect.ValueOf(rc.ReqParam)) {
rb, _ := json.Marshal(rc.ReqParam)
msg = msg + fmt.Sprintf("\n--> %s", string(rb))
}
var errMsg string
switch t := err.(type) {
case *biz.BizError:
errMsg = fmt.Sprintf("\n<-e errCode: %d, errMsg: %s", t.Code(), t.Error())
case error:
errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t.Error(), string(debug.Stack()))
case string:
errMsg = fmt.Sprintf("\n<-e errMsg: %s\n%s", t, string(debug.Stack()))
}
return (msg + errMsg)
}

View File

@@ -0,0 +1,60 @@
package restfulx
import (
"github.com/dgrijalva/jwt-go"
"pandax/base/biz"
"pandax/base/casbin"
"pandax/base/token"
"pandax/pkg/global"
"strconv"
)
type Permission struct {
NeedToken bool // 是否需要token
NeedCasbin bool // 是否进行权限 api路径权限验证
}
func (p *Permission) WithNeedToken(needToken bool) *Permission {
p.NeedToken = needToken
return p
}
func (p *Permission) WithNeedCasBin(needCasBin bool) *Permission {
p.NeedCasbin = needCasBin
return p
}
func PermissionHandler(rc *ReqCtx) error {
permission := rc.RequiredPermission
// 如果需要的权限信息不为空并且不需要token则不返回错误继续后续逻辑
if permission != nil && !permission.NeedToken {
return nil
}
tokenStr := rc.GinCtx.Request.Header.Get("X-TOKEN")
// header不存在则从查询参数token中获取
if tokenStr == "" {
tokenStr = rc.GinCtx.Query("token")
}
if tokenStr == "" {
return biz.PermissionErr
}
j := token.NewJWT("", []byte(global.Conf.Jwt.Key), jwt.SigningMethodHS256)
loginAccount, err := j.ParseToken(tokenStr)
if err != nil || loginAccount == nil {
return biz.PermissionErr
}
rc.LoginAccount = loginAccount
if !permission.NeedCasbin {
return nil
}
e := casbin.Casbin()
// 判断策略中是否存在
tenantId := strconv.Itoa(int(rc.LoginAccount.TenantId))
success, err := e.Enforce(tenantId, loginAccount.RoleKey, rc.GinCtx.Request.URL.Path, rc.GinCtx.Request.Method)
if !success {
return biz.CasbinErr
}
return nil
}

View File

@@ -1,8 +1,9 @@
package ctx
package restfulx
import (
"pandax/base/biz"
"pandax/base/ginx"
"pandax/base/token"
"time"
"github.com/gin-gonic/gin"
@@ -15,8 +16,8 @@ type ReqCtx struct {
GinCtx *gin.Context // gin context
// NeedToken bool // 是否需要token
RequiredPermission *Permission // 需要的权限信息默认为nil需要校验token
LoginAccount *Claims // 登录账号信息只有校验token后才会有值
RequiredPermission *Permission // 需要的权限信息默认为nil需要校验token
LoginAccount *token.Claims // 登录账号信息只有校验token后才会有值
LogInfo *LogInfo // 日志相关信息
ReqParam any // 请求参数,主要用于记录日志
@@ -65,12 +66,7 @@ func (rc *ReqCtx) Download(filename string) {
ginx.Download(rc.GinCtx, filename)
}
// 新建请求上下文默认需要校验token
func NewReqCtx() *ReqCtx {
return &ReqCtx{}
}
func NewReqCtxWithGin(g *gin.Context) *ReqCtx {
func NewReqCtx(g *gin.Context) *ReqCtx {
return &ReqCtx{GinCtx: g, RequiredPermission: &Permission{NeedToken: true, NeedCasbin: true}}
}

95
base/restfulx/restfulx.go Normal file
View File

@@ -0,0 +1,95 @@
package restfulx
import (
"encoding/json"
"net/http"
"pandax/base/biz"
"pandax/base/model"
"pandax/pkg/global"
"strconv"
"github.com/gin-gonic/gin"
)
// 绑定并校验请求结构体参数 结构体添加 例如: binding:"required" 或binding:"required,gt=10"
func BindJsonAndValid(g *gin.Context, data any) {
if err := g.ShouldBindJSON(data); err != nil {
panic(any(biz.NewBizErr("传参格式错误:" + err.Error())))
}
}
// 绑定查询字符串到
func BindQuery(g *gin.Context, data any) {
if err := g.ShouldBindQuery(data); err != nil {
panic(any(biz.NewBizErr(err.Error())))
}
}
func ParamsToAny(g *gin.Context, in any) {
vars := make(map[string]any)
for _, v := range g.Params {
vars[v.Key] = v.Value
}
marshal, _ := json.Marshal(vars)
err := json.Unmarshal(marshal, in)
biz.ErrIsNil(err, "error get path value encoding unmarshal")
return
}
// 获取分页参数
func GetPageParam(g *gin.Context) *model.PageParam {
return &model.PageParam{PageNum: QueryInt(g, "pageNum", 1), PageSize: QueryInt(g, "pageSize", 10)}
}
// 获取查询参数中指定参数值并转为int
func QueryInt(g *gin.Context, qm string, defaultInt int) int {
qv := g.Query(qm)
if qv == "" {
return defaultInt
}
qvi, err := strconv.Atoi(qv)
biz.ErrIsNil(err, "query param not int")
return qvi
}
// 获取路径参数
func PathParamInt(g *gin.Context, pm string) int {
value, _ := strconv.Atoi(g.Param(pm))
return value
}
// 文件下载
func Download(g *gin.Context, filename string) {
g.Writer.Header().Add("success", "true")
g.Writer.Header().Set("Content-Length", "-1")
g.Writer.Header().Set("Content-Disposition", "attachment; filename="+filename)
g.File(filename)
}
// 返回统一成功结果
func SuccessRes(g *gin.Context, data any) {
g.JSON(http.StatusOK, model.Success(data))
}
// 返回失败结果集
func ErrorRes(g *gin.Context, err any) {
if err != nil {
}
switch t := err.(type) {
case *biz.BizError:
g.JSON(http.StatusOK, model.Error(t))
break
case error:
g.JSON(http.StatusOK, model.ServerError())
global.Log.Error(t)
// panic(err)
break
case string:
g.JSON(http.StatusOK, model.ServerError())
global.Log.Error(t)
// panic(err)
break
default:
global.Log.Error(t)
}
}

View File

@@ -1,4 +1,4 @@
package ctx
package token
import (
"errors"