fix(mcp): 生命周期列表改为 selectCount+LIMIT 物理分页
项目未注册 MyBatis-Plus 分页拦截器,selectPage 不做物理分页且 total 恒为 0 (2026-09-24 生产冒烟实测),对齐项目内 battery.fault.list.query 的既有做法
This commit is contained in:
@@ -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) {
|
||||
|
||||
+32
-21
@@ -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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user