package com.ruoyi.account.service.impl; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import com.ruoyi.account.api.model.TAppUserWhitelist; import com.ruoyi.account.api.model.TChargingCard; import com.ruoyi.account.api.model.TVinManagement; import com.ruoyi.account.mapper.TAppUserWhitelistMapper; import com.ruoyi.account.mapper.TChargingCardMapper; import com.ruoyi.account.mapper.TVinManagementMapper; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.jupiter.api.BeforeAll; 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.test.util.ReflectionTestUtils; import java.util.Arrays; import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; /** * 车队解绑聚合服务单测(覆盖三表清空 fleet_id、去重过滤与空入参短路)。 * Mapper 以 Mock 替代,不依赖 Spring 容器与数据库。 */ @ExtendWith(MockitoExtension.class) class FleetUnbindServiceImplTest { @Mock private TVinManagementMapper vinMapper; @Mock private TChargingCardMapper cardMapper; @Mock private TAppUserWhitelistMapper whitelistMapper; private FleetUnbindServiceImpl service; @BeforeAll static void initTableInfo() { // LambdaUpdateWrapper 的 lambda 列名解析依赖 TableInfo 元数据, // 单元测试不启动 Spring,需要手动初始化三个实体的 TableInfo,否则 NPE TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TVinManagement.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TChargingCard.class); TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), TAppUserWhitelist.class); } @BeforeEach void setUp() { service = new FleetUnbindServiceImpl(); ReflectionTestUtils.setField(service, "vinManagementMapper", vinMapper); ReflectionTestUtils.setField(service, "chargingCardMapper", cardMapper); ReflectionTestUtils.setField(service, "appUserWhitelistMapper", whitelistMapper); } @Test void unbindsAllThreeTablesAndSumsAffectedRows() { when(vinMapper.update(isNull(), any())).thenReturn(2); when(cardMapper.update(isNull(), any())).thenReturn(1); when(whitelistMapper.update(isNull(), any())).thenReturn(3); int result = service.unbindByFleetIds(Arrays.asList(4, 5)); assertEquals(6, result); verify(vinMapper).update(isNull(), any()); verify(cardMapper).update(isNull(), any()); verify(whitelistMapper).update(isNull(), any()); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) void clearsFleetIdWithUpdateWrapperInAllTables() { when(vinMapper.update(isNull(), any())).thenReturn(0); when(cardMapper.update(isNull(), any())).thenReturn(0); when(whitelistMapper.update(isNull(), any())).thenReturn(0); service.unbindByFleetIds(Collections.singletonList(9)); // 三表均显式 set fleet_id = null(字段策略会忽略实体 null,必须走 UpdateWrapper set) ArgumentCaptor vinCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(vinMapper).update(isNull(), vinCaptor.capture()); LambdaUpdateWrapper vinWrapper = (LambdaUpdateWrapper) vinCaptor.getValue(); assertTrue(vinWrapper.getSqlSet().contains("fleet_id="), vinWrapper.getSqlSet()); assertTrue(vinWrapper.getSqlSegment().contains("IN"), vinWrapper.getSqlSegment()); ArgumentCaptor cardCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(cardMapper).update(isNull(), cardCaptor.capture()); LambdaUpdateWrapper cardWrapper = (LambdaUpdateWrapper) cardCaptor.getValue(); assertTrue(cardWrapper.getSqlSet().contains("fleet_id="), cardWrapper.getSqlSet()); assertTrue(cardWrapper.getSqlSegment().contains("IN"), cardWrapper.getSqlSegment()); ArgumentCaptor whitelistCaptor = ArgumentCaptor.forClass(Wrapper.class); verify(whitelistMapper).update(isNull(), whitelistCaptor.capture()); LambdaUpdateWrapper whitelistWrapper = (LambdaUpdateWrapper) whitelistCaptor.getValue(); assertTrue(whitelistWrapper.getSqlSet().contains("fleet_id="), whitelistWrapper.getSqlSet()); assertTrue(whitelistWrapper.getSqlSegment().contains("IN"), whitelistWrapper.getSqlSegment()); // 白名单实体无 updateTime 映射,沿用 setSql 方式更新时间 assertTrue(whitelistWrapper.getSqlSet().contains("update_time = now()"), whitelistWrapper.getSqlSet()); } @Test void deduplicatesAndFiltersNullFleetIds() { when(vinMapper.update(isNull(), any())).thenReturn(0); when(cardMapper.update(isNull(), any())).thenReturn(0); when(whitelistMapper.update(isNull(), any())).thenReturn(0); service.unbindByFleetIds(Arrays.asList(4, null, 4, 5)); // 去重过滤后仅 4、5 两个绑定值参与 IN 条件 @SuppressWarnings({"unchecked", "rawtypes"}) ArgumentCaptor captor = ArgumentCaptor.forClass(Wrapper.class); verify(vinMapper).update(isNull(), captor.capture()); LambdaUpdateWrapper wrapper = (LambdaUpdateWrapper) captor.getValue(); assertEquals(2, wrapper.getParamNameValuePairs().size()); } @Test void returnsZeroWithoutTouchingTablesForNullOrEmptyInput() { assertEquals(0, service.unbindByFleetIds(null)); assertEquals(0, service.unbindByFleetIds(Collections.emptyList())); assertEquals(0, service.unbindByFleetIds(Arrays.asList(null, null))); verifyNoInteractions(vinMapper, cardMapper, whitelistMapper); } @Test void returnsZeroWhenNoBoundRecords() { when(vinMapper.update(isNull(), any())).thenReturn(0); when(cardMapper.update(isNull(), any())).thenReturn(0); when(whitelistMapper.update(isNull(), any())).thenReturn(0); List fleetIds = Collections.singletonList(7); int result = service.unbindByFleetIds(fleetIds); assertEquals(0, result); // 无挂靠数据时仍对三表各执行一次解绑语句(0 行受影响),保证幂等清理 verify(vinMapper).update(isNull(), any()); verify(cardMapper).update(isNull(), any()); verify(whitelistMapper).update(isNull(), any()); verify(vinMapper, never()).selectList(any()); } }