package http_binding import ( "git.sxidc.com/go-tools/api_binding/http_binding/binding_context" "git.sxidc.com/go-tools/api_binding/http_binding/middleware" "git.sxidc.com/go-tools/api_binding/http_binding/request" "git.sxidc.com/go-tools/api_binding/http_binding/response" "git.sxidc.com/go-tools/api_binding/utils" "git.sxidc.com/service-supports/fserr" "github.com/gin-gonic/gin" "net/http" "reflect" "strings" ) type BusinessFunc[I any, O any] func(c *binding_context.Context, inputModel I) (O, error) type BindingFunc[O any] func(c *binding_context.Context, request any, sendFunc response.SendFunc[O]) bool type Binding struct { routerGroup *gin.RouterGroup } func NewBinding(apiVersion string, middlewares ...middleware.Func) *Binding { apiPrefix := urlPrefix + "/api" if utils.IsStringNotEmpty(apiVersion) && apiVersion != "root" { apiPrefix += "/" + apiVersion } ginMiddlewares := make([]gin.HandlerFunc, 0) for _, m := range middlewares { innerM := m ginMiddlewares = append(ginMiddlewares, func(c *gin.Context) { innerM(&binding_context.Context{Context: c}) }) } return &Binding{routerGroup: routerInstance.Group(apiPrefix, ginMiddlewares...)} } func PostBind[I any, O any](b *Binding, item *SimpleBindItem[I, O], middlewares ...middleware.Func) { item.bind(b.routerGroup, http.MethodPost, middlewares...) } func DeleteBind[I any, O any](b *Binding, item *SimpleBindItem[I, O], middlewares ...middleware.Func) { item.bind(b.routerGroup, http.MethodDelete, middlewares...) } func PutBind[I any, O any](b *Binding, item *SimpleBindItem[I, O], middlewares ...middleware.Func) { item.bind(b.routerGroup, http.MethodPut, middlewares...) } func GetBind[I any, O any](b *Binding, item *SimpleBindItem[I, O], middlewares ...middleware.Func) { item.bind(b.routerGroup, http.MethodGet, middlewares...) } func Bind[I any, O any](b *Binding, item *BindItem[I, O], middlewares ...middleware.Func) { item.bind(b.routerGroup, middlewares...) } func Static(b *Binding, item *StaticBindItem) { item.bind(b.routerGroup) } type SimpleBindItem[I any, O any] struct { Path string ResponseFunc response.SendFunc[O] BusinessFunc BusinessFunc[I, O] OptionalBindingFunc BindingFunc[O] } func (item *SimpleBindItem[I, O]) bind(routerGroup *gin.RouterGroup, method string, middlewares ...middleware.Func) { bindingItem := &BindItem[I, O]{ Method: method, SimpleBindItem: item, } bindingItem.bind(routerGroup, middlewares...) } type BindItem[I any, O any] struct { Method string *SimpleBindItem[I, O] } func (item *BindItem[I, O]) bind(routerGroup *gin.RouterGroup, middlewares ...middleware.Func) { if utils.IsStringEmpty(item.Path) { panic("需要指定路径") } if utils.IsStringEmpty(item.Method) { panic("需要指定方法") } if item.ResponseFunc == nil { panic("需要指定响应函数") } var inputModel I inputType := reflect.TypeOf(inputModel) if inputType != nil { if inputType.Kind() == reflect.Pointer { panic("输入对象不能使用指针类型") } if inputType.Kind() != reflect.Struct { panic("输入对象必须是结构") } } ginHandleFunctions := make([]gin.HandlerFunc, 0) for _, m := range middlewares { innerM := m ginHandleFunctions = append(ginHandleFunctions, func(c *gin.Context) { innerM(&binding_context.Context{Context: c}) }) } ginHandleFunctions = append(ginHandleFunctions, func(c *gin.Context) { bindingContext := &binding_context.Context{Context: c} if inputType != nil { if item.OptionalBindingFunc != nil { ok := item.OptionalBindingFunc(bindingContext, &inputModel, item.ResponseFunc) if !ok { return } } else { switch item.Method { case http.MethodPost: fallthrough case http.MethodPut: ok := request.BindingJson(bindingContext, &inputModel, item.ResponseFunc) if !ok { return } case http.MethodGet: fallthrough case http.MethodDelete: ok := request.BindingQuery(bindingContext, &inputModel, item.ResponseFunc) if !ok { return } } } } if item.BusinessFunc != nil { statusCode := http.StatusOK outputModel, err := item.BusinessFunc(bindingContext, inputModel) if err != nil { statusCode = fserr.ParseCode(err).HttpCode } item.ResponseFunc(bindingContext, statusCode, outputModel, err) return } }) routerGroup.Handle(item.Method, item.Path, ginHandleFunctions...) } type StaticBindItem struct { RelativePath string Root string WithBasePath bool } func (item *StaticBindItem) bind(routerGroup *gin.RouterGroup) { if item.WithBasePath { routerGroup.Static(strings.TrimPrefix(item.RelativePath, routerGroup.BasePath()), item.Root) } else { routerGroup.Static(item.RelativePath, item.Root) } }