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