| New file |
| | |
| | | package com.ruoyi.order.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.MybatisConfiguration; |
| | | import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; |
| | | import com.ruoyi.chargingPile.api.dto.GetSiteListDTO; |
| | | import com.ruoyi.chargingPile.api.feignClient.ChargingGunClient; |
| | | import com.ruoyi.chargingPile.api.feignClient.ChargingPileClient; |
| | | import com.ruoyi.chargingPile.api.feignClient.SiteClient; |
| | | import com.ruoyi.chargingPile.api.model.TChargingGun; |
| | | import com.ruoyi.chargingPile.api.model.TChargingPile; |
| | | import com.ruoyi.common.core.domain.R; |
| | | import com.ruoyi.common.core.web.domain.AjaxResult; |
| | | import com.ruoyi.common.security.service.TokenService; |
| | | import com.ruoyi.integration.api.feignClient.SendMessageClient; |
| | | import com.ruoyi.integration.api.model.PlatformStopCharging; |
| | | import com.ruoyi.order.api.model.TCardChargingOrder; |
| | | import com.ruoyi.order.mapper.TCardChargingOrderMapper; |
| | | import com.ruoyi.system.api.model.LoginUser; |
| | | 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.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | |
| | | import java.util.Collections; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.mockito.ArgumentMatchers.any; |
| | | import static org.mockito.Mockito.lenient; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | |
| | | /** |
| | | * 管理后台刷卡充电订单「停止充电」单测。 |
| | | * 覆盖:数据范围校验、充电枪匹配、状态拦截、条件更新并发保护、停机指令下发。 |
| | | */ |
| | | @ExtendWith(MockitoExtension.class) |
| | | class TCardChargingOrderStopChargingTest { |
| | | |
| | | private static final Long LOGIN_USER_ID = 9L; |
| | | private static final Integer AUTHORIZED_SITE_ID = 7; |
| | | private static final Long ORDER_ID = 11L; |
| | | private static final Integer GUN_ID = 22; |
| | | private static final Integer PILE_ID = 33; |
| | | |
| | | @InjectMocks |
| | | private TCardChargingOrderServiceImpl service; |
| | | @Mock |
| | | private TCardChargingOrderMapper mapper; |
| | | @Mock |
| | | private TokenService tokenService; |
| | | @Mock |
| | | private SiteClient siteClient; |
| | | @Mock |
| | | private ChargingGunClient chargingGunClient; |
| | | @Mock |
| | | private ChargingPileClient chargingPileClient; |
| | | @Mock |
| | | private SendMessageClient sendMessageClient; |
| | | |
| | | @BeforeEach |
| | | void setUp() { |
| | | ReflectionTestUtils.setField(service, "baseMapper", mapper); |
| | | // lambdaUpdate 解析 getter 引用依赖 MyBatis-Plus 实体元信息缓存,单测环境需显式初始化 |
| | | TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), |
| | | TCardChargingOrder.class); |
| | | // 默认:条件更新成功 |
| | | lenient().when(mapper.update(any(), any())).thenReturn(1); |
| | | } |
| | | |
| | | // ===== 一、正常停充 ===== |
| | | |
| | | @Test |
| | | void stopChargingAdvancesStatusAndSendsCommand() { |
| | | TCardChargingOrder order = order(3); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | mockDevice(order); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(200), result.get("code")); |
| | | ArgumentCaptor<PlatformStopCharging> captor = ArgumentCaptor.forClass(PlatformStopCharging.class); |
| | | verify(sendMessageClient).platformStopCharging(captor.capture()); |
| | | assertEquals("PILE-33", captor.getValue().getCharging_pile_code()); |
| | | assertEquals("GUN-22", captor.getValue().getCharging_gun_code()); |
| | | assertEquals(Integer.valueOf(4), order.getStatus()); |
| | | assertEquals(Integer.valueOf(1), order.getEndMode()); |
| | | verify(mapper).update(any(), any()); |
| | | } |
| | | |
| | | // ===== 二、入参与订单校验 ===== |
| | | |
| | | @Test |
| | | void rejectsInvalidOrderId() { |
| | | AjaxResult result = service.stopCharging("abc", GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("刷卡充电订单ID无效", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsOrderNotFound() { |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(null); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("刷卡充电订单不存在或无权操作", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsOrderOutsideAuthorizationScope() { |
| | | TCardChargingOrder order = order(3); |
| | | order.setSiteId(AUTHORIZED_SITE_ID + 1); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("刷卡充电订单不存在或无权操作", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsMismatchedGun() { |
| | | TCardChargingOrder order = order(3); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID + 1); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("充电枪与订单不匹配", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | // ===== 三、状态拦截 ===== |
| | | |
| | | @Test |
| | | void rejectsOrderNotStartedCharge() { |
| | | TCardChargingOrder order = order(2); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("还未开始充电", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsRepeatedStopOperation() { |
| | | TCardChargingOrder order = order(4); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("不能重复操作", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | // ===== 四、设备与并发 ===== |
| | | |
| | | @Test |
| | | void rejectsWhenDeviceMissing() { |
| | | TCardChargingOrder order = order(3); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("未找到订单对应的充电设备", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsWhenStatusChangedConcurrently() { |
| | | lenient().when(mapper.update(any(), any())).thenReturn(0); |
| | | TCardChargingOrder order = order(3); |
| | | when(mapper.selectById(ORDER_ID)).thenReturn(order); |
| | | mockAuthorizedLogin(); |
| | | mockDevice(order); |
| | | |
| | | AjaxResult result = service.stopCharging(String.valueOf(ORDER_ID), GUN_ID); |
| | | |
| | | assertEquals(Integer.valueOf(500), result.get("code")); |
| | | assertEquals("订单状态已变化,请刷新后重试", result.get("msg")); |
| | | verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class)); |
| | | } |
| | | |
| | | // ===== 辅助方法 ===== |
| | | |
| | | private TCardChargingOrder order(Integer status) { |
| | | TCardChargingOrder order = new TCardChargingOrder(); |
| | | order.setId(ORDER_ID); |
| | | order.setCode("SD202610140001"); |
| | | order.setSiteId(AUTHORIZED_SITE_ID); |
| | | order.setChargingGunId(GUN_ID); |
| | | order.setChargingPileId(PILE_ID); |
| | | order.setStatus(status); |
| | | return order; |
| | | } |
| | | |
| | | private void mockAuthorizedLogin() { |
| | | LoginUser loginUser = new LoginUser(); |
| | | loginUser.setUserid(LOGIN_USER_ID); |
| | | lenient().when(tokenService.getLoginUser()).thenReturn(loginUser); |
| | | GetSiteListDTO site = new GetSiteListDTO(); |
| | | site.setId(AUTHORIZED_SITE_ID); |
| | | lenient().when(siteClient.getSiteListByUserId(LOGIN_USER_ID)) |
| | | .thenReturn(R.ok(Collections.singletonList(site))); |
| | | } |
| | | |
| | | private void mockDevice(TCardChargingOrder order) { |
| | | TChargingGun gun = new TChargingGun(); |
| | | gun.setId(order.getChargingGunId()); |
| | | gun.setCode("GUN-" + order.getChargingGunId()); |
| | | lenient().when(chargingGunClient.getChargingGunById(order.getChargingGunId())).thenReturn(R.ok(gun)); |
| | | TChargingPile pile = new TChargingPile(); |
| | | pile.setId(order.getChargingPileId()); |
| | | pile.setCode("PILE-" + order.getChargingPileId()); |
| | | lenient().when(chargingPileClient.getChargingPileById(order.getChargingPileId())).thenReturn(R.ok(pile)); |
| | | } |
| | | } |