min-report/utils/handler.go

181 lines
3.7 KiB
Go
Raw Normal View History

2023-08-18 09:59:43 +00:00
package utils
import (
"context"
"errors"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"net/http"
"reflect"
)
var (
ErrMustHasTwoParam = errors.New("method must has two input")
ErrMustHasTwoOut = errors.New("method must has two out")
ErrMustValid = errors.New("method must valid")
ErrMustFunc = errors.New("method must func")
ErrMustPtr = errors.New("method must ptr")
ErrMustPointToStruct = errors.New("method must point to struct")
)
func checkMethod(method interface{}) (mV reflect.Value, reqT, replyT reflect.Type, err error) {
mV = reflect.ValueOf(method)
if !mV.IsValid() {
err = ErrMustValid
return
}
mT := mV.Type()
if mT.Kind() != reflect.Func {
err = ErrMustFunc
return
}
if mT.NumIn() != 2 {
err = ErrMustHasTwoParam
return
}
reqT = mT.In(1)
if reqT.Kind() != reflect.Ptr {
err = ErrMustPtr
return
}
if reqT.Elem().Kind() != reflect.Struct {
err = ErrMustPointToStruct
return
}
reqT = reqT.Elem()
if mT.NumOut() != 2 {
err = ErrMustHasTwoOut
return
}
replyT = mT.Out(0)
if replyT.Kind() != reflect.Ptr {
err = ErrMustPtr
return
}
replyT = replyT.Elem()
return mV, reqT, replyT, err
}
var (
RequestKey struct{}
ResponseKey struct{}
)
type Option struct {
logger *logrus.Logger
}
func CreateHandlerFunc(method interface{}, opts ...Option) gin.HandlerFunc {
option := Option{logger: logrus.StandardLogger()}
if len(opts) > 0 {
option = opts[0]
}
mV, reqT, _, err := checkMethod(method)
if err != nil {
panic(err)
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
results := mV.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(req)})
if results[1].Interface() != nil {
return results[0].Interface(), results[1].Interface().(error)
}
return results[0].Interface(), nil
}
l := option.logger
return func(c *gin.Context) {
ctx := c.Request.Context()
req := reflect.New(reqT)
if err := DecodeRequestWithHeader(c, req.Interface()); err != nil {
l.WithFields(logrus.Fields{
"req": c.Request.URL,
"err": err,
}).Warn(ctx, "bind param failed")
WriteResponseWithCode(c, nil, err)
return
}
ctx = context.WithValue(ctx, RequestKey, c.Request)
ctx = context.WithValue(ctx, ResponseKey, c.Writer)
// 有些请求没有带shopid和platform 需要写到context里面,后面规范后可以去掉
l.WithFields(logrus.Fields{
"req": req.Interface(),
"func": mV.Type().String(),
}).Info(ctx, "invoke handler")
rsp, err := handler(ctx, req.Interface())
if err != nil {
l.WithFields(logrus.Fields{
"req": c.Request.URL,
"err": err,
}).Warn(ctx, "handler err")
WriteResponseWithCode(c, nil, err)
return
}
WriteResponseWithCode(c, rsp, nil)
}
}
func StatCode(err error) int {
if err == nil {
return 0
}
return -1
}
func WriteErrResponse(c *gin.Context, err error) {
status := http.StatusOK
code := int64(-1)
var message string
message = err.Error()
c.JSON(status, &RespStruct{
Code: code,
Message: message,
})
}
func WriteResponse(c *gin.Context, rsp interface{}, err error) {
if err != nil {
WriteErrResponse(c, err)
return
}
c.PureJSON(http.StatusOK, rsp)
}
func WriteResponseWithCode(c *gin.Context, rsp interface{}, err error) {
if err != nil {
WriteErrResponse(c, err)
return
}
c.PureJSON(http.StatusOK, &RespStruct{
Data: rsp,
})
}
type RespStruct struct {
Code int64 `json:"code"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func DecodeRequestWithHeader(c *gin.Context, req interface{}) error {
if err := c.ShouldBind(req); err != nil {
// 有些 POST请求但参数写在query兼容一下吧
if err = c.BindQuery(req); err != nil {
return err
}
}
return c.BindHeader(req)
}