You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@shenyu.apache.org by ma...@apache.org on 2023/05/09 03:07:51 UTC

[shenyu] branch master updated: discovery upstream and proxy selector proxy crud (#4621)

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

mahaitao pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new 709a50da2 discovery upstream and proxy selector proxy crud (#4621)
709a50da2 is described below

commit 709a50da28ac69937af3e826157aa3870ea62a3b
Author: huanccwang <Wa...@outlook.com>
AuthorDate: Tue May 9 11:07:40 2023 +0800

    discovery upstream and proxy selector proxy crud (#4621)
---
 .../controller/DiscoveryUpstreamController.java    |  91 +++++
 .../admin/controller/ProxySelectorController.java  | 115 ++++++
 .../admin/mapper/DiscoveryUpstreamMapper.java      |  71 ++++
 .../shenyu/admin/mapper/ProxySelectorMapper.java   |  88 +++++
 .../admin/model/dto/DiscoveryUpstreamDTO.java      | 214 +++++++++++
 .../shenyu/admin/model/dto/ProxySelectorDTO.java   | 177 +++++++++
 .../admin/model/entity/DiscoveryUpstreamDO.java    | 427 +++++++++++++++++++++
 .../shenyu/admin/model/entity/ProxySelectorDO.java | 363 ++++++++++++++++++
 .../admin/model/query/ProxySelectorQuery.java      |  79 ++++
 .../shenyu/admin/model/vo/ProxySelectorVO.java     | 197 ++++++++++
 .../admin/service/DiscoveryUpstreamService.java    |  41 ++
 .../shenyu/admin/service/ProxySelectorService.java |  52 +++
 .../service/impl/DiscoveryUpstreamServiceImpl.java |  91 +++++
 .../service/impl/ProxySelectorServiceImpl.java     | 122 ++++++
 .../mappers/discovery-upstream-sqlmap.xml          | 102 +++++
 .../resources/mappers/proxy-selector-sqlmap.xml    | 118 ++++++
 .../src/main/resources/sql-script/h2/schema.sql    |  48 +--
 .../admin/mapper/DiscoveryUpstreamMapperTest.java  |  98 +++++
 .../admin/mapper/ProxySelectorMapperTest.java      | 117 ++++++
 .../service/DiscoveryUpstreamServiceTest.java      |  81 ++++
 .../admin/service/ProxySelectorServiceTest.java    | 101 +++++
 .../shenyu/common/constant/AdminConstants.java     |   3 +
 22 files changed, 2772 insertions(+), 24 deletions(-)

diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DiscoveryUpstreamController.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DiscoveryUpstreamController.java
new file mode 100644
index 000000000..bf2a3d08a
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/DiscoveryUpstreamController.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.apache.shenyu.admin.controller;
+
+import org.apache.shenyu.admin.mapper.PluginMapper;
+import org.apache.shenyu.admin.model.dto.DiscoveryUpstreamDTO;
+import org.apache.shenyu.admin.model.result.ShenyuAdminResult;
+import org.apache.shenyu.admin.service.DiscoveryUpstreamService;
+import org.apache.shenyu.admin.validation.annotation.Existed;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+@RestController
+@RequestMapping("/discovery-upstream")
+@Validated
+public class DiscoveryUpstreamController {
+
+    private final DiscoveryUpstreamService discoveryUpstreamService;
+
+    public DiscoveryUpstreamController(final DiscoveryUpstreamService discoveryUpstreamService) {
+
+        this.discoveryUpstreamService = discoveryUpstreamService;
+    }
+
+    /**
+     * create discovery upstream.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @PostMapping("")
+    public ShenyuAdminResult createDiscoveryUpstream(@Valid @RequestBody final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        return ShenyuAdminResult.success(discoveryUpstreamService.createOrUpdate(discoveryUpstreamDTO));
+    }
+
+    /**
+     * update discovery upstream.
+     *
+     * @param id                   id
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @PutMapping("/{id}")
+    public ShenyuAdminResult updateDiscoveryUpstream(@PathVariable("id")
+                                                     @Existed(message = "discovery upstream is not existed",
+                                                             provider = PluginMapper.class) final String id,
+                                                     @Valid @RequestBody final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        discoveryUpstreamDTO.setId(id);
+        return ShenyuAdminResult.success(discoveryUpstreamService.createOrUpdate(discoveryUpstreamDTO));
+    }
+
+    /**
+     * batch delete.
+     *
+     * @param ids id list
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @DeleteMapping("/batch")
+    public ShenyuAdminResult deleteDiscoveryUpstream(@NotEmpty @RequestBody final List<@NotBlank String> ids) {
+
+        return ShenyuAdminResult.success(discoveryUpstreamService.delete(ids));
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/ProxySelectorController.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/ProxySelectorController.java
new file mode 100644
index 000000000..78491b9aa
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/controller/ProxySelectorController.java
@@ -0,0 +1,115 @@
+/*
+ * 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 org.apache.shenyu.admin.controller;
+
+import org.apache.shenyu.admin.mapper.PluginMapper;
+import org.apache.shenyu.admin.model.dto.ProxySelectorDTO;
+import org.apache.shenyu.admin.model.page.CommonPager;
+import org.apache.shenyu.admin.model.page.PageParameter;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.admin.model.result.ShenyuAdminResult;
+import org.apache.shenyu.admin.model.vo.ProxySelectorVO;
+import org.apache.shenyu.admin.service.ProxySelectorService;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.apache.shenyu.admin.validation.annotation.Existed;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.validation.Valid;
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.util.List;
+
+@Validated
+@RestController
+@RequestMapping("/proxy-selector")
+public class ProxySelectorController {
+
+    private final ProxySelectorService proxySelectorService;
+
+    public ProxySelectorController(final ProxySelectorService proxySelectorService) {
+
+        this.proxySelectorService = proxySelectorService;
+    }
+
+    /**
+     * page proxy selector.
+     *
+     * @param name        name
+     * @param currentPage currentPage
+     * @param pageSize    pageSize
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @GetMapping("")
+    public ShenyuAdminResult queryProxySelector(final String name, @NotNull final Integer currentPage,
+                                                @NotNull final Integer pageSize) {
+
+        CommonPager<ProxySelectorVO> commonPager = proxySelectorService
+                .listByPage(new ProxySelectorQuery(name, new PageParameter(currentPage, pageSize)));
+        return ShenyuAdminResult.success(ShenyuResultMessage.QUERY_SUCCESS, commonPager);
+    }
+
+    /**
+     * create proxy selector.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @PostMapping("")
+    public ShenyuAdminResult createProxySelector(@Valid @RequestBody final ProxySelectorDTO proxySelectorDTO) {
+
+        return ShenyuAdminResult.success(proxySelectorService.createOrUpdate(proxySelectorDTO));
+    }
+
+    /**
+     * update proxy selector.
+     *
+     * @param id               id
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @PutMapping("/{id}")
+    public ShenyuAdminResult updateProxySelector(@PathVariable("id")
+                                                 @Existed(message = "proxy selector is not existed",
+                                                         provider = PluginMapper.class) final String id,
+                                                 @Valid @RequestBody final ProxySelectorDTO proxySelectorDTO) {
+
+        proxySelectorDTO.setId(id);
+        return ShenyuAdminResult.success(proxySelectorService.createOrUpdate(proxySelectorDTO));
+    }
+
+    /**
+     * batch delete.
+     *
+     * @param ids id list
+     * @return {@linkplain ShenyuAdminResult}
+     */
+    @DeleteMapping("/batch")
+    public ShenyuAdminResult deleteProxySelectors(@NotEmpty @RequestBody final List<@NotBlank String> ids) {
+
+        return ShenyuAdminResult.success(proxySelectorService.delete(ids));
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapper.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapper.java
new file mode 100644
index 000000000..f2ed75ea7
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapper.java
@@ -0,0 +1,71 @@
+/*
+ * 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 org.apache.shenyu.admin.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO;
+import org.apache.shenyu.admin.validation.ExistProvider;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Mapper
+public interface DiscoveryUpstreamMapper extends ExistProvider {
+
+    /**
+     * id existed.
+     *
+     * @param key id
+     * @return true or false
+     */
+    @Override
+    Boolean existed(@Param("id") Serializable key);
+
+    /**
+     * selectByIds.
+     *
+     * @param ids id list
+     * @return discoveryUpstreamDO list
+     */
+    List<DiscoveryUpstreamDO> selectByIds(@Param("ids") List<String> ids);
+
+    /**
+     * insert.
+     *
+     * @param discoveryUpstreamDO discoveryUpstreamDO
+     * @return rows int
+     */
+    int insert(DiscoveryUpstreamDO discoveryUpstreamDO);
+
+    /**
+     * update.
+     *
+     * @param discoveryUpstreamDO discoveryUpstreamDO
+     * @return rows int
+     */
+    int update(DiscoveryUpstreamDO discoveryUpstreamDO);
+
+    /**
+     * deleteByIds.
+     *
+     * @param ids id list
+     * @return rows int
+     */
+    int deleteByIds(@Param("ids") List<String> ids);
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/ProxySelectorMapper.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/ProxySelectorMapper.java
new file mode 100644
index 000000000..933819e40
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/ProxySelectorMapper.java
@@ -0,0 +1,88 @@
+/*
+ * 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 org.apache.shenyu.admin.mapper;
+
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+import org.apache.shenyu.admin.model.entity.ProxySelectorDO;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.admin.validation.ExistProvider;
+
+import java.io.Serializable;
+import java.util.List;
+
+@Mapper
+public interface ProxySelectorMapper extends ExistProvider {
+
+    /**
+     * id existed.
+     *
+     * @param key id
+     * @return true or false
+     */
+    @Override
+    Boolean existed(@Param("id") Serializable key);
+
+    /**
+     * selectByQuery.
+     *
+     * @param query query
+     * @return proxySelectorDO list
+     */
+    List<ProxySelectorDO> selectByQuery(ProxySelectorQuery query);
+
+    /**
+     * nameExisted.
+     *
+     * @param name name
+     * @return true or false
+     */
+    Boolean nameExisted(@Param("name") Serializable name);
+
+    /**
+     * insert.
+     *
+     * @param proxySelectorDO proxySelectorDO
+     * @return rows int
+     */
+    int insert(ProxySelectorDO proxySelectorDO);
+
+    /**
+     * update.
+     *
+     * @param proxySelectorDO proxySelectorDO
+     * @return rows int
+     */
+    int update(ProxySelectorDO proxySelectorDO);
+
+    /**
+     * selectByIds.
+     *
+     * @param ids id list
+     * @return proxySelectorDO list
+     */
+    List<ProxySelectorDO> selectByIds(@Param("ids") List<String> ids);
+
+    /**
+     * deleteByIds.
+     *
+     * @param ids id list
+     * @return rows int
+     */
+    int deleteByIds(@Param("ids") List<String> ids);
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/DiscoveryUpstreamDTO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/DiscoveryUpstreamDTO.java
new file mode 100644
index 000000000..e6a21eb4f
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/DiscoveryUpstreamDTO.java
@@ -0,0 +1,214 @@
+/*
+ * 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 org.apache.shenyu.admin.model.dto;
+
+import org.apache.shenyu.admin.mapper.DiscoveryUpstreamMapper;
+import org.apache.shenyu.admin.validation.annotation.Existed;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * discovery upstream dto.
+ */
+public class DiscoveryUpstreamDTO implements Serializable {
+
+    private static final long serialVersionUID = -1704110184910210095L;
+
+    /**
+     * id.
+     */
+    @Existed(provider = DiscoveryUpstreamMapper.class, nullOfIgnore = true, message = "discovery upstream not exited")
+    private String id;
+
+    /**
+     * discovery id.
+     */
+    @NotBlank(message = "discoveryId不能为空")
+    private String discoveryId;
+
+    /**
+     * protocol.
+     */
+    @NotBlank(message = "protocol不能为空")
+    private String protocol;
+
+    /**
+     * url.
+     */
+    @NotBlank(message = "url不能为空")
+    private String url;
+
+    /**
+     * status.
+     */
+    @NotNull(message = "status不能为空")
+    private Integer status;
+
+    /**
+     * weight.
+     */
+    @NotNull(message = "weight不能为空")
+    private Integer weight;
+
+    /**
+     * props.
+     */
+    @NotBlank(message = "props不能为空")
+    private String props;
+
+    /**
+     * getId.
+     *
+     * @return id
+     */
+    public String getId() {
+
+        return id;
+    }
+
+    /**
+     * setId.
+     *
+     * @param id id
+     */
+    public void setId(final String id) {
+
+        this.id = id;
+    }
+
+    /**
+     * getDiscoveryId.
+     *
+     * @return discoveryId
+     */
+    public String getDiscoveryId() {
+
+        return discoveryId;
+    }
+
+    /**
+     * setDiscoveryId.
+     *
+     * @param discoveryId discoveryId
+     */
+    public void setDiscoveryId(final String discoveryId) {
+
+        this.discoveryId = discoveryId;
+    }
+
+    /**
+     * getProtocol.
+     *
+     * @return protocol
+     */
+    public String getProtocol() {
+
+        return protocol;
+    }
+
+    /**
+     * setProtocol.
+     *
+     * @param protocol protocol
+     */
+    public void setProtocol(final String protocol) {
+
+        this.protocol = protocol;
+    }
+
+    /**
+     * getUrl.
+     *
+     * @return url
+     */
+    public String getUrl() {
+
+        return url;
+    }
+
+    /**
+     * setUrl.
+     *
+     * @param url url
+     */
+    public void setUrl(final String url) {
+
+        this.url = url;
+    }
+
+    /**
+     * getStatus.
+     *
+     * @return status
+     */
+    public int getStatus() {
+        return status;
+    }
+
+    /**
+     * setStatus.
+     *
+     * @param status status
+     */
+    public void setStatus(final int status) {
+
+        this.status = status;
+    }
+
+    /**
+     * getWeight.
+     *
+     * @return weight
+     */
+    public int getWeight() {
+
+        return weight;
+    }
+
+    /**
+     * setWeight.
+     *
+     * @param weight weight
+     */
+    public void setWeight(final int weight) {
+
+        this.weight = weight;
+    }
+
+    /**
+     * getProps.
+     *
+     * @return props
+     */
+    public String getProps() {
+
+        return props;
+    }
+
+    /**
+     * setProps.
+     *
+     * @param props props
+     */
+    public void setProps(final String props) {
+
+        this.props = props;
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/ProxySelectorDTO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/ProxySelectorDTO.java
new file mode 100644
index 000000000..1b40829a7
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/dto/ProxySelectorDTO.java
@@ -0,0 +1,177 @@
+/*
+ * 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 org.apache.shenyu.admin.model.dto;
+
+import org.apache.shenyu.admin.mapper.ProxySelectorMapper;
+import org.apache.shenyu.admin.validation.annotation.Existed;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * proxy selector dto.
+ */
+public class ProxySelectorDTO implements Serializable {
+
+    private static final long serialVersionUID = 3004856980753743799L;
+
+    /**
+     * id.
+     */
+    @Existed(provider = ProxySelectorMapper.class, nullOfIgnore = true, message = "proxy selector not exited")
+    private String id;
+
+    /**
+     * proxy name.
+     */
+    @NotBlank
+    private String name;
+
+    /**
+     * plugin name.
+     */
+    @NotBlank
+    private String pluginName;
+
+    /**
+     * proxy type for tcp, upd, ws.
+     */
+    @NotBlank
+    private String type;
+
+    /**
+     * proxy forward port.
+     */
+    @NotNull
+    private Integer forwardPort;
+
+    /**
+     * other field.
+     */
+    @NotBlank
+    private String props;
+
+    /**
+     * getId.
+     *
+     * @return id
+     */
+    public String getId() {
+        return id;
+    }
+
+    /**
+     * setId.
+     *
+     * @param id id
+     */
+    public void setId(final String id) {
+        this.id = id;
+    }
+
+    /**
+     * getName.
+     *
+     * @return name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * setName.
+     *
+     * @param name name
+     */
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * getPluginName.
+     *
+     * @return pluginName
+     */
+    public String getPluginName() {
+        return pluginName;
+    }
+
+    /**
+     * setPluginName.
+     *
+     * @param pluginName pluginName
+     */
+    public void setPluginName(final String pluginName) {
+        this.pluginName = pluginName;
+    }
+
+    /**
+     * getType.
+     *
+     * @return type
+     */
+    public String getType() {
+        return type;
+    }
+
+    /**
+     * setType.
+     *
+     * @param type type
+     */
+    public void setType(final String type) {
+        this.type = type;
+    }
+
+    /**
+     * getForwardPort.
+     *
+     * @return forwardPort
+     */
+    public Integer getForwardPort() {
+        return forwardPort;
+    }
+
+    /**
+     * setForwardPort.
+     *
+     * @param forwardPort forwardPort
+     */
+    public void setForwardPort(final Integer forwardPort) {
+        this.forwardPort = forwardPort;
+    }
+
+    /**
+     * getProps.
+     *
+     * @return props
+     */
+    public String getProps() {
+        return props;
+    }
+
+    /**
+     * setProps.
+     *
+     * @param props props
+     */
+    public void setProps(final String props) {
+        this.props = props;
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/DiscoveryUpstreamDO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/DiscoveryUpstreamDO.java
new file mode 100644
index 000000000..5b5705b0f
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/DiscoveryUpstreamDO.java
@@ -0,0 +1,427 @@
+/*
+ * 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 org.apache.shenyu.admin.model.entity;
+
+import org.apache.shenyu.admin.model.dto.DiscoveryUpstreamDTO;
+import org.apache.shenyu.common.utils.UUIDUtils;
+import org.springframework.util.StringUtils;
+
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/**
+ * discovery upstream do.
+ */
+public class DiscoveryUpstreamDO extends BaseDO {
+
+    private static final long serialVersionUID = 4636503463949130337L;
+
+    /**
+     * discoveryId.
+     */
+    private String discoveryId;
+
+    /**
+     * protocol.
+     */
+    private String protocol;
+
+    /**
+     * url.
+     */
+    private String url;
+
+    /**
+     * status.
+     */
+    private int status;
+
+    /**
+     * weight.
+     */
+    private int weight;
+
+    /**
+     * props.
+     */
+    private String props;
+
+    /**
+     * DiscoveryUpstreamDO.
+     */
+    public DiscoveryUpstreamDO() {
+
+    }
+
+    /**
+     * DiscoveryUpstreamDO.
+     *
+     * @param discoveryId discoveryId
+     * @param protocol    protocol
+     * @param url         url
+     * @param status      status
+     * @param weight      weight
+     * @param props       props
+     */
+    public DiscoveryUpstreamDO(final String discoveryId, final String protocol, final String url, final int status,
+                               final int weight, final String props) {
+
+        this.discoveryId = discoveryId;
+        this.protocol = protocol;
+        this.url = url;
+        this.status = status;
+        this.weight = weight;
+        this.props = props;
+    }
+
+    /**
+     * getDiscoveryId.
+     *
+     * @return discoveryId
+     */
+    public String getDiscoveryId() {
+
+        return discoveryId;
+    }
+
+    /**
+     * setDiscoveryId.
+     *
+     * @param discoveryId discoveryId
+     */
+    public void setDiscoveryId(final String discoveryId) {
+
+        this.discoveryId = discoveryId;
+    }
+
+    /**
+     * getProtocol.
+     *
+     * @return protocol
+     */
+    public String getProtocol() {
+
+        return protocol;
+    }
+
+    /**
+     * setProtocol.
+     *
+     * @param protocol protocol
+     */
+    public void setProtocol(final String protocol) {
+
+        this.protocol = protocol;
+    }
+
+    /**
+     * getUrl.
+     *
+     * @return url
+     */
+    public String getUrl() {
+
+        return url;
+    }
+
+    /**
+     * setUrl.
+     *
+     * @param url url
+     */
+    public void setUrl(final String url) {
+
+        this.url = url;
+    }
+
+    /**
+     * getStatus.
+     *
+     * @return status
+     */
+    public int getStatus() {
+
+        return status;
+    }
+
+    /**
+     * setStatus.
+     *
+     * @param status status
+     */
+    public void setStatus(final int status) {
+
+        this.status = status;
+    }
+
+    /**
+     * getWeight.
+     *
+     * @return weight
+     */
+    public int getWeight() {
+
+        return weight;
+    }
+
+    /**
+     * setWeight.
+     *
+     * @param weight weight
+     */
+    public void setWeight(final int weight) {
+
+        this.weight = weight;
+    }
+
+    /**
+     * getProps.
+     *
+     * @return props
+     */
+    public String getProps() {
+
+        return props;
+    }
+
+    /**
+     * setProps.
+     *
+     * @param props props
+     */
+    public void setProps(final String props) {
+
+        this.props = props;
+    }
+
+    /**
+     * builder.
+     *
+     * @return DiscoveryUpstreamBuilder
+     */
+    public static DiscoveryUpstreamBuilder builder() {
+
+        return new DiscoveryUpstreamBuilder();
+    }
+
+    /**
+     * buildDiscoveryUpstreamDO.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return DiscoveryUpstreamDO
+     */
+    public static DiscoveryUpstreamDO buildDiscoveryUpstreamDO(final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        return Optional.ofNullable(discoveryUpstreamDTO).map(item -> {
+            Timestamp currentTime = new Timestamp(System.currentTimeMillis());
+            DiscoveryUpstreamDO discoveryUpstreamDO = DiscoveryUpstreamDO.builder()
+                    .discoveryId(item.getDiscoveryId())
+                    .protocol(item.getProtocol())
+                    .status(item.getStatus())
+                    .weight(item.getWeight())
+                    .props(item.getProps())
+                    .dateUpdated(currentTime).build();
+            if (StringUtils.hasLength(item.getId())) {
+                discoveryUpstreamDO.setId(item.getId());
+            } else {
+                discoveryUpstreamDO.setId(UUIDUtils.getInstance().generateShortUuid());
+                discoveryUpstreamDO.setDateCreated(currentTime);
+            }
+            return discoveryUpstreamDO;
+        }).orElse(null);
+    }
+
+    /**
+     * DiscoveryUpstreamBuilder.
+     */
+    public static final class DiscoveryUpstreamBuilder {
+
+        /**
+         * id.
+         */
+        private String id;
+
+        /**
+         * dateCreated.
+         */
+        private Timestamp dateCreated;
+
+        /**
+         * dateUpdated.
+         */
+        private Timestamp dateUpdated;
+
+        /**
+         * discoveryId.
+         */
+        private String discoveryId;
+
+        /**
+         * protocol.
+         */
+        private String protocol;
+
+        /**
+         * url.
+         */
+        private String url;
+
+        /**
+         * status.
+         */
+        private int status;
+
+        /**
+         * weight.
+         */
+        private int weight;
+
+        /**
+         * props.
+         */
+        private String props;
+
+        /**
+         * id.
+         *
+         * @param id the id.
+         * @return ProxySelectorBuilder.
+         */
+        public DiscoveryUpstreamBuilder id(final String id) {
+
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * dateCreated.
+         *
+         * @param dateCreated the dateCreated.
+         * @return ProxySelectorBuilder.
+         */
+        public DiscoveryUpstreamBuilder dateCreated(final Timestamp dateCreated) {
+
+            this.dateCreated = dateCreated;
+            return this;
+        }
+
+        /**
+         * dateUpdated.
+         *
+         * @param dateUpdated the dateUpdated.
+         * @return ProxySelectorBuilder.
+         */
+        public DiscoveryUpstreamBuilder dateUpdated(final Timestamp dateUpdated) {
+
+            this.dateUpdated = dateUpdated;
+            return this;
+        }
+
+        /**
+         * discoveryId.
+         *
+         * @param discoveryId discoveryId
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder discoveryId(final String discoveryId) {
+
+            this.discoveryId = discoveryId;
+            return this;
+        }
+
+        /**
+         * protocol.
+         *
+         * @param protocol protocol
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder protocol(final String protocol) {
+
+            this.protocol = protocol;
+            return this;
+        }
+
+        /**
+         * url.
+         *
+         * @param url url
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder url(final String url) {
+
+            this.url = url;
+            return this;
+        }
+
+        /**
+         * status.
+         *
+         * @param status status
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder status(final int status) {
+
+            this.status = status;
+            return this;
+        }
+
+        /**
+         * weight.
+         *
+         * @param weight weight
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder weight(final int weight) {
+
+            this.weight = weight;
+            return this;
+        }
+
+        /**
+         * props.
+         *
+         * @param props props
+         * @return DiscoveryUpstreamBuilder
+         */
+        public DiscoveryUpstreamBuilder props(final String props) {
+
+            this.props = props;
+            return this;
+        }
+
+        /**
+         * build.
+         *
+         * @return DiscoveryUpstreamDO
+         */
+        public DiscoveryUpstreamDO build() {
+
+            DiscoveryUpstreamDO discoveryUpstreamDO = new DiscoveryUpstreamDO();
+            discoveryUpstreamDO.setId(this.id);
+            discoveryUpstreamDO.setDiscoveryId(this.discoveryId);
+            discoveryUpstreamDO.setProtocol(this.protocol);
+            discoveryUpstreamDO.setUrl(this.url);
+            discoveryUpstreamDO.setStatus(this.status);
+            discoveryUpstreamDO.setWeight(this.weight);
+            discoveryUpstreamDO.setProps(this.props);
+            discoveryUpstreamDO.setDateCreated(this.dateCreated);
+            discoveryUpstreamDO.setDateUpdated(this.dateUpdated);
+            return discoveryUpstreamDO;
+        }
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/ProxySelectorDO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/ProxySelectorDO.java
new file mode 100644
index 000000000..c82d1c6e0
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/ProxySelectorDO.java
@@ -0,0 +1,363 @@
+/*
+ * 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 org.apache.shenyu.admin.model.entity;
+
+import org.apache.shenyu.admin.model.dto.ProxySelectorDTO;
+import org.apache.shenyu.common.utils.UUIDUtils;
+import org.springframework.util.StringUtils;
+
+import java.sql.Timestamp;
+import java.util.Optional;
+
+/**
+ * proxy selector do.
+ */
+public class ProxySelectorDO extends BaseDO {
+
+    private static final long serialVersionUID = 6324671206584485506L;
+
+    /**
+     * proxy name.
+     */
+    private String name;
+
+    /**
+     * plugin name.
+     */
+    private String pluginName;
+
+    /**
+     * proxy type for tcp, upd, ws.
+     */
+    private String type;
+
+    /**
+     * proxy forward port.
+     */
+    private Integer forwardPort;
+
+    /**
+     * other field.
+     */
+    private String props;
+
+    /**
+     * builder.
+     *
+     * @return ProxySelectorBuilder
+     */
+    public static ProxySelectorBuilder builder() {
+
+        return new ProxySelectorBuilder();
+    }
+
+    /**
+     * buildProxySelectorDO.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return ProxySelectorDO
+     */
+    public static ProxySelectorDO buildProxySelectorDO(final ProxySelectorDTO proxySelectorDTO) {
+
+        return Optional.ofNullable(proxySelectorDTO).map(item -> {
+            Timestamp currentTime = new Timestamp(System.currentTimeMillis());
+            ProxySelectorDO proxySelectorDO = ProxySelectorDO.builder()
+                    .name(item.getName())
+                    .pluginName(item.getPluginName())
+                    .forwardPort(item.getForwardPort())
+                    .type(item.getType())
+                    .props(item.getProps())
+                    .dateUpdated(currentTime).build();
+            if (StringUtils.hasLength(item.getId())) {
+                proxySelectorDO.setId(item.getId());
+            } else {
+                proxySelectorDO.setId(UUIDUtils.getInstance().generateShortUuid());
+                proxySelectorDO.setDateCreated(currentTime);
+            }
+            return proxySelectorDO;
+        }).orElse(null);
+    }
+
+    /**
+     * getName.
+     *
+     * @return name
+     */
+    public String getName() {
+
+        return name;
+    }
+
+    /**
+     * setName.
+     *
+     * @param name name
+     */
+    public void setName(final String name) {
+
+        this.name = name;
+    }
+
+    /**
+     * getPluginName.
+     *
+     * @return pluginName
+     */
+    public String getPluginName() {
+
+        return pluginName;
+    }
+
+    /**
+     * setPluginName.
+     *
+     * @param pluginName pluginName
+     */
+    public void setPluginName(final String pluginName) {
+
+        this.pluginName = pluginName;
+    }
+
+    /**
+     * getType.
+     *
+     * @return type
+     */
+    public String getType() {
+
+        return type;
+    }
+
+    /**
+     * setType.
+     *
+     * @param type type
+     */
+    public void setType(final String type) {
+
+        this.type = type;
+    }
+
+    /**
+     * getForwardPort.
+     *
+     * @return forwardPort
+     */
+    public Integer getForwardPort() {
+
+        return forwardPort;
+    }
+
+    /**
+     * setForwardPort.
+     *
+     * @param forwardPort forwardPort
+     */
+    public void setForwardPort(final Integer forwardPort) {
+
+        this.forwardPort = forwardPort;
+    }
+
+    /**
+     * getProps.
+     *
+     * @return props
+     */
+    public String getProps() {
+
+        return props;
+    }
+
+    /**
+     * setProps.
+     *
+     * @param props props
+     */
+    public void setProps(final String props) {
+
+        this.props = props;
+    }
+
+    /**
+     * ProxySelectorBuilder.
+     */
+    public static final class ProxySelectorBuilder {
+
+        /**
+         * id.
+         */
+        private String id;
+
+        /**
+         * dateCreated.
+         */
+        private Timestamp dateCreated;
+
+        /**
+         * dateUpdated.
+         */
+        private Timestamp dateUpdated;
+
+        /**
+         * name.
+         */
+        private String name;
+
+        /**
+         * pluginName.
+         */
+        private String pluginName;
+
+        /**
+         * type.
+         */
+        private String type;
+
+        /**
+         * forwardPort.
+         */
+        private Integer forwardPort;
+
+        /**
+         * props.
+         */
+        private String props;
+
+        /**
+         * ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder() {
+
+        }
+
+        /**
+         * id.
+         *
+         * @param id the id.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder id(final String id) {
+
+            this.id = id;
+            return this;
+        }
+
+        /**
+         * dateCreated.
+         *
+         * @param dateCreated the dateCreated.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder dateCreated(final Timestamp dateCreated) {
+
+            this.dateCreated = dateCreated;
+            return this;
+        }
+
+        /**
+         * dateUpdated.
+         *
+         * @param dateUpdated the dateUpdated.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder dateUpdated(final Timestamp dateUpdated) {
+
+            this.dateUpdated = dateUpdated;
+            return this;
+        }
+
+        /**
+         * name.
+         *
+         * @param name the name.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder name(final String name) {
+
+            this.name = name;
+            return this;
+        }
+
+        /**
+         * pluginName.
+         *
+         * @param pluginName the pluginName.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder pluginName(final String pluginName) {
+
+            this.pluginName = pluginName;
+            return this;
+        }
+
+        /**
+         * type.
+         *
+         * @param type the type.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder type(final String type) {
+
+            this.type = type;
+            return this;
+        }
+
+        /**
+         * forwardPort.
+         *
+         * @param forwardPort the forwardPort.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder forwardPort(final Integer forwardPort) {
+
+            this.forwardPort = forwardPort;
+            return this;
+        }
+
+        /**
+         * props.
+         *
+         * @param props other field.
+         * @return ProxySelectorBuilder.
+         */
+        public ProxySelectorBuilder props(final String props) {
+
+            this.props = props;
+            return this;
+        }
+
+        /**
+         * build.
+         *
+         * @return ProxySelectorDO
+         */
+        public ProxySelectorDO build() {
+
+            ProxySelectorDO proxySelectorDO = new ProxySelectorDO();
+            proxySelectorDO.setId(this.id);
+            proxySelectorDO.setName(this.name);
+            proxySelectorDO.setPluginName(pluginName);
+            proxySelectorDO.setForwardPort(this.forwardPort);
+            proxySelectorDO.setType(this.type);
+            proxySelectorDO.setProps(this.props);
+            proxySelectorDO.setDateCreated(this.dateCreated);
+            proxySelectorDO.setDateUpdated(this.dateUpdated);
+            return proxySelectorDO;
+        }
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/query/ProxySelectorQuery.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/query/ProxySelectorQuery.java
new file mode 100644
index 000000000..c3c74a7ca
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/query/ProxySelectorQuery.java
@@ -0,0 +1,79 @@
+/*
+ * 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 org.apache.shenyu.admin.model.query;
+
+import org.apache.shenyu.admin.model.page.PageParameter;
+
+import java.io.Serializable;
+
+/**
+ * this is proxy selector query.
+ */
+public class ProxySelectorQuery implements Serializable {
+
+    private static final long serialVersionUID = 8058060154624248675L;
+
+    /**
+     * proxy selector name.
+     */
+    private String name;
+
+    /**
+     * page parameter.
+     */
+    private PageParameter pageParameter;
+
+    /**
+     * ProxySelectorQuery.
+     */
+    public ProxySelectorQuery() {
+
+    }
+
+    /**
+     * ProxySelectorQuery.
+     *
+     * @param name          name
+     * @param pageParameter pageParameter
+     */
+    public ProxySelectorQuery(final String name, final PageParameter pageParameter) {
+
+        this.name = name;
+        this.pageParameter = pageParameter;
+    }
+
+    /**
+     * getName.
+     *
+     * @return name
+     */
+    public String getName() {
+
+        return name;
+    }
+
+    /**
+     * getPageParameter.
+     *
+     * @return pageParameter
+     */
+    public PageParameter getPageParameter() {
+
+        return pageParameter;
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/ProxySelectorVO.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/ProxySelectorVO.java
new file mode 100644
index 000000000..d288c87eb
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/ProxySelectorVO.java
@@ -0,0 +1,197 @@
+/*
+ * 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 org.apache.shenyu.admin.model.vo;
+
+import org.apache.shenyu.admin.model.entity.ProxySelectorDO;
+import org.apache.shenyu.common.utils.DateUtils;
+
+import java.io.Serializable;
+
+/**
+ * this is proxy selector view to web front.
+ */
+public class ProxySelectorVO implements Serializable {
+
+    private static final long serialVersionUID = -1329374830009912963L;
+
+    /**
+     * id.
+     */
+    private String id;
+
+    /**
+     * proxy selector name.
+     */
+    private String name;
+
+    /**
+     * pluginName.
+     */
+    private String pluginName;
+
+    /**
+     * forward port.
+     */
+    private Integer forwardPort;
+
+    /**
+     * type.
+     */
+    private String type;
+
+    /**
+     * props.
+     */
+    private String props;
+
+    /**
+     * created time.
+     */
+    private String dateCreated;
+
+    /**
+     * updated time.
+     */
+    private String dateUpdated;
+
+    /**
+     * ProxySelectorVO.
+     */
+    public ProxySelectorVO() {
+
+    }
+
+    /**
+     * ProxySelectorVO.
+     *
+     * @param id          id
+     * @param name        name
+     * @param pluginName  pluginName
+     * @param forwardPort forwardPort
+     * @param type        type
+     * @param props       props
+     * @param dateCreated dateCreated
+     * @param dateUpdated dateUpdated
+     */
+    public ProxySelectorVO(final String id, final String name, final String pluginName, final Integer forwardPort,
+                           final String type, final String props, final String dateCreated, final String dateUpdated) {
+
+        this.id = id;
+        this.name = name;
+        this.pluginName = pluginName;
+        this.forwardPort = forwardPort;
+        this.type = type;
+        this.props = props;
+        this.dateCreated = dateCreated;
+        this.dateUpdated = dateUpdated;
+    }
+
+    /**
+     * getName.
+     *
+     * @return name
+     */
+    public String getName() {
+
+        return name;
+    }
+
+    /**
+     * setName.
+     *
+     * @param name name
+     */
+    public void setName(final String name) {
+
+        this.name = name;
+    }
+
+    /**
+     * getForwardPort.
+     *
+     * @return forwardPort
+     */
+    public Integer getForwardPort() {
+
+        return forwardPort;
+    }
+
+    /**
+     * setForwardPort.
+     *
+     * @param forwardPort forwardPort
+     */
+    public void setForwardPort(final Integer forwardPort) {
+
+        this.forwardPort = forwardPort;
+    }
+
+    /**
+     * getDateCreated.
+     *
+     * @return dateCreated
+     */
+    public String getDateCreated() {
+
+        return dateCreated;
+    }
+
+    /**
+     * setDateCreated.
+     *
+     * @param dateCreated dateCreated
+     */
+    public void setDateCreated(final String dateCreated) {
+
+        this.dateCreated = dateCreated;
+    }
+
+    /**
+     * getDateUpdated.
+     *
+     * @return dateUpdated
+     */
+    public String getDateUpdated() {
+
+        return dateUpdated;
+    }
+
+    /**
+     * setDateUpdated.
+     *
+     * @param dateUpdated dateUpdated
+     */
+    public void setDateUpdated(final String dateUpdated) {
+
+        this.dateUpdated = dateUpdated;
+    }
+
+    /**
+     * buildProxySelectorVO.
+     *
+     * @param proxySelectorDO proxySelectorDO
+     * @return ProxySelectorVO
+     */
+    public static ProxySelectorVO buildProxySelectorVO(final ProxySelectorDO proxySelectorDO) {
+
+        return new ProxySelectorVO(proxySelectorDO.getId(), proxySelectorDO.getName(), proxySelectorDO.getPluginName(),
+                proxySelectorDO.getForwardPort(), proxySelectorDO.getType(), proxySelectorDO.getProps(),
+                DateUtils.localDateTimeToString(proxySelectorDO.getDateCreated().toLocalDateTime()),
+                DateUtils.localDateTimeToString(proxySelectorDO.getDateUpdated().toLocalDateTime()));
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DiscoveryUpstreamService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DiscoveryUpstreamService.java
new file mode 100644
index 000000000..5106f3763
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/DiscoveryUpstreamService.java
@@ -0,0 +1,41 @@
+/*
+ * 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 org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.model.dto.DiscoveryUpstreamDTO;
+
+import java.util.List;
+
+public interface DiscoveryUpstreamService {
+
+    /**
+     * createOrUpdate.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return the string
+     */
+    String createOrUpdate(DiscoveryUpstreamDTO discoveryUpstreamDTO);
+
+    /**
+     * delete.
+     *
+     * @param ids id list
+     * @return the string
+     */
+    String delete(List<String> ids);
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/ProxySelectorService.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/ProxySelectorService.java
new file mode 100644
index 000000000..2899cf832
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/ProxySelectorService.java
@@ -0,0 +1,52 @@
+/*
+ * 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 org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.model.dto.ProxySelectorDTO;
+import org.apache.shenyu.admin.model.page.CommonPager;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.admin.model.vo.ProxySelectorVO;
+
+import java.util.List;
+
+public interface ProxySelectorService {
+
+    /**
+     * listByPage.
+     *
+     * @param query query
+     * @return page
+     */
+    CommonPager<ProxySelectorVO> listByPage(ProxySelectorQuery query);
+
+    /**
+     * createOrUpdate.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return the string
+     */
+    String createOrUpdate(ProxySelectorDTO proxySelectorDTO);
+
+    /**
+     * delete.
+     *
+     * @param ids id list
+     * @return the string
+     */
+    String delete(List<String> ids);
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryUpstreamServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryUpstreamServiceImpl.java
new file mode 100644
index 000000000..5464b7421
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryUpstreamServiceImpl.java
@@ -0,0 +1,91 @@
+/*
+ * 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 org.apache.shenyu.admin.service.impl;
+
+import org.apache.shenyu.admin.mapper.DiscoveryUpstreamMapper;
+import org.apache.shenyu.admin.model.dto.DiscoveryUpstreamDTO;
+import org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO;
+import org.apache.shenyu.admin.service.DiscoveryUpstreamService;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+@Service
+public class DiscoveryUpstreamServiceImpl implements DiscoveryUpstreamService {
+
+    private final DiscoveryUpstreamMapper discoveryUpstreamMapper;
+
+    public DiscoveryUpstreamServiceImpl(final DiscoveryUpstreamMapper discoveryUpstreamMapper) {
+
+        this.discoveryUpstreamMapper = discoveryUpstreamMapper;
+    }
+
+    /**
+     * createOrUpdate.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return the string
+     */
+    @Override
+    public String createOrUpdate(final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        return StringUtils.hasLength(discoveryUpstreamDTO.getId())
+                ? update(discoveryUpstreamDTO) : create(discoveryUpstreamDTO);
+    }
+
+    /**
+     * delete.
+     *
+     * @param ids id list
+     * @return the string
+     */
+    @Override
+    public String delete(final List<String> ids) {
+
+        discoveryUpstreamMapper.deleteByIds(ids);
+        return ShenyuResultMessage.DELETE_SUCCESS;
+    }
+
+    /**
+     * create.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return the string
+     */
+    private String create(final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        DiscoveryUpstreamDO discoveryUpstreamDO = DiscoveryUpstreamDO.buildDiscoveryUpstreamDO(discoveryUpstreamDTO);
+        discoveryUpstreamMapper.insert(discoveryUpstreamDO);
+        return ShenyuResultMessage.CREATE_SUCCESS;
+    }
+
+    /**
+     * update.
+     *
+     * @param discoveryUpstreamDTO discoveryUpstreamDTO
+     * @return the string
+     */
+    private String update(final DiscoveryUpstreamDTO discoveryUpstreamDTO) {
+
+        DiscoveryUpstreamDO discoveryUpstreamDO = DiscoveryUpstreamDO.buildDiscoveryUpstreamDO(discoveryUpstreamDTO);
+        discoveryUpstreamMapper.update(discoveryUpstreamDO);
+        return ShenyuResultMessage.UPDATE_SUCCESS;
+    }
+}
diff --git a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ProxySelectorServiceImpl.java b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ProxySelectorServiceImpl.java
new file mode 100644
index 000000000..4a0481e2f
--- /dev/null
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ProxySelectorServiceImpl.java
@@ -0,0 +1,122 @@
+/*
+ * 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 org.apache.shenyu.admin.service.impl;
+
+import org.apache.shenyu.admin.aspect.annotation.Pageable;
+import org.apache.shenyu.admin.mapper.ProxySelectorMapper;
+import org.apache.shenyu.admin.model.dto.ProxySelectorDTO;
+import org.apache.shenyu.admin.model.entity.ProxySelectorDO;
+import org.apache.shenyu.admin.model.page.CommonPager;
+import org.apache.shenyu.admin.model.page.PageResultUtils;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.admin.model.vo.ProxySelectorVO;
+import org.apache.shenyu.admin.service.ProxySelectorService;
+import org.apache.shenyu.admin.utils.Assert;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.apache.shenyu.common.constant.AdminConstants;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of the {@link org.apache.shenyu.admin.service.ProxySelectorService}.
+ */
+@Service
+public class ProxySelectorServiceImpl implements ProxySelectorService {
+
+    private final ProxySelectorMapper proxySelectorMapper;
+
+    public ProxySelectorServiceImpl(final ProxySelectorMapper proxySelectorMapper) {
+
+        this.proxySelectorMapper = proxySelectorMapper;
+    }
+
+    /**
+     * listByPage.
+     *
+     * @param query query
+     * @return page
+     */
+    @Override
+    @Pageable
+    public CommonPager<ProxySelectorVO> listByPage(final ProxySelectorQuery query) {
+
+        return PageResultUtils.result(query.getPageParameter(), () -> proxySelectorMapper.selectByQuery(query)
+                .stream()
+                .map(ProxySelectorVO::buildProxySelectorVO)
+                .collect(Collectors.toList()));
+    }
+
+    /**
+     * createOrUpdate.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return the string
+     */
+    @Override
+    public String createOrUpdate(final ProxySelectorDTO proxySelectorDTO) {
+
+        return StringUtils.hasLength(proxySelectorDTO.getId()) ? update(proxySelectorDTO) : create(proxySelectorDTO);
+    }
+
+    /**
+     * delete.
+     *
+     * @param ids id list
+     * @return the string
+     */
+    @Override
+    public String delete(final List<String> ids) {
+
+        proxySelectorMapper.deleteByIds(ids);
+        return ShenyuResultMessage.DELETE_SUCCESS;
+    }
+
+    /**
+     * create.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return the string
+     */
+    private String create(final ProxySelectorDTO proxySelectorDTO) {
+
+        Assert.isNull(proxySelectorMapper.nameExisted(proxySelectorDTO.getName()),
+                AdminConstants.PROXY_SELECTOR_NAME_IS_EXIST);
+        ProxySelectorDO proxySelectorDO = ProxySelectorDO.buildProxySelectorDO(proxySelectorDTO);
+        proxySelectorMapper.insert(proxySelectorDO);
+        return ShenyuResultMessage.CREATE_SUCCESS;
+
+    }
+
+    /**
+     * update.
+     *
+     * @param proxySelectorDTO proxySelectorDTO
+     * @return the string
+     */
+    private String update(final ProxySelectorDTO proxySelectorDTO) {
+
+        Assert.isNull(proxySelectorMapper.nameExisted(proxySelectorDTO.getName()),
+                AdminConstants.PROXY_SELECTOR_NAME_IS_EXIST);
+        ProxySelectorDO proxySelectorDO = ProxySelectorDO.buildProxySelectorDO(proxySelectorDTO);
+        proxySelectorMapper.update(proxySelectorDO);
+        return ShenyuResultMessage.UPDATE_SUCCESS;
+    }
+}
diff --git a/shenyu-admin/src/main/resources/mappers/discovery-upstream-sqlmap.xml b/shenyu-admin/src/main/resources/mappers/discovery-upstream-sqlmap.xml
new file mode 100644
index 000000000..29a72403b
--- /dev/null
+++ b/shenyu-admin/src/main/resources/mappers/discovery-upstream-sqlmap.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.apache.shenyu.admin.mapper.DiscoveryUpstreamMapper">
+    <resultMap id="BaseResultMap" type="org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO">
+        <id column="id" jdbcType="VARCHAR" property="id"/>
+        <result column="date_created" jdbcType="TIMESTAMP" property="dateCreated"/>
+        <result column="date_updated" jdbcType="TIMESTAMP" property="dateUpdated"/>
+        <result column="discovery_id" jdbcType="VARCHAR" property="discoveryId"/>
+        <result column="protocol" jdbcType="VARCHAR" property="protocol"/>
+        <result column="url" jdbcType="VARCHAR" property="url"/>
+        <result column="status" jdbcType="INTEGER" property="status"/>
+        <result column="weight" jdbcType="INTEGER" property="weight"/>
+        <result column="props" jdbcType="VARCHAR" property="props"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,
+        date_created,
+        date_updated,
+        discovery_id,
+        protocol,
+        url,
+        status,
+        weight,
+        props
+    </sql>
+
+    <select id="existed" resultType="java.lang.Boolean">
+        SELECT true
+        FROM discovery_upstream
+        WHERE id = #{id} LIMIT 1
+    </select>
+
+    <insert id="insert" parameterType="org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO">
+        INSERT INTO discovery_upstream (
+        id,
+        discovery_id,
+        protocol,
+        url,
+        status,
+        weight,
+        props,date_created,
+        date_updated)
+        VALUES (
+        #{id, jdbcType=VARCHAR},
+        #{discoveryId,jdbcType=VARCHAR},
+        #{protocol,jdbcType=VARCHAR},
+        #{url,jdbcType=VARCHAR},
+        #{status,jdbcType=INTEGER},
+        #{weight,jdbcType=INTEGER},
+        #{props,jdbcType=VARCHAR},
+        #{dateCreated, jdbcType=TIMESTAMP},
+        #{dateUpdated, jdbcType=TIMESTAMP})
+    </insert>
+
+    <update id="update" parameterType="org.apache.shenyu.admin.model.entity.ProxySelectorDO">
+        UPDATE discovery_upstream
+        SET
+        discovery_id=#{discoveryId, jdbcType=VARCHAR},
+        protocol=#{protocol,jdbcType=VARCHAR},
+        url=#{url,jdbcType=VARCHAR},
+        status=#{status,jdbcType=INTEGER},
+        weight=#{weight,jdbcType=INTEGER},
+        props=#{props,jdbcType=VARCHAR},
+        date_updated=#{dateUpdated, jdbcType=TIMESTAMP}
+        WHERE id=#{id, jdbcType=VARCHAR}
+    </update>
+
+    <select id="selectByIds" parameterType="java.util.List" resultMap="BaseResultMap">
+        SELECT
+        <include refid="Base_Column_List"/>
+        FROM discovery_upstream
+        WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id, jdbcType=VARCHAR}
+        </foreach>
+    </select>
+
+    <delete id="deleteByIds" parameterType="java.util.List">
+        DELETE FROM discovery_upstream WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id, jdbcType=VARCHAR}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/shenyu-admin/src/main/resources/mappers/proxy-selector-sqlmap.xml b/shenyu-admin/src/main/resources/mappers/proxy-selector-sqlmap.xml
new file mode 100644
index 000000000..5df080149
--- /dev/null
+++ b/shenyu-admin/src/main/resources/mappers/proxy-selector-sqlmap.xml
@@ -0,0 +1,118 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="org.apache.shenyu.admin.mapper.ProxySelectorMapper">
+    <resultMap id="BaseResultMap" type="org.apache.shenyu.admin.model.entity.ProxySelectorDO">
+        <id column="id" jdbcType="VARCHAR" property="id"/>
+        <result column="date_created" jdbcType="TIMESTAMP" property="dateCreated"/>
+        <result column="date_updated" jdbcType="TIMESTAMP" property="dateUpdated"/>
+        <result column="name" jdbcType="VARCHAR" property="name"/>
+        <result column="plugin_name" jdbcType="VARCHAR" property="pluginName"/>
+        <result column="type" jdbcType="VARCHAR" property="type"/>
+        <result column="forward_port" jdbcType="INTEGER" property="forwardPort"/>
+        <result column="props" jdbcType="VARCHAR" property="props"/>
+    </resultMap>
+
+    <sql id="Base_Column_List">
+        id,
+        date_created,
+        date_updated,
+        name,
+        plugin_name,
+        type,
+        forward_port,
+        props
+    </sql>
+
+    <select id="existed" resultType="java.lang.Boolean">
+        SELECT true
+        FROM proxy_selector
+        WHERE id = #{id} LIMIT 1
+    </select>
+
+    <select id="selectByQuery" parameterType="org.apache.shenyu.admin.model.query.ProxySelectorQuery"
+            resultMap="BaseResultMap">
+        SELECT
+        <include refid="Base_Column_List"/>
+        FROM proxy_selector
+        <where>
+            <if test="name != null and name != ''">
+                <bind name="nameLike" value="('%' + name + '%')"/>
+                AND name LIKE #{nameLike, jdbcType=VARCHAR}
+            </if>
+        </where>
+        ORDER BY date_updated DESC
+    </select>
+
+    <select id="nameExisted" resultType="java.lang.Boolean">
+        SELECT true
+        FROM proxy_selector
+        WHERE name = #{name} LIMIT 1
+    </select>
+
+    <insert id="insert" parameterType="org.apache.shenyu.admin.model.entity.ProxySelectorDO">
+        INSERT INTO proxy_selector (
+        id,
+        name,
+        plugin_name,
+        type,
+        forward_port,
+        props,
+        date_created,
+        date_updated)
+        VALUES (
+        #{id, jdbcType=VARCHAR},
+        #{name,jdbcType=VARCHAR},
+        #{pluginName,jdbcType=VARCHAR},
+        #{type,jdbcType=VARCHAR},
+        #{forwardPort,jdbcType=INTEGER},
+        #{props,jdbcType=VARCHAR},
+        #{dateCreated, jdbcType=TIMESTAMP},
+        #{dateUpdated, jdbcType=TIMESTAMP})
+    </insert>
+
+    <update id="update" parameterType="org.apache.shenyu.admin.model.entity.ProxySelectorDO">
+        UPDATE proxy_selector
+        SET
+        name=#{id, jdbcType=VARCHAR},
+        plugin_name=#{pluginName,jdbcType=VARCHAR},
+        type=#{type,jdbcType=VARCHAR},
+        forward_port=#{forwardPort,jdbcType=INTEGER},
+        props=#{props,jdbcType=VARCHAR},
+        date_updated=#{dateUpdated, jdbcType=TIMESTAMP}
+        WHERE id=#{id, jdbcType=VARCHAR}
+    </update>
+
+    <select id="selectByIds" parameterType="java.util.List" resultMap="BaseResultMap">
+        SELECT
+        <include refid="Base_Column_List"/>
+        FROM proxy_selector
+        WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id, jdbcType=VARCHAR}
+        </foreach>
+    </select>
+
+    <delete id="deleteByIds" parameterType="java.util.List">
+        DELETE FROM proxy_selector WHERE id IN
+        <foreach item="id" collection="ids" open="(" separator="," close=")">
+            #{id, jdbcType=VARCHAR}
+        </foreach>
+    </delete>
+</mapper>
diff --git a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
index ce1ba2ace..d8b5627ae 100755
--- a/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
+++ b/shenyu-admin/src/main/resources/sql-script/h2/schema.sql
@@ -992,15 +992,15 @@ CREATE TABLE IF NOT EXISTS `tag_relation`
 -- ----------------------------
 CREATE TABLE `discovery`
 (
-    `id`           varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'primary key id',
-    `name`         varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the discovery name',
-    `type`         varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'local,zookeeper,etcd,consul,nacos',
-    `server_list`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  COMMENT 'register server url (,)',
-    `listener_node` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  COMMENT 'register server listener to node',
-    `props`     text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the discovery pops (json) ',
+    `id`           varchar(128)  NOT NULL COMMENT 'primary key id',
+    `name`         varchar(255)  NOT NULL COMMENT 'the discovery name',
+    `type`         varchar(64)   NOT NULL COMMENT 'local,zookeeper,etcd,consul,nacos',
+    `server_list`  varchar(255)   COMMENT 'register server url (,)',
+    `listener_node` varchar(255)   COMMENT 'register server listener to node',
+    `props`     text  COMMENT 'the discovery pops (json) ',
     `date_created` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'create time',
     `date_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'update time',
-    PRIMARY KEY (`id`) USING BTREE
+    PRIMARY KEY (`id`)
 );
 
 -- ----------------------------
@@ -1008,16 +1008,16 @@ CREATE TABLE `discovery`
 -- ----------------------------
 CREATE TABLE `discovery_upstream`
 (
-    `id`           varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'primary key id',
-    `discovery_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the discovery id',
-    `protocol`     varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci  COMMENT 'for http, https, tcp, ws',
-    `url`          varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'ip:port',
+    `id`           varchar(128)  NOT NULL COMMENT 'primary key id',
+    `discovery_id` varchar(128)  NOT NULL COMMENT 'the discovery id',
+    `protocol`     varchar(64)   COMMENT 'for http, https, tcp, ws',
+    `url`          varchar(64)   NOT NULL COMMENT 'ip:port',
     `status`      int(0) NOT NULL COMMENT 'type (0, healthy, 1 unhealthy)',
     `weight`      int(0) NOT NULL COMMENT 'the weight for lists',
-    `props`      text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the other field (json)',
+    `props`      text  COMMENT 'the other field (json)',
     `date_created` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'create time',
     `date_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'update time',
-    PRIMARY KEY (`id`) USING BTREE
+    PRIMARY KEY (`id`)
 );
 
 -- ----------------------------
@@ -1025,15 +1025,15 @@ CREATE TABLE `discovery_upstream`
 -- ----------------------------
 CREATE TABLE `proxy_selector`
 (
-    `id`           varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'primary key id',
-    `name`         varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the proxy name',
-    `plugin_name`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the plugin name',
-    `type`         varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'proxy type for tcp, upd, ws',
+    `id`           varchar(128)  NOT NULL COMMENT 'primary key id',
+    `name`         varchar(255)  NOT NULL COMMENT 'the proxy name',
+    `plugin_name`  varchar(255)  NOT NULL COMMENT 'the plugin name',
+    `type`         varchar(64)   NOT NULL COMMENT 'proxy type for tcp, upd, ws',
     `forward_port` int(0) NOT NULL COMMENT 'the proxy forward port',
-    `props`      text CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT 'the other field (json)',
+    `props`      text  COMMENT 'the other field (json)',
     `date_created` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'create time',
     `date_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'update time',
-    PRIMARY KEY (`id`) USING BTREE
+    PRIMARY KEY (`id`)
 );
 
 -- ----------------------------
@@ -1041,13 +1041,13 @@ CREATE TABLE `proxy_selector`
 -- ----------------------------
 CREATE TABLE `discovery_rel`
 (
-    `id`           varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'primary key id',
-    `level`        varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'plugin or selector',
-    `discovery_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the discovery id',
-    `service_id` varchar(128) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'the selector id or proxy selector id',
+    `id`           varchar(128) NOT NULL COMMENT 'primary key id',
+    `level`        varchar(64)  NOT NULL COMMENT 'plugin or selector',
+    `discovery_id` varchar(128) NOT NULL COMMENT 'the discovery id',
+    `service_id` varchar(128)   NOT NULL COMMENT 'the selector id or proxy selector id',
     `date_created` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) COMMENT 'create time',
     `date_updated` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3) COMMENT 'update time',
-    PRIMARY KEY (`id`) USING BTREE
+    PRIMARY KEY (`id`)
 );
 
 
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapperTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapperTest.java
new file mode 100644
index 000000000..c0f10d914
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/DiscoveryUpstreamMapperTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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 org.apache.shenyu.admin.mapper;
+
+import org.apache.shenyu.admin.AbstractSpringIntegrationTest;
+import org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO;
+import org.apache.shenyu.common.utils.UUIDUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class DiscoveryUpstreamMapperTest extends AbstractSpringIntegrationTest {
+
+    @Resource
+    private DiscoveryUpstreamMapper discoveryUpstreamMapper;
+
+    private final DiscoveryUpstreamDO discoveryUpstreamDO = build();
+
+    @BeforeEach
+    void setUp() {
+
+        insert();
+    }
+
+    @Test
+    void existed() {
+
+        Boolean b = discoveryUpstreamMapper.existed(discoveryUpstreamDO.getId());
+        assertEquals(true, b);
+    }
+
+    @Test
+    void selectByIds() {
+
+        List<DiscoveryUpstreamDO> dos = discoveryUpstreamMapper.selectByIds(Arrays.asList(discoveryUpstreamDO.getId()));
+        assertEquals(1, dos.size());
+        assertEquals("1", dos.get(0).getDiscoveryId());
+    }
+
+    void insert() {
+
+        int count = discoveryUpstreamMapper.insert(discoveryUpstreamDO);
+        assertEquals(1, count);
+    }
+
+    @Test
+    void update() {
+
+        discoveryUpstreamDO.setDiscoveryId("2");
+        discoveryUpstreamMapper.update(discoveryUpstreamDO);
+        List<DiscoveryUpstreamDO> dos = discoveryUpstreamMapper.selectByIds(Arrays.asList(discoveryUpstreamDO.getId()));
+        assertEquals("2", dos.get(0).getDiscoveryId());
+    }
+
+    @Test
+    void deleteByIds() {
+
+        discoveryUpstreamMapper.deleteByIds(Arrays.asList(discoveryUpstreamDO.getId()));
+        List<DiscoveryUpstreamDO> dos = discoveryUpstreamMapper.selectByIds(Arrays.asList(discoveryUpstreamDO.getId()));
+        assertEquals(0, dos.size());
+    }
+
+    private DiscoveryUpstreamDO build() {
+
+        DiscoveryUpstreamDO discoveryUpstreamDO = new DiscoveryUpstreamDO();
+        discoveryUpstreamDO.setId(UUIDUtils.getInstance().generateShortUuid());
+        discoveryUpstreamDO.setDiscoveryId("1");
+        discoveryUpstreamDO.setStatus(1);
+        discoveryUpstreamDO.setWeight(1);
+        discoveryUpstreamDO.setProps("test");
+        discoveryUpstreamDO.setUrl("test");
+        discoveryUpstreamDO.setProtocol("test");
+        discoveryUpstreamDO.setDateCreated(new Timestamp(System.currentTimeMillis()));
+        discoveryUpstreamDO.setDateUpdated(new Timestamp(System.currentTimeMillis()));
+        return discoveryUpstreamDO;
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ProxySelectorMapperTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ProxySelectorMapperTest.java
new file mode 100644
index 000000000..91d28a9dc
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ProxySelectorMapperTest.java
@@ -0,0 +1,117 @@
+/*
+ * 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 org.apache.shenyu.admin.mapper;
+
+import org.apache.shenyu.admin.AbstractSpringIntegrationTest;
+import org.apache.shenyu.admin.model.entity.ProxySelectorDO;
+import org.apache.shenyu.admin.model.page.PageParameter;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.common.utils.UUIDUtils;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import javax.annotation.Resource;
+import java.sql.Timestamp;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+class ProxySelectorMapperTest extends AbstractSpringIntegrationTest {
+
+    @Resource
+    private ProxySelectorMapper proxySelectorMapper;
+
+    private final ProxySelectorDO proxySelectorDO = build();
+
+    @BeforeEach
+    void setUp() {
+
+        insert();
+    }
+
+    @Test
+    void existed() {
+
+        Boolean b = proxySelectorMapper.existed(proxySelectorDO.getId());
+        assertEquals(true, b);
+    }
+
+    @Test
+    void selectByQuery() {
+
+        ProxySelectorDO newProxySelectorDO = build();
+        newProxySelectorDO.setName("test2");
+        proxySelectorMapper.insert(newProxySelectorDO);
+        ProxySelectorQuery query = new ProxySelectorQuery("test2", new PageParameter());
+        List<ProxySelectorDO> list = proxySelectorMapper.selectByQuery(query);
+        assertEquals(list.size(), 1);
+        assertEquals(list.get(0).getName(), "test2");
+    }
+
+    @Test
+    void nameExisted() {
+
+        Boolean b = proxySelectorMapper.nameExisted("test");
+        assertEquals(true, b);
+    }
+
+    void insert() {
+
+        int count = proxySelectorMapper.insert(proxySelectorDO);
+        assertEquals(1, count);
+    }
+
+    @Test
+    void update() {
+
+        proxySelectorDO.setName("test1");
+        int count = proxySelectorMapper.update(proxySelectorDO);
+        assertEquals(1, count);
+    }
+
+    @Test
+    void selectByIds() {
+
+        List<ProxySelectorDO> list = proxySelectorMapper.selectByIds(Arrays.asList(proxySelectorDO.getId()));
+        assertEquals(list.size(), 1);
+        assertEquals(list.get(0).getName(), "test");
+    }
+
+    @Test
+    void deleteByIds() {
+
+        proxySelectorMapper.deleteByIds(Arrays.asList(proxySelectorDO.getId()));
+        Boolean b = proxySelectorMapper.existed(1);
+        assertEquals(null, b);
+    }
+
+    private ProxySelectorDO build() {
+
+        ProxySelectorDO proxySelectorDO = new ProxySelectorDO();
+        proxySelectorDO.setId(UUIDUtils.getInstance().generateShortUuid());
+        proxySelectorDO.setName("test");
+        proxySelectorDO.setPluginName("test");
+        proxySelectorDO.setForwardPort(8080);
+        proxySelectorDO.setType("tcp");
+        proxySelectorDO.setProps("test");
+        proxySelectorDO.setDateCreated(new Timestamp(System.currentTimeMillis()));
+        proxySelectorDO.setDateUpdated(new Timestamp(System.currentTimeMillis()));
+        return proxySelectorDO;
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryUpstreamServiceTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryUpstreamServiceTest.java
new file mode 100644
index 000000000..fccbbb5c7
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryUpstreamServiceTest.java
@@ -0,0 +1,81 @@
+/*
+ * 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 org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.mapper.DiscoveryUpstreamMapper;
+import org.apache.shenyu.admin.model.dto.DiscoveryUpstreamDTO;
+import org.apache.shenyu.admin.model.entity.DiscoveryUpstreamDO;
+import org.apache.shenyu.admin.service.impl.DiscoveryUpstreamServiceImpl;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+
+import java.util.Arrays;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class DiscoveryUpstreamServiceTest {
+
+    @InjectMocks
+    private DiscoveryUpstreamServiceImpl discoveryUpstreamService;
+
+    @Mock
+    private DiscoveryUpstreamMapper discoveryUpstreamMapper;
+
+    @BeforeEach
+    void setUp() {
+
+        discoveryUpstreamService = new DiscoveryUpstreamServiceImpl(discoveryUpstreamMapper);
+    }
+
+    @Test
+    void createOrUpdate() {
+
+        DiscoveryUpstreamDTO discoveryUpstreamDTO = new DiscoveryUpstreamDTO();
+        discoveryUpstreamDTO.setDiscoveryId("1");
+        discoveryUpstreamDTO.setProps("test");
+        discoveryUpstreamDTO.setProtocol("test");
+        discoveryUpstreamDTO.setUrl("test");
+        discoveryUpstreamDTO.setWeight(1);
+        discoveryUpstreamDTO.setStatus(1);
+        given(discoveryUpstreamMapper.insert(DiscoveryUpstreamDO.buildDiscoveryUpstreamDO(discoveryUpstreamDTO)))
+                .willReturn(1);
+        assertEquals(ShenyuResultMessage.CREATE_SUCCESS, discoveryUpstreamService.createOrUpdate(discoveryUpstreamDTO));
+        discoveryUpstreamDTO.setId("1");
+        given(discoveryUpstreamMapper.update(DiscoveryUpstreamDO.buildDiscoveryUpstreamDO(discoveryUpstreamDTO)))
+                .willReturn(1);
+        assertEquals(ShenyuResultMessage.UPDATE_SUCCESS, discoveryUpstreamService.createOrUpdate(discoveryUpstreamDTO));
+
+    }
+
+    @Test
+    void delete() {
+
+        given(discoveryUpstreamMapper.deleteByIds(Arrays.asList("1"))).willReturn(1);
+        assertEquals(ShenyuResultMessage.DELETE_SUCCESS, discoveryUpstreamService.delete(Arrays.asList("1")));
+    }
+}
diff --git a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ProxySelectorServiceTest.java b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ProxySelectorServiceTest.java
new file mode 100644
index 000000000..868b6f1fe
--- /dev/null
+++ b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ProxySelectorServiceTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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 org.apache.shenyu.admin.service;
+
+import org.apache.shenyu.admin.mapper.ProxySelectorMapper;
+import org.apache.shenyu.admin.model.dto.ProxySelectorDTO;
+import org.apache.shenyu.admin.model.entity.ProxySelectorDO;
+import org.apache.shenyu.admin.model.page.PageParameter;
+import org.apache.shenyu.admin.model.query.ProxySelectorQuery;
+import org.apache.shenyu.admin.service.impl.ProxySelectorServiceImpl;
+import org.apache.shenyu.admin.utils.ShenyuResultMessage;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.mockito.junit.jupiter.MockitoSettings;
+import org.mockito.quality.Strictness;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.BDDMockito.given;
+
+@ExtendWith(MockitoExtension.class)
+@MockitoSettings(strictness = Strictness.LENIENT)
+class ProxySelectorServiceTest {
+
+    @InjectMocks
+    private ProxySelectorServiceImpl proxySelectorService;
+
+    @Mock
+    private ProxySelectorMapper proxySelectorMapper;
+
+    @BeforeEach
+    void testSetUp() {
+
+        proxySelectorService = new ProxySelectorServiceImpl(proxySelectorMapper);
+    }
+
+    @Test
+    void testListByPage() {
+
+        final ProxySelectorQuery proxySelectorQuery = new ProxySelectorQuery("test", new PageParameter());
+        final List<ProxySelectorDO> list = new ArrayList<>();
+        ProxySelectorDO proxySelectorDO = new ProxySelectorDO();
+        proxySelectorDO.setId("123");
+        proxySelectorDO.setName("test");
+        proxySelectorDO.setPluginName("test");
+        proxySelectorDO.setForwardPort(8080);
+        proxySelectorDO.setProps("test");
+        proxySelectorDO.setDateCreated(new Timestamp(System.currentTimeMillis()));
+        proxySelectorDO.setDateUpdated(new Timestamp(System.currentTimeMillis()));
+        list.add(proxySelectorDO);
+        given(this.proxySelectorMapper.selectByQuery(proxySelectorQuery)).willReturn(list);
+        assertEquals(proxySelectorService.listByPage(proxySelectorQuery).getDataList().size(), list.size());
+    }
+
+    @Test
+    void testCreateOrUpdate() {
+
+        ProxySelectorDTO proxySelectorDTO = new ProxySelectorDTO();
+        proxySelectorDTO.setName("test");
+        proxySelectorDTO.setPluginName("test");
+        proxySelectorDTO.setForwardPort(8080);
+        proxySelectorDTO.setProps("test");
+        given(proxySelectorMapper.nameExisted("test")).willReturn(null);
+        given(proxySelectorMapper.insert(ProxySelectorDO.buildProxySelectorDO(proxySelectorDTO))).willReturn(1);
+        assertEquals(proxySelectorService.createOrUpdate(proxySelectorDTO), ShenyuResultMessage.CREATE_SUCCESS);
+        proxySelectorDTO.setId("123");
+        given(proxySelectorMapper.update(ProxySelectorDO.buildProxySelectorDO(proxySelectorDTO))).willReturn(1);
+        assertEquals(proxySelectorService.createOrUpdate(proxySelectorDTO), ShenyuResultMessage.UPDATE_SUCCESS);
+    }
+
+    @Test
+    void testDelete() {
+
+        List<String> ids = new ArrayList<>();
+        ids.add("123");
+        given(proxySelectorMapper.deleteByIds(ids)).willReturn(1);
+        assertEquals(proxySelectorService.delete(ids), ShenyuResultMessage.DELETE_SUCCESS);
+    }
+}
diff --git a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java
index 9de092d5f..1bc787407 100644
--- a/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java
+++ b/shenyu-common/src/main/java/org/apache/shenyu/common/constant/AdminConstants.java
@@ -270,5 +270,8 @@ public final class AdminConstants {
 
     public static final String PLUGIN_ABSTRACR_PATH = "org.apache.shenyu.plugin.base.AbstractShenyuPlugin";
 
+    public static final String PROXY_SELECTOR_NAME_IS_EXIST = "The proxy selector name already exists and can't be added repeatedly!";
+
+    public static final String PROXY_SELECTOR_ID_IS_NOT_EXIST = "The proxy selector(s) does not exist";
 }