You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2023/03/22 08:31:07 UTC

[dubbo-admin] branch refactor-with-go updated: feat: refactor the tag route feature with go (#1030)

This is an automated email from the ASF dual-hosted git repository.

liujun pushed a commit to branch refactor-with-go
in repository https://gitbox.apache.org/repos/asf/dubbo-admin.git


The following commit(s) were added to refs/heads/refactor-with-go by this push:
     new 6f1e816e feat: refactor the tag route feature with go (#1030)
6f1e816e is described below

commit 6f1e816ef0649ff7b610c6264e389cf5a4628adc
Author: Yixiang Zhao <se...@foxmail.com>
AuthorDate: Wed Mar 22 16:31:01 2023 +0800

    feat: refactor the tag route feature with go (#1030)
---
 go.mod                                             |   4 +-
 go.sum                                             |   7 +-
 pkg/admin/constant/const.go                        |   4 +
 pkg/admin/handlers/overrides.go                    |   6 +-
 pkg/admin/handlers/tag_route.go                    | 168 +++++++++++++++++++++
 .../tag_route.go}                                  |  48 +++---
 pkg/admin/router/router.go                         |  11 ++
 pkg/admin/services/override_service_impl.go        |   4 +-
 pkg/admin/services/override_service_impl_test.go   |   2 +-
 .../tag_route_service.go}                          |  38 ++---
 pkg/admin/services/tag_route_service_impl.go       | 125 +++++++++++++++
 pkg/admin/util/base_service_metadata.go            |  36 ++---
 .../{base_service_metadata.go => yaml_parser.go}   |  35 ++---
 ...ase_service_metadata.go => yaml_parser_test.go} |  60 +++++---
 14 files changed, 408 insertions(+), 140 deletions(-)

