fix(mcp): 生命周期列表改为 selectCount+LIMIT 物理分页

项目未注册 MyBatis-Plus 分页拦截器,selectPage 不做物理分页且 total 恒为 0
(2026-09-24 生产冒烟实测),对齐项目内 battery.fault.list.query 的既有做法
This commit is contained in:
wangheng
2026-09-24 10:21:57 +08:00
parent 04f7508062
commit 98a3d3c6e4
2 changed files with 42 additions and 29 deletions
@@ -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<DeviceLifeCycle> result = deviceLifeCycleMapper.selectPage(new Page<>(pageNo, pageSize),
new LambdaQueryWrapper<DeviceLifeCycle>()
.eq(DeviceLifeCycle::getSoftDelete, 0)
.like(DeviceLifeCycle::getSnCode, snCode.trim())
.orderByAsc(DeviceLifeCycle::getId));
// 项目未注册 MyBatis-Plus 分页拦截器,按惯例 selectCount + LIMIT 物理分页
LambdaQueryWrapper<DeviceLifeCycle> wrapper = new LambdaQueryWrapper<DeviceLifeCycle>()
.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<DeviceLifeCycle> cycles = deviceLifeCycleMapper.selectList(wrapper);
Integer maxRoadDays = null;
List<Map<String, Object>> 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<String, Object> output = new LinkedHashMap<>();
output.put("total", result.getTotal());
output.put("total", total);
output.put("page", pageNo);
output.put("rows", pageSize);
if (truncatedRows) {
@@ -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<DeviceLifeCycle> 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<String, Object> 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<String, Object> 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<LambdaQueryWrapper<DeviceLifeCycle>> 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<DeviceLifeCycle> 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<String, Object> output = tools.hardwareDeviceLifecycleQuery("SN-UNKNOWN", null, null);
assertEquals(1L, output.get("total"));
@SuppressWarnings("unchecked")
List<Map<String, Object>> records = (List<Map<String, Object>>) output.get("records");
Map<String, Object> 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<DeviceLifeCycle> 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<DeviceLifeCycle> 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<DeviceLifeCycle> 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);