min-report/utils/handler.go

181 lines
3.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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