diff --git a/go.mod b/go.mod
index 26fbe51f..2f43008a 100644
--- a/go.mod
+++ b/go.mod
@@ -64,7 +64,7 @@ require (
 	github.com/go-kit/log v0.1.0 // indirect
 	github.com/go-logfmt/logfmt v0.5.0 // indirect
 	github.com/go-logr/logr v1.2.3 // indirect
-	github.com/go-ole/go-ole v1.2.4 // indirect
+	github.com/go-ole/go-ole v1.2.6 // indirect
 	github.com/go-openapi/jsonpointer v0.19.5 // indirect
 	github.com/go-openapi/jsonreference v0.20.0 // indirect
 	github.com/go-openapi/swag v0.19.14 // indirect
@@ -100,7 +100,7 @@ require (
 	github.com/modern-go/reflect2 v1.0.2 // indirect
 	github.com/mschoch/smat v0.2.0 // indirect
 	github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
-	github.com/nacos-group/nacos-sdk-go v1.1.1 // indirect
+	github.com/nacos-group/nacos-sdk-go v1.1.3 // indirect
 	github.com/natefinch/lumberjack v2.0.0+incompatible // indirect
 	github.com/pelletier/go-toml v1.7.0 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.6 // indirect
diff --git a/go.sum b/go.sum
index 5528ead8..95e56037 100644
--- a/go.sum
+++ b/go.sum
@@ -235,8 +235,9 @@ github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV
 github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
 github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
+github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
+github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
 github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
 github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -568,8 +569,9 @@ github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRW
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/nacos-group/nacos-sdk-go v1.0.8/go.mod h1:hlAPn3UdzlxIlSILAyOXKxjFSvDJ9oLzTJ9hLAK1KzA=
-github.com/nacos-group/nacos-sdk-go v1.1.1 h1:beczWcOoTaVBMgCgikqvZflrN5Xbw7pWAWpxl+VJGIA=
 github.com/nacos-group/nacos-sdk-go v1.1.1/go.mod h1:UHOtQNQY/qpk2dhg6gDq8u5+/CEIc3+lWmrmxEzX0/g=
+github.com/nacos-group/nacos-sdk-go v1.1.3 h1:xNlSC9li2A11ifTA8HCqgM6NRImGUJA4X+gGK5muJuQ=
+github.com/nacos-group/nacos-sdk-go v1.1.3/go.mod h1:cBv9wy5iObs7khOqov1ERFQrCuTR4ILpgaiaVMxEmGI=
 github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
 github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
@@ -969,6 +971,7 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
diff --git a/pkg/admin/constant/const.go b/pkg/admin/constant/const.go
index b4832a6d..bc0c15a9 100644
--- a/pkg/admin/constant/const.go
+++ b/pkg/admin/constant/const.go
@@ -65,6 +65,10 @@ const (
 	IP                     = "ip"
 	PlusSigns              = "+"
 	PunctuationPoint       = "."
+	ConditionRoute         = "condition_route"
+	TagRoute               = "tag_route"
+	ConditionRuleSuffix    = ".condition-router"
+	TagRuleSuffix          = ".tag-router"
 )
 
 var Configs = set.NewSet(WeightKey, BalancingKey)
diff --git a/pkg/admin/handlers/overrides.go b/pkg/admin/handlers/overrides.go
index b278c928..a0b482d6 100644
--- a/pkg/admin/handlers/overrides.go
+++ b/pkg/admin/handlers/overrides.go
@@ -88,7 +88,11 @@ func SearchOverride(c *gin.Context) {
 	result := make([]*model.DynamicConfig, 0)
 	var err error
 	if service != "" {
-		id := util.BuildServiceKey(service, serviceGroup, serviceVersion)
+		id := util.BuildServiceKey(model.Base{
+			Service:        service,
+			ServiceGroup:   serviceGroup,
+			ServiceVersion: serviceVersion,
+		})
 		override, err = overrideServiceImpl.FindOverride(id)
 		if err != nil {
 			c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
diff --git a/pkg/admin/handlers/tag_route.go b/pkg/admin/handlers/tag_route.go
new file mode 100644
index 00000000..baa17d06
--- /dev/null
+++ b/pkg/admin/handlers/tag_route.go
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package handlers
+
+import (
+	"net/http"
+	"strings"
+
+	"github.com/apache/dubbo-admin/pkg/admin/config"
+	"github.com/apache/dubbo-admin/pkg/admin/model"
+	"github.com/apache/dubbo-admin/pkg/admin/services"
+	"github.com/gin-gonic/gin"
+)
+
+var tagRouteService services.TagRoutesService = &services.TagRoutesServiceImpl{
+	GovernanceConfig: &config.GovernanceConfigImpl{},
+}
+
+func CreateRule(c *gin.Context) {
+	var tagRouteDto model.TagRouteDto
+	err := c.BindJSON(&tagRouteDto)
+	if err != nil {
+		panic(err)
+	}
+
+	err = tagRouteService.CreateTagRoute(tagRouteDto)
+
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": "success",
+	})
+}
+
+func UpdateRule(c *gin.Context) {
+	var tagRouteDto model.TagRouteDto
+	err := c.BindJSON(&tagRouteDto)
+	if err != nil {
+		panic(err)
+	}
+	id := c.Param("id")
+	id = strings.ReplaceAll(id, "*", "/")
+
+	_, err = tagRouteService.FindTagRoute(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+
+	err = tagRouteService.UpdateTagRoute(tagRouteDto)
+
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": "success",
+	})
+}
+
+func SearchRoutes(c *gin.Context) {
+	application := c.Query("application")
+
+	tagRoute, err := tagRouteService.FindTagRoute(application)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": []model.TagRouteDto{tagRoute},
+	})
+}
+
+func DetailRoute(c *gin.Context) {
+	id := c.Param("id")
+	id = strings.ReplaceAll(id, "*", "/")
+
+	tagRoute, err := tagRouteService.FindTagRoute(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": tagRoute,
+	})
+}
+
+func DeleteRoute(c *gin.Context) {
+	id := c.Param("id")
+	id = strings.ReplaceAll(id, "*", "/")
+
+	err := tagRouteService.DeleteTagRoute(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": "success",
+	})
+}
+
+func EnableRoute(c *gin.Context) {
+	id := c.Param("id")
+	id = strings.ReplaceAll(id, "*", "/")
+
+	err := tagRouteService.EnableTagRoute(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": "success",
+	})
+}
+
+func DisableRoute(c *gin.Context) {
+	id := c.Param("id")
+	id = strings.ReplaceAll(id, "*", "/")
+
+	err := tagRouteService.DisableTagRoute(id)
+	if err != nil {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"error": err.Error(),
+		})
+		return
+	}
+	c.JSON(http.StatusOK, gin.H{
+		"code": 1,
+		"data": "success",
+	})
+}
diff --git a/pkg/admin/util/base_service_metadata.go b/pkg/admin/model/tag_route.go
similarity index 52%
copy from pkg/admin/util/base_service_metadata.go
copy to pkg/admin/model/tag_route.go
index 794d78cb..69df9c0e 100644
--- a/pkg/admin/util/base_service_metadata.go
+++ b/pkg/admin/model/tag_route.go
@@ -15,35 +15,31 @@
  * limitations under the License.
  */
 
