diff --git a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/DeviceLifecycleTools.java b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/DeviceLifecycleTools.java index fcd9c72..8bb9633 100644 --- a/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/DeviceLifecycleTools.java +++ b/xiaoqu-agent-service/src/main/java/me/iiv/agent/mcp/tool/DeviceLifecycleTools.java @@ -1,7 +1,6 @@ package me.iiv.agent.mcp.tool; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import me.iiv.agent.entity.m0.Agent; import me.iiv.agent.entity.m0.DeviceLifeCycle; import me.iiv.agent.entity.m0.District; @@ -141,15 +140,18 @@ public class DeviceLifecycleTools implements ToolObject { boolean truncatedRows = rows != null && rows > MAX_ROWS; int pageSize = rows == null || rows < 1 ? DEFAULT_ROWS : Math.min(rows, MAX_ROWS); - Page result = deviceLifeCycleMapper.selectPage(new Page<>(pageNo, pageSize), - new LambdaQueryWrapper() - .eq(DeviceLifeCycle::getSoftDelete, 0) - .like(DeviceLifeCycle::getSnCode, snCode.trim()) - .orderByAsc(DeviceLifeCycle::getId)); + // 项目未注册 MyBatis-Plus 分页拦截器,按惯例 selectCount + LIMIT 物理分页 + LambdaQueryWrapper wrapper = new LambdaQueryWrapper() + .eq(DeviceLifeCycle::getSoftDelete, 0) + .like(DeviceLifeCycle::getSnCode, snCode.trim()); + Long total = deviceLifeCycleMapper.selectCount(wrapper); + wrapper.orderByAsc(DeviceLifeCycle::getId) + .last("LIMIT " + (long) (pageNo - 1) * pageSize + "," + pageSize); + List cycles = deviceLifeCycleMapper.selectList(wrapper); Integer maxRoadDays = null; List> records = new ArrayList<>(); - for (DeviceLifeCycle cycle : result.getRecords()) { + for (DeviceLifeCycle cycle : cycles) { if (cycle.getRoadState() != null && cycle.getRoadState() == 1 && cycle.getRoadDate() != null && maxRoadDays == null) { maxRoadDays = maxRoadDays(); @@ -158,7 +160,7 @@ public class DeviceLifecycleTools implements ToolObject { } Map output = new LinkedHashMap<>(); - output.put("total", result.getTotal()); + output.put("total", total); output.put("page", pageNo); output.put("rows", pageSize); if (truncatedRows) { diff --git a/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/tool/DeviceLifecycleToolsTest.java b/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/tool/DeviceLifecycleToolsTest.java index cdf3cdf..8bcfdc7 100644 --- a/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/tool/DeviceLifecycleToolsTest.java +++ b/xiaoqu-agent-service/src/test/java/me/iiv/agent/mcp/tool/DeviceLifecycleToolsTest.java @@ -1,8 +1,8 @@ package me.iiv.agent.mcp.tool; import com.baomidou.mybatisplus.core.MybatisConfiguration; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; -import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import me.iiv.agent.entity.m0.Agent; import me.iiv.agent.entity.m0.DeviceLifeCycle; import me.iiv.agent.entity.m0.District; @@ -32,6 +32,7 @@ import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.data.redis.core.HashOperations; @@ -127,17 +128,34 @@ class DeviceLifecycleToolsTest { @Test void queryTruncatesRowsOverLimitWithNotice() { - Page page = new Page<>(1, 100); - page.setTotal(0); - page.setRecords(List.of()); - when(deviceLifeCycleMapper.selectPage(any(), any())).thenReturn(page); + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(0L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of()); Map output = tools.hardwareDeviceLifecycleQuery("SN1", 1, 500); + assertEquals(0L, output.get("total")); assertEquals(100, output.get("rows")); assertEquals("rows 超过上限 100,已截断为 100", output.get("notice")); } + @Test + void queryPaginatesWithPhysicalLimit() { + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(200L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of()); + + Map output = tools.hardwareDeviceLifecycleQuery("X1A1", 2, 50); + + assertEquals(200L, output.get("total")); + assertEquals(2, output.get("page")); + // 回归:项目未注册 MyBatis-Plus 分页拦截器,必须物理 LIMIT(2026-09-24 生产实测 selectPage 不分页且 total=0) + ArgumentCaptor> captor = + ArgumentCaptor.forClass(LambdaQueryWrapper.class); + verify(deviceLifeCycleMapper).selectList(captor.capture()); + String sql = captor.getValue().getSqlSegment(); + assertTrue(sql.contains("sn_code LIKE")); + assertTrue(sql.contains("LIMIT 50,50")); + } + @Test void queryMapsEnumTextsIncludingUnknownAndInit() { DeviceLifeCycle cycle = new DeviceLifeCycle(); @@ -150,13 +168,12 @@ class DeviceLifecycleToolsTest { cycle.setNetworkState(1); cycle.setAssetState(2); cycle.setJindieSyncState(0); - Page page = new Page<>(1, 20); - page.setTotal(1); - page.setRecords(List.of(cycle)); - when(deviceLifeCycleMapper.selectPage(any(), any())).thenReturn(page); + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(1L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of(cycle)); Map output = tools.hardwareDeviceLifecycleQuery("SN-UNKNOWN", null, null); + assertEquals(1L, output.get("total")); @SuppressWarnings("unchecked") List> records = (List>) output.get("records"); Map row = records.get(0); @@ -181,10 +198,8 @@ class DeviceLifecycleToolsTest { cycle.setRoadState(1); cycle.setRoadDate(Date.from(LocalDate.now(SHANGHAI).minusDays(10) .atStartOfDay(SHANGHAI).toInstant())); - Page page = new Page<>(1, 20); - page.setTotal(1); - page.setRecords(List.of(cycle)); - when(deviceLifeCycleMapper.selectPage(any(), any())).thenReturn(page); + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(1L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of(cycle)); when(redisTemplate.opsForHash()).thenReturn(hashOperations); when(hashOperations.get("Dictionary", "MaxRoadDays")).thenReturn("7"); @@ -205,10 +220,8 @@ class DeviceLifecycleToolsTest { cycle.setRoadState(1); cycle.setRoadDate(Date.from(LocalDate.now(SHANGHAI).minusDays(3) .atStartOfDay(SHANGHAI).toInstant())); - Page page = new Page<>(1, 20); - page.setTotal(1); - page.setRecords(List.of(cycle)); - when(deviceLifeCycleMapper.selectPage(any(), any())).thenReturn(page); + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(1L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of(cycle)); when(redisTemplate.opsForHash()).thenReturn(hashOperations); when(hashOperations.get("Dictionary", "MaxRoadDays")).thenReturn("7"); @@ -227,10 +240,8 @@ class DeviceLifecycleToolsTest { cycle.setRoadState(1); cycle.setRoadDate(Date.from(LocalDate.now(SHANGHAI).minusDays(1) .atStartOfDay(SHANGHAI).toInstant())); - Page page = new Page<>(1, 20); - page.setTotal(1); - page.setRecords(List.of(cycle)); - when(deviceLifeCycleMapper.selectPage(any(), any())).thenReturn(page); + when(deviceLifeCycleMapper.selectCount(any())).thenReturn(1L); + when(deviceLifeCycleMapper.selectList(any())).thenReturn(List.of(cycle)); when(redisTemplate.opsForHash()).thenReturn(hashOperations); when(hashOperations.get("Dictionary", "MaxRoadDays")).thenReturn(null);