Browse Source

添加url模式

yjp 1 week ago
parent
commit
8784aa949f
6 changed files with 296 additions and 14 deletions
  1. 6 1
      baize.go
  2. 98 4
      framework/core/api/api.go
  3. 16 0
      framework/core/api/options.go
  4. 165 3
      framework/core/api/router.go
  5. 10 4
      framework/core/application/config.go
  6. 1 2
      go.sum

+ 6 - 1
baize.go

@@ -12,7 +12,12 @@ func NewApplication(conf application.Config) *application.App {
 	apiConfig := conf.ApiConfig
 	apiInstance := api.New(api.WithUrlPrefix(apiConfig.UrlPrefix),
 		api.WithPort(apiConfig.Port),
-		api.WithLogSkipPaths(apiConfig.LogSkipPaths...))
+		api.WithLogSkipPaths(apiConfig.LogSkipPaths...),
+		api.WithDumpPermissionItemsOption(&api.DumpPermissionItemsOption{
+			Namespace: conf.DumpPermissionItem.Namespace,
+			Gateway:   conf.DumpPermissionItem.Gateway,
+			SaveDir:   conf.DumpPermissionItem.SaveDir,
+		}))
 
 	// 创建基础设施
 	infrastructureConfig := new(infrastructure.Config)

+ 98 - 4
framework/core/api/api.go

@@ -2,15 +2,20 @@ package api
 
 import (
 	"context"
-	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/logger"
-	"git.sxidc.com/go-tools/utils/strutils"
-	"github.com/gin-gonic/gin"
-	"github.com/pkg/errors"
+	"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 {
@@ -189,3 +194,92 @@ func (api *Api) ChooseRouter(routerType string, version string) Router {
 
 	return router
 }
+
+type DumpedPermissionItems struct {
+	Namespace string           `json:"namespace"`
+	New       []PermissionItem `json:"new" json:"new"`
+	Update    []PermissionItem `json:"update" json:"update"`
+	Delete    []PermissionItem `json:"delete" json:"delete"`
+}
+
+// DumpRouterPermissionItems 导出
+// 参数:
+// - 无
+// 返回值:
+// - 导出的权限条目
+func (api *Api) DumpRouterPermissionItems() (*DumpedPermissionItems, error) {
+	if api.options.dumpPermissionItemsOption == nil {
+		return new(DumpedPermissionItems), nil
+	}
+
+	dumped := &DumpedPermissionItems{
+		Namespace: api.options.dumpPermissionItemsOption.Namespace,
+		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 nil, err
+		}
+
+		err = json.Unmarshal(content, &savedPermissionItems)
+		if err != nil {
+			return nil, 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)
+		}
+	}
+
+	return dumped, nil
+}

+ 16 - 0
framework/core/api/options.go

@@ -9,6 +9,15 @@ type Options struct {
 
 	// 日志跳过的打印路径
 	logSkipPaths []string
+
+	// 导出权限项配置
+	dumpPermissionItemsOption *DumpPermissionItemsOption
+}
+
+type DumpPermissionItemsOption struct {
+	Namespace string
+	Gateway   string
+	SaveDir   string
 }
 
 func (options Options) GetPort() string {
@@ -41,3 +50,10 @@ func WithLogSkipPaths(logSkipPaths ...string) Option {
 		options.logSkipPaths = logSkipPaths
 	}
 }
+
+// WithDumpPermissionItemsOption 设置导出权限项配置
+func WithDumpPermissionItemsOption(option *DumpPermissionItemsOption) Option {
+	return func(options *Options) {
+		options.dumpPermissionItemsOption = option
+	}
+}

+ 165 - 3
framework/core/api/router.go

@@ -1,8 +1,17 @@
 package api
 
 import (
-	"github.com/gin-gonic/gin"
+	"errors"
 	"net/http"
+	"net/url"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"git.sxidc.com/go-framework/baize/framework/core/infrastructure/logger"
+	"git.sxidc.com/go-tools/utils/strutils"
+	"github.com/gin-gonic/gin"
 )
 
 // Handler 请求处理函数