-package util
+package model
 
-import "strings"
+type TagRouteDto struct {
+	Base
 
-func BuildServiceKey(path, group, version string) string {
-	var length int
-	if path != "" {
-		length += len(path)
-	}
-	if group != "" {
-		length += len(group)
-	}
-	if version != "" {
-		length += len(version)
-	}
-	length += 2
+	Tags []Tag `json:"tags" binding:"required"`
 
-	var buf strings.Builder
-	buf.Grow(length)
+	Priority      int    `json:"priority"`
+	Enabled       bool   `json:"enabled" binding:"required"`
+	Force         bool   `json:"force"`
+	Runtime       bool   `json:"runtime"`
+	ConfigVersion string `json:"configVersion" binding:"required"`
+}
 
-	if group != "" {
-		buf.WriteString(group)
-		buf.WriteString("/")
-	}
-	buf.WriteString(path)
-	if version != "" {
-		buf.WriteString(":")
-		buf.WriteString(version)
-	}
+type TagRoute struct {
+	Priority int    `json:"priority"`
+	Enabled  bool   `json:"enabled"`
+	Force    bool   `json:"force"`
+	Runtime  bool   `json:"runtime"`
+	Key      string `json:"key"`
+	Tags     []Tag  `json:"tags"`
+}
 
-	return buf.String()
+type Tag struct {
+	Name      string       `json:"name" binding:"required"`
+	Match     []ParamMatch `json:"match" binding:"required"`
+	Addresses []string     `json:"addresses"`
 }
diff --git a/pkg/admin/router/router.go b/pkg/admin/router/router.go
index ad948a84..f370b749 100644
--- a/pkg/admin/router/router.go
+++ b/pkg/admin/router/router.go
@@ -45,5 +45,16 @@ func InitRouter() *gin.Engine {
 		override.PUT("/:id", handlers.UpdateOverride)
 	}
 
+	tagRoute := router.Group("/api/:env/rules/route/tag")
+	{
+		tagRoute.POST("/", handlers.CreateRule)
+		tagRoute.PUT("/:id", handlers.UpdateRule)
+		tagRoute.GET("/", handlers.SearchRoutes)
+		tagRoute.GET("/:id", handlers.DetailRoute)
+		tagRoute.DELETE("/:id", handlers.DeleteRoute)
+		tagRoute.PUT("/enable/:id", handlers.EnableRoute)
+		tagRoute.PUT("/disable/:id", handlers.DisableRoute)
+	}
+
 	return router
 }
