181 lines
3.7 KiB
Go
181 lines
3.7 KiB
Go
|
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)
|
|||
|
}
|