@@ -30,11 +39,43 @@ type Router interface {
 
 	// BasePath 路由的基础路径,即去掉http://ip:port之后的URL部分(不包括AddRoute的RelativePath)
 	BasePath() string
+
+	// DumpPermissionItems 获取权限条目
+	DumpPermissionItems() []PermissionItem
+}
+
+type PermissionItem struct {
+	Group              string `json:"group"`
+	Name               string `json:"name"`
+	Description        string `json:"description"`
+	Resource           string `json:"resource"`
+	Action             string `json:"action"`
+	NeedCheckExpire    bool   `json:"needCheckExpire"`
+	SensitiveWordScene int    `json:"sensitiveWordScene"`
+}
+
+func (p *PermissionItem) check() error {
+	if strutils.IsStringEmpty(p.Name) {
+		return errors.New("没有权限条目名称")
+	}
+
+	if strutils.IsStringEmpty(p.Resource) {
+		return errors.New("没有权限条目resource")
+	}
+
+	if strutils.IsStringEmpty(p.Action) {
+		return errors.New("没有权限条目action")
+	}
+
+	return nil
 }
 
 type RootRouter struct {
 	engine    *gin.Engine
 	versioned map[string]*PrefixRouter
+
+	// 由于gin的router的保证,不会有重复的条目
+	permissionItems []PermissionItem
 }
 
 func newRootRouter(engine *gin.Engine) *RootRouter {
@@ -72,8 +113,22 @@ func (r *RootRouter) VersionedRouter(version string) Router {
 	return r.versioned[version]
 }
 
-func (r *RootRouter) AddRoute(method string, relativePath string, handlers ...Handler) Router {
+func (r *RootRouter) DumpPermissionItems() []PermissionItem {
+	return r.permissionItems
+}
+
+func (r *RootRouter) AddRoute(method string, relativePathPattern string, handlers ...Handler) Router {
+	relativePath, permissionItem, err := parseRelativePathPerm(r.engine.BasePath(), relativePathPattern, method)
+	if err != nil {
+		panic(err)
+	}
+
 	r.engine.Handle(method, relativePath, transferHandlers(handlers...)...)
+
+	if permissionItem != nil {
+		r.permissionItems = append(r.permissionItems, *permissionItem)
+	}
+
 	return r
 }
 
@@ -84,6 +139,9 @@ func (r *RootRouter) ServerHttp(w http.ResponseWriter, req *http.Request) {
 type PrefixRouter struct {
 	groupRouter *gin.RouterGroup
 	versioned   map[string]*PrefixRouter
+
+	// 由于gin的router的保证,不会有重复的条目
+	permissionItems []PermissionItem
 }
 
 func newPrefixRouter(group *gin.RouterGroup) *PrefixRouter {
@@ -117,8 +175,18 @@ func (r *PrefixRouter) VersionedRouter(version string) Router {
 	return r.versioned[version]
 }
 
-func (r *PrefixRouter) AddRoute(method string, relativePath string, handlers ...Handler) Router {
+func (r *PrefixRouter) AddRoute(method string, relativePathPattern string, handlers ...Handler) Router {
+	relativePath, permissionItem, err := parseRelativePathPerm(r.groupRouter.BasePath(), relativePathPattern, method)
+	if err != nil {
+		panic(err)
+	}
+
 	r.groupRouter.Handle(method, relativePath, transferHandlers(handlers...)...)
+
+	if permissionItem != nil {
+		r.permissionItems = append(r.permissionItems, *permissionItem)
+	}
+
 	return r
 }
 
@@ -134,6 +202,10 @@ func (r *PrefixRouter) BasePath() string {
 	return r.groupRouter.BasePath()
 }
 
+func (r *PrefixRouter) DumpPermissionItems() []PermissionItem {
+	return r.permissionItems
+}
+
 func transferHandlers(handlers ...Handler) []gin.HandlerFunc {
 	ginHandlers := make([]gin.HandlerFunc, 0)
 	for _, handler := range handlers {
@@ -145,3 +217,93 @@ func transferHandlers(handlers ...Handler) []gin.HandlerFunc {
 
 	return ginHandlers
 }
+
+const (
+	tagPartSeparator         = ";"
+	tagPartKeyValueSeparator = ":"
+)
+
+const (
+	permTagKey                    = "perm"
+	permTagPartGroup              = "group"
+	permTagPartName               = "name"
+	permTagPartDescription        = "description"
+	permTagPartNeedCheckExpire    = "needCheckExpire"
+	permTagPartSensitiveWordScene = "sensitiveWordScene"
+)
+
+func parseRelativePathPerm(basePath string, relativePathPattern string, method string) (string, *PermissionItem, error) {
+	re := regexp.MustCompile(`\$\{(.+)\}\$`)
+	matches := re.FindStringSubmatch(relativePathPattern)
+
+	if len(matches) == 1 {
+		return relativePathPattern, nil, nil
+	}
+
+	relativePath := re.ReplaceAllString(relativePathPattern, "")
+	permTag, ok := reflect.StructTag(matches[1]).Lookup(permTagKey)
+	if !ok {
+		return relativePath, nil, nil
+	}
+
+	tagParts := strings.Split(permTag, tagPartSeparator)
+	if tagParts == nil || len(tagParts) == 0 {
+		return relativePath, nil, nil
+	}
+
+	fullPath, err := url.JoinPath(basePath, relativePath)
+	if err != nil {
+		return "", nil, err
+	}
+
+	permissionItem := &PermissionItem{
+		Group:              "",
+		Name:               "",
+		Description:        "",
+		Resource:           fullPath,
+		Action:             method,
+		NeedCheckExpire:    false,
+		SensitiveWordScene: 0,
+	}
+
+	for _, tagPart := range tagParts {
+		tagPartKeyValue := strings.SplitN(strings.TrimSpace(tagPart), tagPartKeyValueSeparator, 2)
+
+		if strutils.IsStringEmpty(tagPartKeyValue[0]) {
+			continue
+		}
+
+		switch tagPartKeyValue[0] {
+		case permTagPartGroup:
+			permissionItem.Group = tagPartKeyValue[1]
+		case permTagPartName:
+			permissionItem.Name = tagPartKeyValue[1]
+		case permTagPartDescription:
+			permissionItem.Description = tagPartKeyValue[1]
+		case permTagPartNeedCheckExpire:
+			permissionItem.NeedCheckExpire = true
+		case permTagPartSensitiveWordScene:
+			scene, err := strconv.Atoi(tagPartKeyValue[1])
+			if err != nil {
+				return relativePath, nil, err
+			}
+
+			if scene < 1 || scene > 4 {
+				return relativePath, nil, errors.New("不支持的敏感词场景: " + tagPartKeyValue[1])
+			}
+
+			permissionItem.SensitiveWordScene = scene
+		default:
+			err := errors.New(permTagKey + "不支持的tag: " + tagPartKeyValue[0])
+			logger.GetInstance().Error(err)
+			continue
+		}
+	}
+
+	err = permissionItem.check()
+	if err != nil {
+		return "", nil, err
+	}
+
+	return relativePath, permissionItem, nil
+}

+ 10 - 4
framework/core/application/config.go

@@ -2,11 +2,12 @@ package application
 
 import (
 	"encoding/json"
+	"os"
+
 	"git.sxidc.com/go-framework/baize/framework/core/infrastructure"
 	"git.sxidc.com/go-tools/utils/fileutils"
 	"github.com/pkg/errors"
 	"gopkg.in/yaml.v3"
-	"os"
 )
 
 type Config struct {
@@ -16,9 +17,14 @@ type Config struct {
 }
 
 type ApiConfig struct {
-	UrlPrefix    string   `json:"url_prefix" yaml:"url_prefix"`
-	Port         string   `json:"port" yaml:"port"`
-	LogSkipPaths []string `json:"log_skip_paths" yaml:"log_skip_paths"`
+	UrlPrefix          string   `json:"url_prefix" yaml:"url_prefix"`
+	Port               string   `json:"port" yaml:"port"`
+	LogSkipPaths       []string `json:"log_skip_paths" yaml:"log_skip_paths"`
+	DumpPermissionItem struct {
+		Namespace string `json:"namespace" yaml:"namespace"`
+		Gateway   string `json:"gateway" yaml:"gateway"`
+		SaveDir   string `json:"save_dir" yaml:"save_dir"`
+	} `json:"dump_permission_item" yaml:"dump_permission_item"`
 }
 
 type InfrastructureConfig struct {

+ 1 - 2
go.sum

@@ -281,13 +281,12 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
 golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
 golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-google.golang.org/genproto v0.0.0-20240318140521-94a12d6c2237/go.mod h1:9sVD8c25Af3p0rGs7S7LLsxWKFiJt/65LdSyqXBkX/Y=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
 google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
 google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=