diff --git a/pkg/admin/services/override_service_impl.go b/pkg/admin/services/override_service_impl.go
index b8fb13f5..a08b85be 100644
--- a/pkg/admin/services/override_service_impl.go
+++ b/pkg/admin/services/override_service_impl.go
@@ -31,7 +31,7 @@ type OverrideServiceImpl struct {
 }
 
 func (s *OverrideServiceImpl) SaveOverride(dynamicConfig *model.DynamicConfig) error {
-	key := util.BuildServiceKey(dynamicConfig.Service, dynamicConfig.ServiceVersion, dynamicConfig.ServiceGroup)
+	key := util.BuildServiceKey(dynamicConfig.Base)
 	path := getPath(key)
 	existConfig, err := s.GovernanceConfig.GetConfig(path)
 	if err != nil {
@@ -95,7 +95,7 @@ func getPath(key string) string {
 }
 
 func (s *OverrideServiceImpl) UpdateOverride(update *model.DynamicConfig) error {
-	key := util.BuildServiceKey(update.Service, update.ServiceGroup, update.ServiceVersion)
+	key := util.BuildServiceKey(update.Base)
 	path := getPath(key)
 	existConfig, err := s.GovernanceConfig.GetConfig(path)
 	if err != nil {
diff --git a/pkg/admin/services/override_service_impl_test.go b/pkg/admin/services/override_service_impl_test.go
index a3b2446b..9f45b81e 100644
--- a/pkg/admin/services/override_service_impl_test.go
+++ b/pkg/admin/services/override_service_impl_test.go
@@ -85,7 +85,7 @@ func TestOverrideServiceImpl_UpdateOverride(t *testing.T) {
 	ctrl := gomock.NewController(t)
 	mockGovernanceConfig := config.NewMockGovernanceConfig(ctrl)
 	mockGovernanceConfig.EXPECT().SetConfig(gomock.Any(), gomock.Any()).Return(nil)
-	mockGovernanceConfig.EXPECT().GetConfig(getPath("testGroup/testService:testVersion")).Return("configVersion: v2.7\nconfigs:\n- addresses:\n  - 0.0.0.0\n  enabled: false\n  parameters:\n    timeout: 6000\n  side: consumer\nenabled: true\nkey: testService\nscope: service\n", nil)
+	mockGovernanceConfig.EXPECT().GetConfig(getPath("testService:testVersion:testGroup")).Return("configVersion: v2.7\nconfigs:\n- addresses:\n  - 0.0.0.0\n  enabled: false\n  parameters:\n    timeout: 6000\n  side: consumer\nenabled: true\nkey: testService\nscope: service\n", nil)
 	mockGovernanceConfig.EXPECT().Register(gomock.Any()).Return(nil)
 	mockGovernanceConfig.EXPECT().UnRegister(gomock.Any()).Return(nil)
 
diff --git a/pkg/admin/util/base_service_metadata.go b/pkg/admin/services/tag_route_service.go
similarity index 62%
copy from pkg/admin/util/base_service_metadata.go
copy to pkg/admin/services/tag_route_service.go
index 794d78cb..d9facbd4 100644
--- a/pkg/admin/util/base_service_metadata.go
+++ b/pkg/admin/services/tag_route_service.go
@@ -15,35 +15,15 @@
  * limitations under the License.
  */
 
-package util
+package services
 
-import "strings"
+import "github.com/apache/dubbo-admin/pkg/admin/model"
 
-func BuildServiceKey(path, group, version string) string {
-	var length int
-	if path != "" {
-		length += len(path)
-	}
-	if group != "" {
-		length += len(group)
-	}
-	if version != "" {
-		length += len(version)
-	}
-	length += 2
-
-	var buf strings.Builder
-	buf.Grow(length)
-
-	if group != "" {
-		buf.WriteString(group)
-		buf.WriteString("/")
-	}
-	buf.WriteString(path)
-	if version != "" {
-		buf.WriteString(":")
-		buf.WriteString(version)
-	}
-
-	return buf.String()
+type TagRoutesService interface {
+	CreateTagRoute(model.TagRouteDto) error
+	UpdateTagRoute(model.TagRouteDto) error
+	DeleteTagRoute(string) error
+	FindTagRoute(string) (model.TagRouteDto, error)
+	EnableTagRoute(string) error
+	DisableTagRoute(string) error
 }
diff --git a/pkg/admin/services/tag_route_service_impl.go b/pkg/admin/services/tag_route_service_impl.go
new file mode 100644
index 00000000..54feb717
--- /dev/null
+++ b/pkg/admin/services/tag_route_service_impl.go
@@ -0,0 +1,125 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package services
+
+import (
+	"fmt"
+	"strings"
+
+	"github.com/apache/dubbo-admin/pkg/admin/config"
+	"github.com/apache/dubbo-admin/pkg/admin/constant"
+	"github.com/apache/dubbo-admin/pkg/admin/model"
+	"github.com/apache/dubbo-admin/pkg/admin/util"
+)
+
+type TagRoutesServiceImpl struct {
+	GovernanceConfig config.GovernanceConfig
+}
+
+func (t *TagRoutesServiceImpl) CreateTagRoute(tagRoute model.TagRouteDto) error {
+	id := util.BuildServiceKey(tagRoute.Base)
+	path := getTagRoutePath(id, constant.TagRoute)
+	store := convertTagRouteToStore(tagRoute)
+	obj, _ := util.DumpObject(store)
+	return t.GovernanceConfig.SetConfig(path, obj)
+}
+
+func (t *TagRoutesServiceImpl) UpdateTagRoute(tagRoute model.TagRouteDto) error {
+	id := util.BuildServiceKey(tagRoute.Base)
+	path := getTagRoutePath(id, constant.TagRoute)
+	cfg, _ := t.GovernanceConfig.GetConfig(path)
+	if cfg == "" {
+		return fmt.Errorf("tag route %s not found", id)
+	}
+	store := convertTagRouteToStore(tagRoute)
+	obj, _ := util.DumpObject(store)
+	return t.GovernanceConfig.SetConfig(path, obj)
+}
+
+func (t *TagRoutesServiceImpl) DeleteTagRoute(id string) error {
+	path := getTagRoutePath(id, constant.TagRoute)
+	return t.GovernanceConfig.DeleteConfig(path)
+}
+
+func (t *TagRoutesServiceImpl) FindTagRoute(id string) (model.TagRouteDto, error) {
+	path := getTagRoutePath(id, constant.TagRoute)
+	cfg, err := t.GovernanceConfig.GetConfig(path)
+	if cfg != "" {
+		var tagRoute model.TagRoute
+		_ = util.LoadObject(cfg, &tagRoute)
+		return convertTagRouteToDto(tagRoute), nil
+	}
+	return model.TagRouteDto{}, err
+}
+
+func (t *TagRoutesServiceImpl) EnableTagRoute(id string) error {
+	path := getTagRoutePath(id, constant.TagRoute)
+	cfg, err := t.GovernanceConfig.GetConfig(path)
+	if cfg != "" {
+		var tagRoute model.TagRoute
+		_ = util.LoadObject(cfg, &tagRoute)
+		tagRoute.Enabled = true
+		obj, _ := util.DumpObject(tagRoute)
+		return t.GovernanceConfig.SetConfig(path, obj)
+	}
+	return err
+}
+
+func (t *TagRoutesServiceImpl) DisableTagRoute(id string) error {
+	path := getTagRoutePath(id, constant.TagRoute)
+	cfg, err := t.GovernanceConfig.GetConfig(path)
+	if cfg != "" {
+		var tagRoute model.TagRoute
+		_ = util.LoadObject(cfg, &tagRoute)
+		tagRoute.Enabled = false
+		obj, _ := util.DumpObject(tagRoute)
+		return t.GovernanceConfig.SetConfig(path, obj)
+	}
+	return err
+}
+
+func getTagRoutePath(key string, routeType string) string {
+	key = strings.ReplaceAll(key, "*", "/")
+	if routeType == constant.ConditionRoute {
+		return key + constant.ConditionRuleSuffix
+	} else {
+		return key + constant.TagRuleSuffix
+	}
+}
+
+func convertTagRouteToStore(tagRoute model.TagRouteDto) model.TagRoute {
+	var store model.TagRoute
+	store.Key = tagRoute.Application
+	store.Enabled = tagRoute.Enabled
+	store.Force = tagRoute.Force
+	store.Priority = tagRoute.Priority
+	store.Runtime = tagRoute.Runtime
+	store.Tags = tagRoute.Tags
+	return store
+}
+
+func convertTagRouteToDto(tagRoute model.TagRoute) model.TagRouteDto {
+	var dto model.TagRouteDto
+	dto.Application = tagRoute.Key
+	dto.Enabled = tagRoute.Enabled
+	dto.Force = tagRoute.Force
+	dto.Priority = tagRoute.Priority
+	dto.Runtime = tagRoute.Runtime
+	dto.Tags = tagRoute.Tags
+	return dto
+}
diff --git a/pkg/admin/util/base_service_metadata.go b/pkg/admin/util/base_service_metadata.go
index 794d78cb..3bfb4f3f 100644
--- a/pkg/admin/util/base_service_metadata.go
+++ b/pkg/admin/util/base_service_metadata.go
@@ -17,33 +17,15 @@
 
 package util
 
-import "strings"
+import (
+	"github.com/apache/dubbo-admin/pkg/admin/constant"
+	"github.com/apache/dubbo-admin/pkg/admin/model"
+)
 
-func BuildServiceKey(path, group, version string) string {
-	var length int
-	if path != "" {
-		length += len(path)
+func BuildServiceKey(baseDto model.Base) string {
+	if baseDto.Application != "" {
+		return baseDto.Application
 	}
-	if group != "" {
-		length += len(group)
-	}
-	if version != "" {
-		length += len(version)
-	}
-	length += 2
-
-	var buf strings.Builder
-	buf.Grow(length)
-
-	if group != "" {
-		buf.WriteString(group)
-		buf.WriteString("/")
-	}
-	buf.WriteString(path)
-	if version != "" {
-		buf.WriteString(":")
-		buf.WriteString(version)
-	}
-
-	return buf.String()
+	// id format: "${class}:${version}:${group}"
+	return baseDto.Service + constant.Colon + baseDto.ServiceVersion + constant.Colon + baseDto.ServiceGroup
 }
diff --git a/pkg/admin/util/base_service_metadata.go b/pkg/admin/util/yaml_parser.go
similarity index 63%
copy from pkg/admin/util/base_service_metadata.go
copy to pkg/admin/util/yaml_parser.go
index 794d78cb..da4dadf5 100644
--- a/pkg/admin/util/base_service_metadata.go
+++ b/pkg/admin/util/yaml_parser.go
@@ -17,33 +17,16 @@
 
 package util
 
-import "strings"
+import "dubbo.apache.org/dubbo-go/v3/common/yaml"
 
-func BuildServiceKey(path, group, version string) string {
-	var length int
-	if path != "" {
-		length += len(path)
-	}
-	if group != "" {
-		length += len(group)
-	}
-	if version != "" {
-		length += len(version)
-	}
-	length += 2
-
-	var buf strings.Builder
-	buf.Grow(length)
-
-	if group != "" {
-		buf.WriteString(group)
-		buf.WriteString("/")
-	}
-	buf.WriteString(path)
-	if version != "" {
-		buf.WriteString(":")
-		buf.WriteString(version)
+func DumpObject(obj interface{}) (string, error) {
+	bytes, err := yaml.MarshalYML(obj)
+	if err != nil {
+		return "", err
 	}
+	return string(bytes), nil
+}
 
-	return buf.String()
+func LoadObject(content string, obj interface{}) error {
+	return yaml.UnmarshalYML([]byte(content), obj)
 }
diff --git a/pkg/admin/util/base_service_metadata.go b/pkg/admin/util/yaml_parser_test.go
similarity index 57%
copy from pkg/admin/util/base_service_metadata.go
copy to pkg/admin/util/yaml_parser_test.go
index 794d78cb..a9291ecd 100644
--- a/pkg/admin/util/base_service_metadata.go
+++ b/pkg/admin/util/yaml_parser_test.go
@@ -17,33 +17,45 @@
 
 package util
 
-import "strings"
+import (
+	"testing"
+)
 
-func BuildServiceKey(path, group, version string) string {
-	var length int
-	if path != "" {
-		length += len(path)
-	}
-	if group != "" {
-		length += len(group)
-	}
-	if version != "" {
-		length += len(version)
-	}
-	length += 2
-
-	var buf strings.Builder
-	buf.Grow(length)
+type TagRoute struct {
+	Priority int
+	Enable   bool
+	Force    bool
+	Runtime  bool
+	Key      string
+}
 
-	if group != "" {
-		buf.WriteString(group)
-		buf.WriteString("/")
+func TestDumpObject(t *testing.T) {
+	tagRoute := TagRoute{
+		Priority: 1,
+		Enable:   true,
+		Force:    true,
+		Runtime:  true,
 	}
-	buf.WriteString(path)
-	if version != "" {
-		buf.WriteString(":")
-		buf.WriteString(version)
+	str, err := DumpObject(tagRoute)
+	if err != nil {
+		t.Fatal(err)
 	}
+	t.Log(str)
+}
 
-	return buf.String()
+func TestLoadObject(t *testing.T) {
+	str := `configVersion: v3.0
+force: true
+enabled: true
+key: shop-detail
+tags:
+  - name: gray
+    match:
+      - key: env
+        value:
+          exact: gray
+`
+	var tagRoute TagRoute
+	LoadObject(str, &tagRoute)
+	println(tagRoute.Key)
 }