package api import ( "context" "encoding/json" "net/http" "os" "path/filepath" "runtime/debug" "slices" "strconv" "time" "git.sxidc.com/go-framework/baize/framework/core/infrastructure/logger" "git.sxidc.com/go-tools/utils/fileutils" "git.sxidc.com/go-tools/utils/strutils" "github.com/gin-gonic/gin" "github.com/pkg/errors" ) type Api struct { options Options server *http.Server rootRouter *RootRouter prefixRouter *PrefixRouter } func New(opts ...Option) *Api { options := new(Options) for _, opt := range opts { opt(options) } if strutils.IsStringEmpty(options.port) { options.port = "8080" } engine := gin.New() server := &http.Server{ Addr: ":" + options.port, Handler: engine, } engine.Use( // 日志中间件 func(c *gin.Context) { start := time.Now() c.Next() path := c.Request.URL.Path if slices.Contains(options.logSkipPaths, path) { return } end := time.Now() logger.GetInstance().Info(" %d | %s | %s \"%s\"", c.Writer.Status(), strconv.Itoa(int(end.Sub(start).Milliseconds()))+"ms", c.Request.Method, path, ) }, // recover func(c *gin.Context) { err := recover() if err != nil { logger.GetInstance().Error("%s", debug.Stack()) } }) api := &Api{ options: *options, server: server, rootRouter: newRootRouter(engine), } if strutils.IsStringNotEmpty(options.urlPrefix) { api.prefixRouter = newPrefixRouter(engine.Group(options.urlPrefix)) } return api } func NewWithEngine(server *http.Server, engine *gin.Engine, opts ...Option) *Api { options := new(Options) for _, opt := range opts { opt(options) } if strutils.IsStringEmpty(options.port) { options.port = "8080" } api := &Api{ options: *options, server: server, rootRouter: newRootRouter(engine), } if strutils.IsStringNotEmpty(options.urlPrefix) { api.prefixRouter = newPrefixRouter(engine.Group(options.urlPrefix)) } return api } // Start 运行Api // 参数: 无 // 返回值: // - 错误 func (api *Api) Start() error { err := api.server.ListenAndServe() if err != nil && !errors.Is(err, http.ErrServerClosed) { return errors.New(err.Error()) } return nil } // Finish 终止Api // 参数: 无 // 返回值: // - 错误 func (api *Api) Finish() error { err := api.server.Shutdown(context.Background()) if err != nil { return errors.New(err.Error()) } return nil } // Options 获取Api的选项 // 参数: 无 // 返回值: // - Api的选项 func (api *Api) Options() Options { return api.options } // RootRouter 获取Api的根路由 // 参数: 无 // 返回值: // - Api的根路由 func (api *Api) RootRouter() Router { return api.rootRouter } // PrefixRouter 获取Api的前缀路由 // 参数: 无 // 返回值: // - Api的前缀路由 func (api *Api) PrefixRouter() Router { if api.prefixRouter == nil { return api.rootRouter } return api.prefixRouter } const ( RouterRoot = "root" RouterPrefix = "prefix" ) // ChooseRouter 选择Router // 参数: // - routerType: 路由类型,有两种 // - api.RouterRoot: 根路由 // - api.RouterPrefix: 带url前缀的路由 // // - version: 版本,用于获取基于上面两中路由够造的带有版本号的路由,传空字符串则选定上面两种路由本身 // 返回值: // - 选择的Router func (api *Api) ChooseRouter(routerType string, version string) Router { var router Router switch routerType { case RouterRoot: router = api.RootRouter() case RouterPrefix: router = api.PrefixRouter() default: router = api.PrefixRouter() } if strutils.IsStringNotEmpty(version) { router = router.VersionedRouter(version) } return router } type DumpedPermissionItems struct { Namespace string `json:"namespace"` Gateway string `json:"gateway"` New []PermissionItem `json:"new"` Update []PermissionItem `json:"update"` Delete []PermissionItem `json:"delete"` } // DumpRouterPermissionItems 导出 // 参数: // - onSave 保存权限条目回调 // 返回值: // - 导出的权限条目 func (api *Api) DumpRouterPermissionItems(onSave func(items *DumpedPermissionItems) error) error { if api.options.dumpPermissionItemsOption == nil { return nil } dumped := &DumpedPermissionItems{ Namespace: api.options.dumpPermissionItemsOption.Namespace, Gateway: api.options.dumpPermissionItemsOption.Gateway, New: make([]PermissionItem, 0), Update: make([]PermissionItem, 0), Delete: make([]PermissionItem, 0), } savedPermissionItems := make([]PermissionItem, 0) saveDir := filepath.Join( api.options.dumpPermissionItemsOption.SaveDir, api.options.dumpPermissionItemsOption.Namespace, api.options.dumpPermissionItemsOption.Gateway) saveFilePath := filepath.Join(saveDir, "dump.json") exist := fileutils.PathExists(saveFilePath) if exist { content, err := os.ReadFile(saveFilePath) if err != nil { return err } err = json.Unmarshal(content, &savedPermissionItems) if err != nil { return err } } routerPermissionItems := api.rootRouter.DumpPermissionItems() routerPermissionItems = append(routerPermissionItems, api.prefixRouter.DumpPermissionItems()...) for _, savedPermissionItem := range savedPermissionItems { find := false for _, routerPermissionItem := range routerPermissionItems { if savedPermissionItem.Resource == routerPermissionItem.Resource && savedPermissionItem.Action == routerPermissionItem.Action { if savedPermissionItem.Group != routerPermissionItem.Group || savedPermissionItem.Name != routerPermissionItem.Name || savedPermissionItem.Description != routerPermissionItem.Description || savedPermissionItem.NeedCheckExpire != routerPermissionItem.NeedCheckExpire || savedPermissionItem.SensitiveWordScene != routerPermissionItem.SensitiveWordScene { dumped.Update = append(dumped.Update, routerPermissionItem) } find = true break } } if !find { dumped.Delete = append(dumped.Delete, savedPermissionItem) } } for _, routerPermissionItem := range routerPermissionItems { find := false for _, savedPermissionItem := range savedPermissionItems { if routerPermissionItem.Resource == savedPermissionItem.Resource && routerPermissionItem.Action == savedPermissionItem.Action { find = true break } } if !find { dumped.New = append(dumped.New, routerPermissionItem) } } if onSave != nil { err := onSave(dumped) if err != nil { return err } } if !fileutils.PathExists(saveDir) { err := os.MkdirAll(saveDir, os.ModeDir|os.ModePerm) if err != nil { return err } } if routerPermissionItems == nil || len(routerPermissionItems) == 0 { if fileutils.PathExists(saveFilePath) { err := os.Remove(saveFilePath) if err != nil { return err } } } routerPermissionItemsJson, err := json.Marshal(routerPermissionItems) if err != nil { return err } err = os.WriteFile(saveFilePath, routerPermissionItemsJson, os.ModePerm) if err != nil { return err } return nil }