package binding import ( "git.sxidc.com/go-framework/baize/api" "git.sxidc.com/go-framework/baize/domain" "git.sxidc.com/go-tools/utils/strutils" "git.sxidc.com/service-supports/fserr" "net/http" "strings" ) func PostBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) { item.bind(router, http.MethodPost, middlewares...) } func DeleteBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) { item.bind(router, http.MethodDelete, middlewares...) } func PutBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) { item.bind(router, http.MethodPut, middlewares...) } func GetBind[O any](router api.Router, item *SimpleBindItem[O], middlewares ...api.Handler) { item.bind(router, http.MethodGet, middlewares...) } func Bind[O any](router api.Router, item *BindItem[O], middlewares ...api.Handler) { item.bind(router, middlewares...) } func Static(router api.Router, item *StaticBindItem) { item.bind(router) } func StaticFile(router api.Router, item *StaticFileBindItem) { item.bind(router) } type DTOBindFunc func(c *api.Context, dto DTO) error type FormDomainObjectsFunc func(c *api.Context, dto DTO) ([]domain.Object, error) type ServiceFunc[O any] func(c *api.Context, dto DTO, objects []domain.Object) (O, error) type ResponseFunc[O any] func(c *api.Context, statusCode int, data O, err error) // SimpleBindItem 路由条目 type SimpleBindItem[O any] struct { // URL相对路径 Path string // 使用的dto,非必传,当dto为nil时,说明该接口没有参数 DTO DTO // 可选的dto绑定函数 // 非必传,POST和PUT请求默认为JsonBody,DELETE默认为PathParams,GET默认为QueryParams // 额外还提供了一些转化函数: // MultipartForm: 绑定multipart form // FormBody: 绑定form body // XMLBody: 绑定XML body DTOBindFunc DTOBindFunc // 通过DTO构造使用的领域对象,之后在ServiceFunc中会按照构造实体的顺序进行回调 // 非必传,如果为nil,则说明没有领域对象 // 与Objects字段二选一使用,如果都指定,会按照该字段处理 FormDomainObjectsFunc FormDomainObjectsFunc // 使用的领域对象,当使用Tag对实体进行标注后,可以直接通过该字段给定实体,之后在ServiceFunc中会按照给定实体的顺序进行回调 // 非必传,如果为nil或长度为0,则说明没有领域对象 // 与FormObjectsFunc字段二选一使用,如果都指定,会按照FormObjectsFunc字段处理 Objects []domain.Object // 应用服务泛型函数 ServiceFunc ServiceFunc[O] // 响应泛型函数,如果不响应,需要使用NoResponse零值占位 ResponseFunc ResponseFunc[O] } func (item *SimpleBindItem[O]) bind(router api.Router, method string, middlewares ...api.Handler) { bindingItem := &BindItem[O]{ Method: method, SimpleBindItem: item, } bindingItem.bind(router, middlewares...) } // BindItem 路由条目结构 type BindItem[O any] struct { Method string *SimpleBindItem[O] } func (item *BindItem[O]) bind(router api.Router, middlewares ...api.Handler) { if strutils.IsStringEmpty(item.Path) { panic("需要指定路径") } if strutils.IsStringEmpty(item.Method) { panic("需要指定方法") } if item.ResponseFunc == nil { panic("需要指定响应函数") } if item.ServiceFunc == nil { panic("需要指定应用服务函数") } // 给单个路由增加中间件 handlers := []api.Handler{ func(c *api.Context) { dto := item.DTO // 有请求数据 if dto != nil { // 将请求数据解析到dto中 if item.DTOBindFunc != nil { err := item.DTOBindFunc(c, dto) if err != nil { var outputZero O item.ResponseFunc(c, http.StatusBadRequest, outputZero, err) return } } else { switch item.Method { case http.MethodPost: fallthrough case http.MethodPut: err := JsonBody(c, dto) if err != nil { var outputZero O item.ResponseFunc(c, http.StatusBadRequest, outputZero, err) return } case http.MethodGet: err := QueryParams(c, dto) if err != nil { var outputZero O item.ResponseFunc(c, http.StatusBadRequest, outputZero, err) return } case http.MethodDelete: err := PathParams(c, dto) if err != nil { var outputZero O item.ResponseFunc(c, http.StatusBadRequest, outputZero, err) return } } } } // 进行领域对象转化 var domainObjects []domain.Object if item.FormDomainObjectsFunc != nil { innerDomainObjects, err := item.FormDomainObjectsFunc(c, dto) if err != nil { var outputZero O item.ResponseFunc(c, http.StatusOK, outputZero, err) return } domainObjects = innerDomainObjects } else { // TODO Tag处理 } // 执行业务函数 statusCode := http.StatusOK outputModel, err := item.ServiceFunc(c, dto, domainObjects) if err != nil { statusCode = fserr.ParseCode(err).HttpCode } item.ResponseFunc(c, statusCode, outputModel, err) return }, } handlers = append(handlers, middlewares...) // 所有的函数加入到执行链中 router.AddRoute(item.Method, item.Path, handlers...) } // StaticBindItem 静态路由item type StaticBindItem struct { RelativePath string Root string WithBasePath bool } func (item *StaticBindItem) bind(router api.Router) { if item.WithBasePath { router.Static(strings.TrimPrefix(item.RelativePath, router.BasePath()), item.Root) } else { router.Static(item.RelativePath, item.Root) } } type StaticFileBindItem struct { RelativePath string FilePath string WithBasePath bool } func (item *StaticFileBindItem) bind(router api.Router) { if item.WithBasePath { router.StaticFile(strings.TrimPrefix(item.RelativePath, router.BasePath()), item.FilePath) } else { router.StaticFile(item.RelativePath, item.FilePath) } }