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

hefengen 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 7178b824e [ISSUE #4891] <Add a locking mechanism to solve the problem 
of repeatedly inserting swagger apidoc&modify the swagger example.> (#4892)
7178b824e is described below

commit 7178b824e9f6554ae27eb8bfea70f3b27a200fa9
Author: lianjunwei <[email protected]>
AuthorDate: Mon Jul 24 20:07:31 2023 +0800

    [ISSUE #4891] <Add a locking mechanism to solve the problem of repeatedly 
inserting swagger apidoc&modify the swagger example.> (#4892)
    
    * apidoc sql
    
    * refact
    
    * commit
    
    * [Task] Shenyu-admin: Fix API document failed to build because of NPE.
    
    * [Task] Shenyu-admin: Fix API document failed to build because of NPE.
    
    * solve conficts,modify LICENSE.
    
    * delete useless code.
    
    * delete useless code.
    
    * commit
    
    * [ISSUE #3843]admin apidoc fix: the required attribute prompt is incorrect 
when micro service parameter uses "@ApiModelProperty".
    
    * commit
    
    * [shenyu-examples]add swagger to the example project to test the apidoc 
function of the gateway management system.
    
    * commit
    
    * commit
    
    * commit
    
    * [ISSUE #4690]Supports gzip compression in response to HTTP requests.
    
    * [examples]Add Swagger sample project to demonstrate automatic pull 
interface documentation.
    
    * delete exapmple
    
    * delete useless code.
    
    * delete useless code.
    
    * add doc lock, and modify swagger example.
    
    * fix NPE.
    
    ---------
    
    Co-authored-by: lianjunwei <[email protected]>
    Co-authored-by: dragon-zhang <[email protected]>
    Co-authored-by: xiaoyu <[email protected]>
---
 .../apache/shenyu/admin/model/entity/TagDO.java    | 18 +++++
 .../org/apache/shenyu/admin/model/vo/TagVO.java    | 18 +++++
 .../apache/shenyu/admin/service/TagService.java    | 11 ++-
 .../shenyu/admin/service/impl/TagServiceImpl.java  | 20 +++++-
 .../manager/impl/LoadServiceDocEntryImpl.java      | 11 ++-
 .../manager/impl/PullSwaggerDocServiceImpl.java    | 78 +++++++++++++++++-----
 .../manager/impl/RegisterApiDocServiceImpl.java    |  4 +-
 .../http/ShenyuTestSwaggerApplication.java         |  2 +-
 .../http/controller/HttpTestController.java        | 36 +++++++++-
 .../examples/http/controller/OauthController.java  |  4 --
 .../examples/http/controller/OrderController.java  |  2 +-
 .../http/controller/RequestController.java         |  7 +-
 .../controller/SpringMvcMappingPathController.java | 12 ++--
 .../examples/http/controller/UploadController.java | 10 +--
 .../apache/shenyu/examples/http/dto/OrderDTO.java  | 21 ++++++
 .../shenyu/examples/http/dto/RequestDTO.java       |  4 +-
 .../{dto/OrderDTO.java => result/TreeResult.java}  | 76 ++++++++++++++-------
 17 files changed, 260 insertions(+), 74 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/TagDO.java 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/TagDO.java
index 22b85a421..314783652 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/TagDO.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/entity/TagDO.java
@@ -324,6 +324,8 @@ public final class TagDO extends BaseDO {
          */
         private String apiDocMd5;
 
+        private String docLock;
+
         /**
          * get id.
          * @return id
@@ -422,6 +424,22 @@ public final class TagDO extends BaseDO {
         public void setApiDocMd5(final String apiDocMd5) {
             this.apiDocMd5 = apiDocMd5;
         }
+
+        /**
+         * get docLock.
+         * @return docLock
+         */
+        public String getDocLock() {
+            return docLock;
+        }
+
+        /**
+         * set docLock.
+         * @param docLock docLock
+         */
+        public void setDocLock(final String docLock) {
+            this.docLock = docLock;
+        }
     }
 
 }
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/TagVO.java 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/TagVO.java
index 52c4a368e..2e26c2dc3 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/TagVO.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/model/vo/TagVO.java
@@ -53,6 +53,8 @@ public class TagVO implements Serializable {
      */
     private String ext;
 
+    private TagDO.TagExt tagExt;
+
     /**
      * created time.
      */
@@ -165,6 +167,22 @@ public class TagVO implements Serializable {
         this.ext = ext;
     }
 
+    /**
+     * get tagExt.
+     * @return tagExt
+     */
+    public TagDO.TagExt getTagExt() {
+        return tagExt;
+    }
+
+    /**
+     * set tagExt.
+     * @param tagExt tagExt
+     */
+    public void setTagExt(final TagDO.TagExt tagExt) {
+        this.tagExt = tagExt;
+    }
+
     /**
      * get create time.
      * @return createtime
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/TagService.java 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/TagService.java
index cec65ffdb..fcce6e5cc 100644
--- a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/TagService.java
+++ b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/TagService.java
@@ -29,13 +29,22 @@ import org.apache.shenyu.admin.model.vo.TagVO;
 public interface TagService {
 
     /**
-     * create or update tag.
+     * create tag.
      *
      * @param tagDTO {@linkplain TagDTO}
      * @return rows int
      */
     int create(TagDTO tagDTO);
 
+    /**
+     * create root tag.
+     *
+     * @param tagDTO tagDTO {@linkplain TagDTO}
+     * @param tagExt tagDTO {@linkplain TagDO.TagExt}
+     * @return rows int
+     */
+    int createRootTag(TagDTO tagDTO, TagDO.TagExt tagExt);
+
     /**
      * create or update tag.
      *
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/TagServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/TagServiceImpl.java
index d453f5a7f..4210e6367 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/TagServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/TagServiceImpl.java
@@ -26,6 +26,7 @@ import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.shenyu.admin.mapper.TagMapper;
 import org.apache.shenyu.admin.model.dto.TagDTO;
 import org.apache.shenyu.admin.model.entity.BaseDO;
@@ -52,15 +53,30 @@ public class TagServiceImpl implements TagService {
 
     @Override
     public int create(final TagDTO tagDTO) {
+        tagDTO.setParentTagId(StringUtils.isNotEmpty(tagDTO.getParentTagId()) 
? tagDTO.getParentTagId() : AdminConstants.TAG_ROOT_PARENT_ID);
+        return createInner(tagDTO, null);
+    }
+
+    @Override
+    public int createRootTag(final TagDTO tagDTO, final TagDO.TagExt tagExt) {
+        Assert.notNull(tagDTO, "tagDTO is not allowed null");
+        tagDTO.setParentTagId(StringUtils.isNotEmpty(tagDTO.getParentTagId()) 
? tagDTO.getParentTagId() : AdminConstants.TAG_ROOT_PARENT_ID);
+        return createInner(tagDTO, tagExt);
+    }
+
+    private int createInner(final TagDTO tagDTO, final TagDO.TagExt tagExt) {
         Assert.notNull(tagDTO, "tagDTO is not allowed null");
         Assert.notNull(tagDTO.getParentTagId(), "parent tag id is not allowed 
null");
         String ext = "";
         if 
(!tagDTO.getParentTagId().equals(AdminConstants.TAG_ROOT_PARENT_ID)) {
             TagDO tagDO = 
tagMapper.selectByPrimaryKey(tagDTO.getParentTagId());
             ext = buildExtParamByParentTag(tagDO);
+        } else {
+            ext = GsonUtils.getInstance().toJson(tagExt);
         }
         TagDO tagDO = TagDO.buildTagDO(tagDTO);
         tagDO.setExt(ext);
+        tagDTO.setId(tagDO.getId());
         return tagMapper.insert(tagDO);
     }
 
@@ -120,7 +136,7 @@ public class TagServiceImpl implements TagService {
         List<String> rootIds = 
tagDOS.stream().map(TagDO::getId).collect(Collectors.toList());
         List<TagDO> tagDOList = tagMapper.selectByParentTagIds(rootIds);
         Map<String, Boolean> map = tagDOList.stream().collect(
-            Collectors.toMap(TagDO::getParentTagId, tagDO -> true, (a, b) -> 
b, ConcurrentHashMap::new));
+                Collectors.toMap(TagDO::getParentTagId, tagDO -> true, (a, b) 
-> b, ConcurrentHashMap::new));
         return tagDOS.stream().map(tag -> {
             TagVO tagVO = TagVO.buildTagVO(tag);
             if (map.get(tag.getId()) != null) {
@@ -138,7 +154,7 @@ public class TagServiceImpl implements TagService {
     private void updateSubTags(final TagDTO tagDTO) {
         List<TagDO> allData = tagMapper.selectByQuery(new TagQuery());
         Map<String, TagDO> allDataMap = allData.stream().collect(
-            Collectors.toMap(BaseDO::getId, Function.identity(), (a, b) -> b, 
ConcurrentHashMap::new));
+                Collectors.toMap(BaseDO::getId, Function.identity(), (a, b) -> 
b, ConcurrentHashMap::new));
         TagDO update = TagDO.buildTagDO(tagDTO);
         allDataMap.put(update.getId(), update);
         Map<String, List<String>> relationMap = new ConcurrentHashMap<>();
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/LoadServiceDocEntryImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/LoadServiceDocEntryImpl.java
index 6b6971918..adaf6dba4 100755
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/LoadServiceDocEntryImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/LoadServiceDocEntryImpl.java
@@ -96,7 +96,7 @@ public class LoadServiceDocEntryImpl implements 
LoadServiceDocEntry {
             return;
         }
         final Set<UpstreamInstance> currentServices = new 
HashSet<>(serviceList);
-        LOG.info("load api document  serviceList={}", 
JsonUtils.toJson(currentServices));
+        LOG.info("load api document, serviceList={}", 
JsonUtils.toJson(currentServices));
         pullSwaggerDocService.pullApiDocument(currentServices);
     }
 
@@ -112,7 +112,7 @@ public class LoadServiceDocEntryImpl implements 
LoadServiceDocEntry {
                 return;
             }
             final Set<UpstreamInstance> currentServices = new 
HashSet<>(serviceList);
-            LOG.info("loadDocOnSelectorChanged serviceList={}", 
JsonUtils.toJson(currentServices));
+            LOG.info("loadDocOnSelectorChanged, serviceList={}", 
JsonUtils.toJson(currentServices));
             pullSwaggerDocService.pullApiDocument(currentServices);
         }
     }
@@ -155,7 +155,7 @@ public class LoadServiceDocEntryImpl implements 
LoadServiceDocEntry {
         CommonPager<SelectorVO> commonPager = selectorService.listByPage(new 
SelectorQuery(Lists.newArrayList(supportSwaggerPluginMap.keySet()), null, new 
PageParameter(1, Integer.MAX_VALUE)));
         List<SelectorVO> clusterList = commonPager.getDataList();
         if (CollectionUtils.isEmpty(clusterList)) {
-            LOG.info("getAllClusterLastUpdateInstanceList, Not loaded into 
available backend services.");
+            LOG.info("getAllClusterLastUpdateInstanceList. Not loaded into 
available backend services.");
             return Collections.emptyList();
         }
         return clusterList.parallelStream()
@@ -174,7 +174,7 @@ public class LoadServiceDocEntryImpl implements 
LoadServiceDocEntry {
 
     private UpstreamInstance getClusterLastUpdateInstance(final SelectorData 
selectorData) {
         if (!supportSwaggerPluginMap.containsKey(selectorData.getPluginId())) {
-            LOG.info("getClusterLastUpdateInstance. pluginNae={} does not 
support pulling API documents.", selectorData.getPluginName());
+            LOG.info("getClusterLastUpdateInstance. pluginName={} does not 
support pulling API documents.", selectorData.getPluginName());
             return null;
         }
         List<UpstreamInstance> allInstances = 
getInstances(selectorData.getPluginId(), selectorData.getHandle(), 
selectorData.getName(), selectorData.getEnabled());
@@ -195,8 +195,7 @@ public class LoadServiceDocEntryImpl implements 
LoadServiceDocEntry {
                 .orElse(null);
     }
 
-    private List<UpstreamInstance> getInstances(final String pluginId, final 
String handle, final String contextPath,
-        final boolean enabled) {
+    private List<UpstreamInstance> getInstances(final String pluginId, final 
String handle, final String contextPath, final boolean enabled) {
         List<UpstreamInstance> allInstances = null;
         // Get service instance.
         if (StringUtils.isNotEmpty(handle)) {
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/PullSwaggerDocServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/PullSwaggerDocServiceImpl.java
index b920b7db4..1f2606140 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/PullSwaggerDocServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/PullSwaggerDocServiceImpl.java
@@ -17,6 +17,8 @@
 
 package org.apache.shenyu.admin.service.manager.impl;
 
+import com.google.common.collect.Interner;
+import com.google.common.collect.Interners;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -24,7 +26,9 @@ import java.util.Set;
 import javax.annotation.Resource;
 import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
 import org.apache.shenyu.admin.model.bean.UpstreamInstance;
+import org.apache.shenyu.admin.model.dto.TagDTO;
 import org.apache.shenyu.admin.model.entity.TagDO;
 import org.apache.shenyu.admin.model.vo.TagVO;
 import org.apache.shenyu.admin.service.TagService;
@@ -32,7 +36,7 @@ import org.apache.shenyu.admin.service.manager.DocManager;
 import org.apache.shenyu.admin.service.manager.PullSwaggerDocService;
 import org.apache.shenyu.admin.utils.HttpUtils;
 import org.apache.shenyu.common.constant.AdminConstants;
-import org.apache.shenyu.common.utils.JsonUtils;
+import org.apache.shenyu.common.utils.GsonUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
@@ -48,7 +52,11 @@ public class PullSwaggerDocServiceImpl implements 
PullSwaggerDocService {
 
     private static final String SWAGGER_V2_PATH = "/v2/api-docs";
 
-    private static final int PULL_MIN_INTERVAL_TIME = 30 * 1000;
+    private static final long PULL_MIN_INTERVAL_TIME = 30 * 1000;
+
+    private static final long DOC_LOCK_EXPIRED_TIME = 60 * 1000;
+
+    private final Interner<Object> interner = Interners.newWeakInterner();
 
     @Resource
     private DocManager docManager;
@@ -69,12 +77,15 @@ public class PullSwaggerDocServiceImpl implements 
PullSwaggerDocService {
     @Override
     @SuppressWarnings("unchecked")
     public void pullApiDocument(final UpstreamInstance instance) {
-        TagVO tagVO = getTagVO(instance);
-        TagDO.TagExt tagExt = Objects.nonNull(tagVO) ? 
convertTagExt(tagVO.getExt()) : new TagDO.TagExt();
-        if (!canPull(instance, tagExt.getRefreshTime())) {
-            LOG.info("api document has been pulled and cannot be pulled 
again,instance: {}", instance.getClusterName());
-            return;
+        TagVO tagVO = null;
+        synchronized (interner.intern(instance.getClusterName())) {
+            tagVO = saveTagVOAndAcquireLock(instance);
+            if (!canPull(instance, tagVO)) {
+                LOG.info("api document has been pulled and cannot be pulled 
again,instance: {}", instance.getClusterName());
+                return;
+            }
         }
+        TagDO.TagExt tagExt = tagVO.getTagExt();
         long newRefreshTime = System.currentTimeMillis();
         String url = getSwaggerRequestUrl(instance);
         try {
@@ -89,31 +100,66 @@ public class PullSwaggerDocServiceImpl implements 
PullSwaggerDocService {
                 }
             );
             tagExt.setRefreshTime(newRefreshTime);
-            //Save the time of the last updated document and the newMd5 of 
apidoc.
-            tagVO = Objects.nonNull(tagVO) ? tagVO : getTagVO(instance);
-            if (Objects.nonNull(tagVO)) {
-                tagService.updateTagExt(tagVO.getId(), tagExt);
-            }
         } catch (Exception e) {
             LOG.error("add api document fail. clusterName={} url={} error={}", 
instance.getClusterName(), url, e);
+        } finally {
+            tagExt.setDocLock(null);
+            //Save the time of the last updated document and the newMd5 of 
apidoc.
+            tagService.updateTagExt(tagVO.getId(), tagExt);
         }
     }
 
-    private boolean canPull(final UpstreamInstance instance, final Long 
cacheLastStartUpTime) {
+    private boolean canPull(final UpstreamInstance instance, final TagVO 
tagVO) {
         boolean canPull = false;
+        if (Objects.isNull(tagVO) || Objects.isNull(tagVO.getTagExt()) || 
StringUtils.isEmpty(tagVO.getTagExt().getDocLock())) {
+            LOG.info("Unable to obtain lock for {}, retry after {} seconds.", 
instance.getClusterName(), DOC_LOCK_EXPIRED_TIME / 1000);
+            return false;
+        }
+        Long cacheLastStartUpTime = tagVO.getTagExt().getRefreshTime();
         if (Objects.isNull(cacheLastStartUpTime) || instance.getStartupTime() 
> cacheLastStartUpTime + PULL_MIN_INTERVAL_TIME) {
             canPull = true;
         }
         return canPull;
     }
 
-    private TagVO getTagVO(final UpstreamInstance instance) {
+    private TagVO saveTagVOAndAcquireLock(final UpstreamInstance instance) {
         List<TagVO> tagVOList = 
tagService.findByQuery(instance.getContextPath(), 
AdminConstants.TAG_ROOT_PARENT_ID);
-        return CollectionUtils.isNotEmpty(tagVOList) ? tagVOList.get(0) : null;
+        if (CollectionUtils.isNotEmpty(tagVOList)) {
+            TagVO tagVO = tagVOList.get(0);
+            TagDO.TagExt tagExt = convertTagExt(tagVO.getExt());
+            tagVO.setTagExt(tagExt);
+            if (StringUtils.isNotEmpty(tagExt.getDocLock()) && 
NumberUtils.toLong(tagExt.getDocLock(), 0) > System.currentTimeMillis()) {
+                tagExt.setDocLock(null);
+                return tagVO;
+            }
+            tagExt.setDocLock(this.generateDocLock());
+            tagService.updateTagExt(tagVO.getId(), tagExt);
+            return tagVO;
+        }
+        return createRootTagAndAcquireLock(instance);
+    }
+
+    private TagVO createRootTagAndAcquireLock(final UpstreamInstance instance) 
{
+        TagDTO tagDTO = new TagDTO();
+        tagDTO.setTagDesc(instance.getClusterName());
+        tagDTO.setName(instance.getContextPath());
+        tagDTO.setParentTagId(AdminConstants.TAG_ROOT_PARENT_ID);
+        TagDO.TagExt tagExt = new TagDO.TagExt();
+        tagExt.setDocLock(this.generateDocLock());
+        tagService.createRootTag(tagDTO, tagExt);
+
+        TagVO tagVO = new TagVO();
+        tagVO.setId(tagDTO.getId());
+        tagVO.setTagExt(tagExt);
+        return tagVO;
+    }
+
+    private String generateDocLock() {
+        return String.valueOf(System.currentTimeMillis() + 
DOC_LOCK_EXPIRED_TIME);
     }
 
     private TagDO.TagExt convertTagExt(final String ext) {
-        return StringUtils.isNotEmpty(ext) ? JsonUtils.jsonToObject(ext, 
TagDO.TagExt.class) : new TagDO.TagExt();
+        return StringUtils.isNotEmpty(ext) ? 
GsonUtils.getInstance().fromJson(ext, TagDO.TagExt.class) : new TagDO.TagExt();
     }
 
     private String getSwaggerRequestUrl(final UpstreamInstance instance) {
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/RegisterApiDocServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/RegisterApiDocServiceImpl.java
index 334a04a7e..020988a3e 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/RegisterApiDocServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/manager/impl/RegisterApiDocServiceImpl.java
@@ -29,7 +29,6 @@ import org.apache.shenyu.admin.model.vo.TagVO;
 import org.apache.shenyu.admin.service.ApiService;
 import org.apache.shenyu.admin.service.TagService;
 import org.apache.shenyu.admin.service.manager.RegisterApiDocService;
-import org.apache.shenyu.common.constant.AdminConstants;
 import org.apache.shenyu.common.utils.UUIDUtils;
 import org.apache.shenyu.register.common.dto.ApiDocRegisterDTO;
 import org.apache.shenyu.register.common.enums.EventType;
@@ -69,9 +68,8 @@ public class RegisterApiDocServiceImpl implements 
RegisterApiDocService {
                     String id = UUIDUtils.getInstance().generateShortUuid();
                     tagDTO.setTagDesc(tag);
                     tagDTO.setName(tag);
-                    tagDTO.setParentTagId(AdminConstants.TAG_ROOT_PARENT_ID);
                     tagDTO.setId(id);
-                    tagService.create(tagDTO);
+                    tagService.createRootTag(tagDTO, null);
                     tagsIds.add(id);
                 }
             }
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/ShenyuTestSwaggerApplication.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/ShenyuTestSwaggerApplication.java
index ccd141b3b..cb2b6ceef 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/ShenyuTestSwaggerApplication.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/ShenyuTestSwaggerApplication.java
@@ -21,7 +21,7 @@ import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 /**
- * ShenyuTestHttpApplication.
+ * ShenyuTestSwaggerApplication.
  */
 @SpringBootApplication
 public class ShenyuTestSwaggerApplication {
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
index d2c2aef72..edc1c154e 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/HttpTestController.java
@@ -18,13 +18,16 @@
 package org.apache.shenyu.examples.http.controller;
 
 import com.google.common.collect.ImmutableMap;
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
 import io.swagger.annotations.ApiImplicitParams;
 import io.swagger.annotations.ApiOperation;
+import java.util.Arrays;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
 import org.apache.shenyu.common.utils.GsonUtils;
 import org.apache.shenyu.examples.http.dto.UserDTO;
 import org.apache.shenyu.examples.http.result.ResultBean;
+import org.apache.shenyu.examples.http.result.TreeResult;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.http.HttpHeaders;
@@ -64,6 +67,7 @@ import java.util.Map;
 @RestController
 @RequestMapping("/test")
 @ShenyuSpringMvcClient("/test/**")
+@Api(tags = "HttpBaseTestAPI")
 public class HttpTestController {
 
     private static final Logger LOGGER = 
LoggerFactory.getLogger(HttpTestController.class);
@@ -74,7 +78,7 @@ public class HttpTestController {
      * @param userDTO the user dto
      * @return the user dto
      */
-    @ApiOperation(value = "payment", notes = "The user pays the order.")
+    @ApiOperation(value = "payment", notes = "The user pays the order.", 
position = -100)
     @PostMapping("/payment")
     public UserDTO post(@RequestBody final UserDTO userDTO) {
         return userDTO;
@@ -281,6 +285,7 @@ public class HttpTestController {
      * @param requestParameter parameter
      * @return result
      */
+    @ApiOperation(value = "modifyRequestWithHeaderAndCookie", notes = "modify 
request with header and cookie.")
     @PostMapping(path = "/modifyRequest")
     public Map<String, Object> modifyRequest(@RequestBody final UserDTO 
userDTO,
         @CookieValue(value = "cookie", defaultValue = "") final String cookie,
@@ -294,6 +299,35 @@ public class HttpTestController {
         return result;
     }
 
+    /**
+     * Return Tree structure data.
+     *
+     * @param param param
+     * @return TreeResult
+     */
+    @ApiOperation(value = "retureTreeData", notes = "Return Tree structure 
data.")
+    @PostMapping("/tree/v1")
+    public TreeResult tree(final UserDTO param) {
+        int id = 0;
+        TreeResult parent = new TreeResult();
+        parent.setId(++id);
+        parent.setName("parentNode" + param.getUserName());
+        parent.setParentId(0);
+
+        TreeResult child1 = new TreeResult();
+        child1.setId(++id);
+        child1.setName("childNode1");
+        child1.setParentId(1);
+
+        TreeResult child2 = new TreeResult();
+        child2.setId(++id);
+        child2.setName("childNode2");
+        child2.setParentId(1);
+
+        parent.setChildren(Arrays.asList(child1, child2));
+        return parent;
+    }
+
     /**
      * download file.
      *
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OauthController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OauthController.java
index 7bfab2d44..be9c4f63e 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OauthController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OauthController.java
@@ -17,8 +17,6 @@
 
 package org.apache.shenyu.examples.http.controller;
 
-import org.apache.shenyu.client.apidocs.annotations.ApiDoc;
-import org.apache.shenyu.client.apidocs.annotations.ApiModule;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -30,7 +28,6 @@ import org.springframework.web.bind.annotation.RestController;
 @RestController
 @RequestMapping("/oauth")
 @ShenyuSpringMvcClient("/oauth")
-@ApiModule(value = "oauth")
 public class OauthController {
 
     /**
@@ -39,7 +36,6 @@ public class OauthController {
      * @return String
      */
     @GetMapping("/authorize")
-    @ApiDoc(desc = "authorize")
     public String testCode() {
         return "authorize";
     }
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OrderController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OrderController.java
index 1a3ee45e6..a96380ce3 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OrderController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/OrderController.java
@@ -38,7 +38,7 @@ import java.util.Objects;
 /**
  * TestController.
  */
-@Api(tags = "Order API")
+@Api(tags = "Order API", position = 2)
 @RestController
 @RequestMapping("/order")
 @ShenyuSpringMvcClient("/order")
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/RequestController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/RequestController.java
index b61229001..22b8539a9 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/RequestController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/RequestController.java
@@ -17,6 +17,7 @@
 
 package org.apache.shenyu.examples.http.controller;
 
+import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
 import org.slf4j.Logger;
@@ -34,6 +35,7 @@ import reactor.core.publisher.Mono;
 /**
  * RequestController.
  */
+@Api(tags = "RequestController", position = 1)
 @RestController
 @RequestMapping("/request")
 @ShenyuSpringMvcClient("/request/**")
@@ -48,7 +50,7 @@ public class RequestController {
      * @param serverHttpRequest request
      * @return response
      */
-    @ApiOperation(value = "header", notes = "test request header.")
+    @ApiOperation(value = "testRequestHeader", notes = "test request header.")
     @GetMapping(path = "/header")
     public Mono<String> testRequestHeader(@RequestHeader("header_key1") final 
String headerKey1,
         final ServerHttpRequest serverHttpRequest) {
@@ -63,7 +65,7 @@ public class RequestController {
      * @param serverHttpRequest request
      * @return response
      */
-    @ApiOperation(value = "parameter", notes = "test request parameter.")
+    @ApiOperation(value = "testRequestParameter", notes = "test request 
parameter.")
     @PostMapping(path = "/parameter")
     public Mono<String> testRequestParameter(@RequestParam("parameter_key1") 
final String parameterKey1,
         final ServerHttpRequest serverHttpRequest) {
@@ -78,6 +80,7 @@ public class RequestController {
      * @param serverHttpRequest request
      * @return response
      */
+    @ApiOperation(value = "testRequestCookie", notes = "test request 
parameter.")
     @GetMapping(path = "/cookie")
     public Mono<String> testRequestCookie(@CookieValue("userId") final String 
userId,
         final ServerHttpRequest serverHttpRequest) {
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMappingPathController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMappingPathController.java
index 5bf9a0b09..15130d842 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMappingPathController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/SpringMvcMappingPathController.java
@@ -17,8 +17,8 @@
 
 package org.apache.shenyu.examples.http.controller;
 
-import org.apache.shenyu.client.apidocs.annotations.ApiDoc;
-import org.apache.shenyu.client.apidocs.annotations.ApiModule;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
@@ -29,7 +29,7 @@ import org.springframework.web.bind.annotation.RestController;
  */
 @RestController
 @ShenyuSpringMvcClient(desc = "spring annotation register")
-@ApiModule(value = "springMvcMappingPathController")
+@Api(value = "springMvcMappingPathController")
 public class SpringMvcMappingPathController {
 
     private static final String HELLO_SUFFIX = "I'm Shenyu-Gateway System. 
Welcome!";
@@ -40,7 +40,7 @@ public class SpringMvcMappingPathController {
      * @return result
      */
     @RequestMapping("hello")
-    @ApiDoc(desc = "hello")
+    @ApiOperation(value = "hello", notes = "say hello.")
     public String hello() {
         return "hello! " + HELLO_SUFFIX;
     }
@@ -52,7 +52,7 @@ public class SpringMvcMappingPathController {
      * @return result
      */
     @RequestMapping("hi")
-    @ApiDoc(desc = "hi")
+    @ApiOperation(value = "hi", notes = "say hello to name.")
     public String hello(final String name) {
         return "hi! " + name + "! " + HELLO_SUFFIX;
     }
@@ -64,7 +64,7 @@ public class SpringMvcMappingPathController {
      * @return result
      */
     @PostMapping("post/hi")
-    @ApiDoc(desc = "post/hi")
+    @ApiOperation(value = "postHi", notes = "post hi.")
     public String post(final String name) {
         return "[post method result]:hi! " + name + "! " + HELLO_SUFFIX;
     }
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/UploadController.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/UploadController.java
index b81e73b40..93d1e9a2b 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/UploadController.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/controller/UploadController.java
@@ -17,9 +17,9 @@
 
 package org.apache.shenyu.examples.http.controller;
 
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import java.util.stream.Collectors;
-import org.apache.shenyu.client.apidocs.annotations.ApiDoc;
-import org.apache.shenyu.client.apidocs.annotations.ApiModule;
 import org.apache.shenyu.client.springmvc.annotation.ShenyuSpringMvcClient;
 import org.springframework.http.MediaType;
 import org.springframework.http.codec.multipart.FilePart;
@@ -36,7 +36,7 @@ import reactor.core.publisher.Mono;
 @RestController
 @RequestMapping("/upload")
 @ShenyuSpringMvcClient("/upload/**")
-@ApiModule(value = "upload")
+@Api(tags = "upload api")
 public class UploadController {
 
     /**
@@ -46,7 +46,7 @@ public class UploadController {
      * @return response
      */
     @PostMapping(value = "/webFluxSingle", consumes = 
{MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.TEXT_PLAIN_VALUE})
-    @ApiDoc(desc = "webFluxSingle")
+    @ApiOperation(value = "webFluxSingle", notes = "upload webFluxSingle.")
     public Mono<String> webFluxSingle(@RequestPart("file") final FilePart 
file) {
         return Mono.just(file.filename());
     }
@@ -58,7 +58,7 @@ public class UploadController {
      * @return response
      */
     @PostMapping(value = "/webFluxFiles", consumes = 
{MediaType.MULTIPART_FORM_DATA_VALUE, MediaType.TEXT_PLAIN_VALUE})
-    @ApiDoc(desc = "webFluxFiles")
+    @ApiOperation(value = "webFluxFiles", notes = "upload webFluxFiles.")
     public Mono<String> webFluxFiles(@RequestPart(value = "files", required = 
false) final Flux<FilePart> files) {
         return files.map(FilePart::filename).collect(Collectors.joining(","));
     }
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
index 160f85478..6f1024659 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
@@ -19,6 +19,7 @@ package org.apache.shenyu.examples.http.dto;
 
 import io.swagger.annotations.ApiModelProperty;
 import java.io.Serializable;
+import java.util.Date;
 import java.util.StringJoiner;
 
 /**
@@ -32,6 +33,9 @@ public class OrderDTO implements Serializable {
     @ApiModelProperty(value = "name", required = true, example = "jack")
     private String name;
 
+    @ApiModelProperty(value = "createTime", example = "2023-08-01 10:10:01")
+    private Date createTime;
+
     /**
      * Get id.
      *
@@ -68,11 +72,28 @@ public class OrderDTO implements Serializable {
         this.name = name;
     }
 
+    /**
+     * get createTime.
+     * @return createTime
+     */
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    /**
+     * setCreateTime.
+     * @param createTime createTime
+     */
+    public void setCreateTime(final Date createTime) {
+        this.createTime = createTime;
+    }
+
     @Override
     public String toString() {
         return new StringJoiner(", ", OrderDTO.class.getSimpleName() + "[", 
"]")
             .add("id='" + id + "'")
             .add("name='" + name + "'")
+            .add("createTime='" + createTime + "'")
             .toString();
     }
 
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/RequestDTO.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/RequestDTO.java
index 8dbcb0a00..ab506d8e6 100644
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/RequestDTO.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/RequestDTO.java
@@ -28,13 +28,13 @@ public class RequestDTO {
     @ApiModelProperty(value = "module", required = true, example = 
"usercenter")
     private String module;
 
-    @ApiModelProperty(value = "method", required = true, example = 
"findByUserId")
+    @ApiModelProperty(value = "method", required = true, example = 
"findByUserId", position = 1)
     private String method;
 
     @ApiModelProperty(value = "content", example = "hello,shenyu")
     private String content;
 
-    @ApiModelProperty(value = "extInfo", example = "extended information")
+    @ApiModelProperty(value = "extInfo", example = "extended information", 
position = 2)
     private String extInfo;
 
     public RequestDTO() {
diff --git 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/result/TreeResult.java
old mode 100644
new mode 100755
similarity index 55%
copy from 
shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
copy to 
shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/result/TreeResult.java
index 160f85478..c5f7f831e
--- 
a/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/dto/OrderDTO.java
+++ 
b/shenyu-examples/shenyu-examples-http-swagger2/src/main/java/org/apache/shenyu/examples/http/result/TreeResult.java
@@ -15,43 +15,44 @@
  * limitations under the License.
  */
 
-package org.apache.shenyu.examples.http.dto;
+package org.apache.shenyu.examples.http.result;
 
 import io.swagger.annotations.ApiModelProperty;
-import java.io.Serializable;
-import java.util.StringJoiner;
+import java.util.List;
 
-/**
- * The type Order dto.
- */
-public class OrderDTO implements Serializable {
-
-    @ApiModelProperty(value = "id", required = true, example = "100000")
-    private String id;
+public class TreeResult {
+    @ApiModelProperty(value = "id", required = true, example = "123")
+    private Integer id;
 
-    @ApiModelProperty(value = "name", required = true, example = "jack")
+    @ApiModelProperty(value = "name", required = true, example = "shenyu")
     private String name;
 
+    @ApiModelProperty(value = "parent id")
+    private Integer parentId;
+
+    @ApiModelProperty(value = "children node list", example = "list")
+    private List<TreeResult> children;
+
     /**
-     * Get id.
+     * get id.
      *
-     * @return id
+     * @return Integer
      */
-    public String getId() {
+    public Integer getId() {
         return id;
     }
 
     /**
-     * Set id.
+     * setId.
      *
      * @param id id
      */
-    public void setId(final String id) {
+    public void setId(final Integer id) {
         this.id = id;
     }
 
     /**
-     * Get name.
+     * get name.
      *
      * @return name
      */
@@ -60,7 +61,7 @@ public class OrderDTO implements Serializable {
     }
 
     /**
-     * Set name.
+     * setName.
      *
      * @param name name
      */
@@ -68,12 +69,39 @@ public class OrderDTO implements Serializable {
         this.name = name;
     }
 
-    @Override
-    public String toString() {
-        return new StringJoiner(", ", OrderDTO.class.getSimpleName() + "[", 
"]")
-            .add("id='" + id + "'")
-            .add("name='" + name + "'")
-            .toString();
+    /**
+     * getParentId.
+     *
+     * @return parentId
+     */
+    public Integer getParentId() {
+        return parentId;
     }
 
+    /**
+     * setParentId.
+     *
+     * @param parentId parentId
+     */
+    public void setParentId(final Integer parentId) {
+        this.parentId = parentId;
+    }
+
+    /**
+     * getChildren.
+     *
+     * @return list
+     */
+    public List<TreeResult> getChildren() {
+        return children;
+    }
+
+    /**
+     * setChildren.
+     *
+     * @param children children
+     */
+    public void setChildren(final List<TreeResult> children) {
+        this.children = children;
+    }
 }


Reply via email to