diff --git a/kit/ginx/ginx.go b/kit/ginx/ginx.go new file mode 100644 index 0000000..6bc3925 --- /dev/null +++ b/kit/ginx/ginx.go @@ -0,0 +1,96 @@ +package ginx + +import ( + "encoding/json" + "net/http" + "pandax/kit/biz" + "pandax/kit/logger" + "pandax/kit/model" + "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()) + logger.Log.Error(t) + // panic(err) + break + case string: + g.JSON(http.StatusOK, model.ServerError()) + logger.Log.Error(t) + // panic(err) + break + default: + logger.Log.Error(t) + } +} diff --git a/kit/ginx/log_handler.go b/kit/ginx/log_handler.go new file mode 100644 index 0000000..446c4f4 --- /dev/null +++ b/kit/ginx/log_handler.go @@ -0,0 +1,15 @@ +package ginx + +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 +} diff --git a/kit/ginx/permission_handler.go b/kit/ginx/permission_handler.go new file mode 100644 index 0000000..cd8fba6 --- /dev/null +++ b/kit/ginx/permission_handler.go @@ -0,0 +1,16 @@ +package ginx + +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 +} diff --git a/kit/ginx/req_ctx.go b/kit/ginx/req_ctx.go new file mode 100644 index 0000000..b8e0a57 --- /dev/null +++ b/kit/ginx/req_ctx.go @@ -0,0 +1,124 @@ +package ginx + +import ( + "pandax/kit/biz" + "pandax/kit/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 +}