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

Aias00 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 2f99c24ece fix(admin): paginate API listings and batch tag lookups 
(#7235)
2f99c24ece is described below

commit 2f99c24ece8cb737ee58dfd1d469a93e15acd282
Author: Liming Deng <[email protected]>
AuthorDate: Wed Sep 30 11:46:11 2026 +0800

    fix(admin): paginate API listings and batch tag lookups (#7235)
---
 .../shenyu/admin/mapper/TagRelationMapper.java     |  8 +++++
 .../shenyu/admin/service/impl/ApiServiceImpl.java  | 30 ++++++++++-------
 .../src/main/resources/mappers/api-sqlmap.xml      |  3 +-
 .../main/resources/mappers/tag-relation-sqlmap.xml | 14 ++++++++
 .../apache/shenyu/admin/mapper/ApiMapperTest.java  | 38 ++++++++++++++++++++++
 .../shenyu/admin/mapper/TagRelationMapperTest.java | 14 ++++++++
 .../shenyu/admin/service/ApiServiceTest.java       | 24 ++++++++++++++
 7 files changed, 118 insertions(+), 13 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/TagRelationMapper.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/TagRelationMapper.java
index 67622e601e..d453173b6e 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/TagRelationMapper.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/mapper/TagRelationMapper.java
@@ -96,6 +96,14 @@ public interface TagRelationMapper extends ExistProvider {
      */
     List<TagRelationDO> selectByQuery(TagRelationQuery tagRelationQuery);
 
+    /**
+     * Select tag relations for one API page.
+     *
+     * @param apiIds API identifiers
+     * @return matching relations
+     */
+    List<TagRelationDO> selectByApiIds(@Param("apiIds") List<String> apiIds);
+
     /**
      * update record.
      *
diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java
index 8ac6ae4f74..43e02883c5 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/ApiServiceImpl.java
@@ -23,6 +23,7 @@ import org.apache.commons.collections4.CollectionUtils;
 import org.apache.commons.lang3.StringUtils;
 import java.util.Objects;
 import 
org.apache.shenyu.admin.disruptor.RegisterClientServerDisruptorPublisher;
+import org.apache.shenyu.admin.aspect.annotation.Pageable;
 import org.apache.shenyu.admin.mapper.ApiMapper;
 import org.apache.shenyu.admin.mapper.TagMapper;
 import org.apache.shenyu.admin.mapper.TagRelationMapper;
@@ -59,6 +60,8 @@ import 
org.springframework.transaction.annotation.Transactional;
 
 import java.sql.Timestamp;
 import java.util.List;
+import java.util.Map;
+import java.util.Collections;
 import java.util.Optional;
 import java.util.stream.Collectors;
 
@@ -265,18 +268,23 @@ public class ApiServiceImpl implements ApiService {
     }
 
     @Override
+    @Pageable
     public CommonPager<ApiVO> listByPage(final ApiQuery apiQuery) {
-        return PageResultUtils.result(apiQuery.getPageParameter(), () -> 
apiMapper.selectByQuery(apiQuery)
-                .stream().map(item -> {
-                    List<TagRelationDO> tagRelations = 
tagRelationMapper.selectByQuery(TagRelationQuery.builder().apiId(item.getId()).build());
-                    List<String> tagIds = 
tagRelations.stream().map(TagRelationDO::getTagId).collect(Collectors.toList());
-                    List<TagVO> tagVOS = Lists.newArrayList();
-                    if (CollectionUtils.isNotEmpty(tagIds)) {
-                        List<TagDO> tagDOS = tagMapper.selectByIds(tagIds);
-                        tagVOS = 
tagDOS.stream().map(TagVO::buildTagVO).collect(Collectors.toList());
-                    }
-                    return ApiVO.buildApiVO(item, tagVOS);
-                }).collect(Collectors.toList()));
+        List<ApiDO> apis = apiMapper.selectByQuery(apiQuery);
+        if (apis.isEmpty()) {
+            return PageResultUtils.result(apiQuery.getPageParameter(), 
Collections::emptyList);
+        }
+        List<String> apiIds = 
apis.stream().map(ApiDO::getId).collect(Collectors.toList());
+        List<TagRelationDO> relations = 
tagRelationMapper.selectByApiIds(apiIds);
+        List<String> tagIds = 
relations.stream().map(TagRelationDO::getTagId).filter(Objects::nonNull).distinct().collect(Collectors.toList());
+        Map<String, TagVO> tags = tagIds.isEmpty() ? Collections.emptyMap() : 
tagMapper.selectByIds(tagIds).stream()
+                .collect(Collectors.toMap(TagDO::getId, TagVO::buildTagVO));
+        Map<String, List<TagRelationDO>> relationsByApi = 
relations.stream().collect(Collectors.groupingBy(TagRelationDO::getApiId));
+        return PageResultUtils.result(apiQuery.getPageParameter(), () -> 
apis.stream().map(api -> {
+            List<TagVO> apiTags = relationsByApi.getOrDefault(api.getId(), 
Collections.emptyList()).stream()
+                    
.map(TagRelationDO::getTagId).distinct().map(tags::get).filter(Objects::nonNull).collect(Collectors.toList());
+            return ApiVO.buildApiVO(api, apiTags);
+        }).collect(Collectors.toList()));
     }
 
     @Override
diff --git a/shenyu-admin/src/main/resources/mappers/api-sqlmap.xml 
b/shenyu-admin/src/main/resources/mappers/api-sqlmap.xml
index 2192ffefe1..f011b0c9b9 100644
--- a/shenyu-admin/src/main/resources/mappers/api-sqlmap.xml
+++ b/shenyu-admin/src/main/resources/mappers/api-sqlmap.xml
@@ -280,7 +280,6 @@
            api.api_owner,
            api.api_desc,
            api.api_source,
-           api.document,
            api.document_md5,
            api.date_created,
            api.date_updated
@@ -297,7 +296,7 @@
         and state = #{state, jdbcType=TINYINT}
       </if>
     </where>
-    order by api.date_created desc
+    order by api.date_created desc, api.id desc
   </select>
 
   <select id="selectByIds" parameterType="java.util.List" 
resultMap="BaseResultMap">
diff --git a/shenyu-admin/src/main/resources/mappers/tag-relation-sqlmap.xml 
b/shenyu-admin/src/main/resources/mappers/tag-relation-sqlmap.xml
index 7dc16797ef..5e63b730ac 100644
--- a/shenyu-admin/src/main/resources/mappers/tag-relation-sqlmap.xml
+++ b/shenyu-admin/src/main/resources/mappers/tag-relation-sqlmap.xml
@@ -193,4 +193,18 @@
             #{id, jdbcType=VARCHAR}
         </foreach>
     </delete>
+    <select id="selectByApiIds" resultMap="BaseResultMap">
+        SELECT <include refid="Base_Column_List"/>
+        FROM tag_relation
+        WHERE
+        <choose>
+            <when test="apiIds != null and apiIds.size() > 0">
+                api_id IN
+                <foreach collection="apiIds" item="apiId" open="(" 
separator="," close=")">
+                    #{apiId, jdbcType=VARCHAR}
+                </foreach>
+            </when>
+            <otherwise>1 = 0</otherwise>
+        </choose>
+    </select>
 </mapper>
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ApiMapperTest.java 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ApiMapperTest.java
index 232ef66eaa..cd5a1dda76 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ApiMapperTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/ApiMapperTest.java
@@ -21,6 +21,9 @@ import org.apache.shenyu.admin.AbstractSpringIntegrationTest;
 import org.apache.shenyu.admin.model.entity.ApiDO;
 import org.apache.shenyu.admin.model.page.PageParameter;
 import org.apache.shenyu.admin.model.query.ApiQuery;
+import org.apache.shenyu.admin.model.page.CommonPager;
+import org.apache.shenyu.admin.model.vo.ApiVO;
+import org.apache.shenyu.admin.service.ApiService;
 import org.apache.shenyu.common.utils.UUIDUtils;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -34,6 +37,8 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.greaterThan;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
 
 /**
  * Test cases for ApiMapper.
@@ -43,6 +48,9 @@ public final class ApiMapperTest extends 
AbstractSpringIntegrationTest {
     @Resource
     private ApiMapper apiMapper;
 
+    @Resource
+    private ApiService apiService;
+
     private final ApiDO apiDO = buildApiDO();
 
     @BeforeEach
@@ -51,6 +59,36 @@ public final class ApiMapperTest extends 
AbstractSpringIntegrationTest {
         assertEquals(1, count);
     }
 
+    @Test
+    public void testApiListPaginationExcludesDocument() {
+        ApiDO first = buildApiDO();
+        String path = "/pagination/" + first.getId();
+        first.setApiPath(path);
+        first.setDocument("large document");
+        ApiDO second = buildApiDO();
+        second.setApiPath(path);
+        apiMapper.insert(first);
+        apiMapper.insert(second);
+        try {
+            ApiQuery query = new ApiQuery();
+            query.setApiPath(path);
+            query.setPageParameter(new PageParameter(1, 1));
+            CommonPager<ApiVO> pageOne = apiService.listByPage(query);
+            query.setPageParameter(new PageParameter(2, 1));
+            CommonPager<ApiVO> pageTwo = apiService.listByPage(query);
+            assertEquals(2, pageOne.getPage().getTotalCount());
+            assertEquals(1, pageOne.getDataList().size());
+            assertEquals(1, pageTwo.getDataList().size());
+            assertNotEquals(pageOne.getDataList().get(0).getId(), 
pageTwo.getDataList().get(0).getId());
+            assertNull(pageOne.getDataList().get(0).getDocument());
+            assertNull(pageTwo.getDataList().get(0).getDocument());
+            assertEquals("large document", 
apiMapper.selectByPrimaryKey(first.getId()).getDocument());
+        } finally {
+            apiMapper.deleteByPrimaryKey(first.getId());
+            apiMapper.deleteByPrimaryKey(second.getId());
+        }
+    }
+
     @Test
     public void testInsert() {
         ApiDO newApiDO = buildApiDO();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/TagRelationMapperTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/TagRelationMapperTest.java
index 33a203137d..7b8f9895ce 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/TagRelationMapperTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/mapper/TagRelationMapperTest.java
@@ -20,6 +20,7 @@ package org.apache.shenyu.admin.mapper;
 import com.google.common.collect.Lists;
 import java.sql.Timestamp;
 import java.util.List;
+import java.util.Collections;
 import java.util.Objects;
 import jakarta.annotation.Resource;
 import org.apache.shenyu.admin.AbstractSpringIntegrationTest;
@@ -38,6 +39,19 @@ public class TagRelationMapperTest extends 
AbstractSpringIntegrationTest {
     @Resource
     private TagRelationMapper tagRelationMapper;
 
+    @Test
+    public void testSelectByApiIds() {
+        TagRelationDO record = buildTagRelationDO();
+        record.setApiId(UUIDUtils.getInstance().generateShortUuid());
+        tagRelationMapper.insert(record);
+        try {
+            assertEquals(record.getId(), 
tagRelationMapper.selectByApiIds(Collections.singletonList(record.getApiId())).get(0).getId());
+            assertEquals(0, 
tagRelationMapper.selectByApiIds(Collections.emptyList()).size());
+        } finally {
+            tagRelationMapper.deleteByPrimaryKey(record.getId());
+        }
+    }
+
     @Test
     public void testInsert() {
         TagRelationDO record = buildTagRelationDO();
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ApiServiceTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ApiServiceTest.java
index 0b69cbf3a1..1a6cc3c3b1 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ApiServiceTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/ApiServiceTest.java
@@ -25,6 +25,8 @@ import org.apache.shenyu.admin.model.dto.ApiDTO;
 import org.apache.shenyu.admin.model.entity.ApiDO;
 import org.apache.shenyu.admin.model.entity.MetaDataDO;
 import org.apache.shenyu.admin.model.entity.SelectorDO;
+import org.apache.shenyu.admin.model.entity.TagDO;
+import org.apache.shenyu.admin.model.entity.TagRelationDO;
 import org.apache.shenyu.admin.model.page.CommonPager;
 import org.apache.shenyu.admin.model.page.PageParameter;
 import org.apache.shenyu.admin.model.query.ApiQuery;
@@ -45,6 +47,7 @@ import org.mockito.quality.Strictness;
 import java.sql.Timestamp;
 import java.time.LocalDateTime;
 import java.util.Collections;
+import java.util.Arrays;
 import java.util.List;
 import java.util.stream.Collectors;
 import java.util.stream.IntStream;
@@ -236,6 +239,27 @@ public final class ApiServiceTest {
         given(this.apiMapper.selectByQuery(apiQuery)).willReturn(apiDOList);
         final CommonPager<ApiVO> apiDOCommonPager = 
this.apiService.listByPage(apiQuery);
         assertEquals(apiDOCommonPager.getDataList().size(), apiDOList.size());
+        
verify(tagRelationMapper).selectByApiIds(apiDOList.stream().map(ApiDO::getId).collect(Collectors.toList()));
+        verify(tagRelationMapper, never()).selectByQuery(any());
+        verify(tagMapper, never()).selectByIds(any());
+    }
+
+    @Test
+    public void testListByPageBatchesTags() {
+        ApiQuery query = new ApiQuery(null, 0, "", new PageParameter());
+        
given(apiMapper.selectByQuery(query)).willReturn(Arrays.asList(buildApiDO("first"),
 buildApiDO("second")));
+        given(tagRelationMapper.selectByApiIds(Arrays.asList("first", 
"second"))).willReturn(Arrays.asList(
+                TagRelationDO.builder().apiId("first").tagId("tag").build(), 
TagRelationDO.builder().apiId("second").tagId("tag").build()));
+        TagDO tag = new TagDO();
+        tag.setId("tag");
+        tag.setDateCreated(new Timestamp(0));
+        tag.setDateUpdated(new Timestamp(0));
+        
given(tagMapper.selectByIds(Collections.singletonList("tag"))).willReturn(Collections.singletonList(tag));
+        List<ApiVO> result = apiService.listByPage(query).getDataList();
+        assertEquals("tag", result.get(0).getTags().get(0).getId());
+        assertEquals("tag", result.get(1).getTags().get(0).getId());
+        verify(tagMapper).selectByIds(Collections.singletonList("tag"));
+        verify(tagRelationMapper, never()).selectByQuery(any());
     }
 
     private ApiDO buildApiDO(final String id) {

Reply via email to