From 63b68c39653208a1756640034c028789946d0e12 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期二, 13 十月 2026 15:47:20 +0800
Subject: [PATCH] 修改bug

---
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java                |   26 ++++
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TVinChargingOrderServiceImpl.java             |   36 ++++++
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TVinChargingOrderService.java                      |    7 +
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java |  194 ++++++++++++++++++++++++++++++++
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TVinChargingOrderAppletServiceTest.java       |   81 +++++++++++++
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java                   |   12 ++
 6 files changed, 355 insertions(+), 1 deletions(-)

diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
index 40ef089..a2002a6 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/controller/TChargingOrderController.java
@@ -1912,6 +1912,18 @@
     })
     public AjaxResult stopCharging(@PathVariable String id) {
         log.info("小程序主动发起停止充电,订单ID:{}", id);
+        // VIN 码充电订单路由:VD 订单存于 t_vin_charging_order,不在 t_charging_order;
+        // VD 表存在该 ID 订单时优先走 VIN 停充(内部按状态返回准确的“不能重复操作/还未开始充电”),
+        // 其余情况回落普通停充。雪花 ID 两表同源生成,撞号概率可忽略
+        TVinChargingOrder vinOrder = null;
+        try {
+            vinOrder = vinChargingOrderService.getById(Long.valueOf(id));
+        } catch (NumberFormatException ignored) {
+            // 非数字订单ID,按普通停充处理
+        }
+        if (vinOrder != null) {
+            return vinChargingOrderService.stopChargingByApplet(id);
+        }
         return chargingOrderService.stopCharging(id);
     }
     
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TVinChargingOrderService.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TVinChargingOrderService.java
index 44ddba7..f56fe23 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TVinChargingOrderService.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TVinChargingOrderService.java
@@ -37,6 +37,13 @@
 
     AjaxResult stopCharging(String chargingOrderId, Integer chargingGunId);
 
+    /**
+     * 小程序端停止VIN码充电订单(正在充电界面调用):
+     * 复用 getAuthorizedAppletOrder 鉴权(VIN当前归属+下单用户一致),仅状态3允许停止,
+     * 置状态4/结束方式1/结束时间并下发平台停机指令
+     */
+    AjaxResult stopChargingByApplet(String chargingOrderId);
+
     List<Map<String, Object>> accountingStrategyList(Long chargingOrderId);
 
     // ===== 设备侧VIN码充电链路(V0.2 VIN码充电) =====
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
index 9e39242..8d08113 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java
@@ -56,6 +56,7 @@
 import com.ruoyi.order.mapper.TChargingOrderMapper;
 import com.ruoyi.order.mapper.TChargingOrderShareRecordMapper;
 import com.ruoyi.order.mapper.TSettlementConfirmMapper;
+import com.ruoyi.order.mapper.TVinChargingOrderMapper;
 import com.ruoyi.order.service.TChargingOrderAccountingStrategyService;
 import com.ruoyi.order.service.TChargingOrderRefundService;
 import com.ruoyi.order.service.TChargingOrderService;
@@ -251,6 +252,9 @@
 
     @Resource
     private UploadRealTimeMonitoringDataService uploadRealTimeMonitoringDataService;
+
+    @Resource
+    private TVinChargingOrderMapper vinChargingOrderMapper;
 
     @Resource
     private TCECClient tcecClient;
@@ -2767,7 +2771,19 @@
                 .orderByDesc(TChargingOrder::getCreateTime)
                 .last(" limit 0, 1"));
         if (null == one || !one.getAppUserId().equals(userId)) {
-            return null;
+            // VIN 码充电订单兜底:VD 订单存于 t_vin_charging_order,且后付模式无支付动作,
+            // 不按 rechargePaymentStatus 过滤;TVinChargingOrder 继承 TChargingOrder,后续组装原样复用
+            TVinChargingOrder vinOrder = vinChargingOrderMapper.selectOne(new LambdaQueryWrapper<TVinChargingOrder>()
+                    .eq(TVinChargingOrder::getAppUserId, userId)
+                    .eq(TVinChargingOrder::getChargingGunId, id)
+                    .in(TVinChargingOrder::getStatus, Arrays.asList(2, 3, 4))
+                    .eq(TVinChargingOrder::getDelFlag, 0)
+                    .orderByDesc(TVinChargingOrder::getCreateTime)
+                    .last(" limit 0, 1"));
+            if (null == vinOrder) {
+                return null;
+            }
+            one = vinOrder;
         }
         ChargingDetails chargingDetails = new ChargingDetails();
         chargingDetails.setId(one.getId().toString());
@@ -8058,6 +8074,14 @@
     public EndOfChargePageInfo getEndOfChargePageInfo(String id) {
         EndOfChargePageInfo info = new EndOfChargePageInfo();
         TChargingOrder chargingOrder = this.getById(id);
+        if (chargingOrder == null) {
+            // VIN 码充电订单兜底:VD 订单存于 t_vin_charging_order(继承 TChargingOrder,字段兼容;
+            // 结算已写 paymentAmount/startTime/endTime,后续组装原样复用)
+            chargingOrder = vinChargingOrderMapper.selectById(id);
+        }
+        if (chargingOrder == null) {
+            throw new RuntimeException("充电订单不存在");
+        }
         Long userId = tokenService.getLoginUserApplet().getUserId();
         if (!chargingOrder.getAppUserId().equals(userId)) {
             throw new RuntimeException("权限不足");
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TVinChargingOrderServiceImpl.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TVinChargingOrderServiceImpl.java
index a1d3852..7e4fa97 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TVinChargingOrderServiceImpl.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TVinChargingOrderServiceImpl.java
@@ -432,6 +432,42 @@
     }
 
     @Override
+    public AjaxResult stopChargingByApplet(String chargingOrderId) {
+        Long orderId;
+        try {
+            orderId = Long.valueOf(chargingOrderId);
+        } catch (NumberFormatException e) {
+            return AjaxResult.error("VIN码充电订单ID无效");
+        }
+        TVinChargingOrder order = getAuthorizedAppletOrder(orderId);
+        if (order == null) {
+            return AjaxResult.error("VIN码充电订单不存在或无权操作");
+        }
+        Integer status = order.getStatus();
+        if (status == null || !status.equals(3)) {
+            return AjaxResult.error(status != null && (status.equals(4) || status.equals(5))
+                    ? "不能重复操作" : "还未开始充电");
+        }
+        TChargingGun chargingGun = chargingGunClient.getChargingGunById(order.getChargingGunId()).getData();
+        TChargingPile chargingPile = order.getChargingPileId() == null ? null
+                : chargingPileClient.getChargingPileById(order.getChargingPileId()).getData();
+        if (chargingGun == null || chargingPile == null) {
+            return AjaxResult.error("未找到订单对应的充电设备");
+        }
+        order.setStatus(4);
+        order.setEndMode(1);
+        order.setEndTime(LocalDateTime.now());
+        if (!updateById(order)) {
+            return AjaxResult.error("更新VIN码充电订单状态失败");
+        }
+        PlatformStopCharging command = new PlatformStopCharging();
+        command.setCharging_pile_code(chargingPile.getCode());
+        command.setCharging_gun_code(chargingGun.getCode());
+        sendMessageClient.platformStopCharging(command);
+        return AjaxResult.success();
+    }
+
+    @Override
     public List<Map<String, Object>> accountingStrategyList(Long chargingOrderId) {
         TVinChargingOrder order = getAuthorizedOrder(chargingOrderId);
         return order == null ? Collections.emptyList() : baseMapper.accountingStrategyList(chargingOrderId);
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java
new file mode 100644
index 0000000..dda4567
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TChargingOrderServiceImplVinFallbackTest.java
@@ -0,0 +1,194 @@
+package com.ruoyi.order.service.impl;
+
+import com.ruoyi.account.api.feignClient.AppUserIntegralChangeClient;
+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.Site;
+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.security.service.TokenService;
+import com.ruoyi.integration.api.model.UploadRealTimeMonitoringData;
+import com.ruoyi.order.api.model.TChargingOrder;
+import com.ruoyi.order.api.model.TVinChargingOrder;
+import com.ruoyi.order.dto.ChargingDetails;
+import com.ruoyi.order.mapper.TChargingOrderMapper;
+import com.ruoyi.order.mapper.TVinChargingOrderMapper;
+import com.ruoyi.order.util.mongodb.service.UploadRealTimeMonitoringDataService;
+import com.ruoyi.order.vo.EndOfChargePageInfo;
+import com.baomidou.mybatisplus.core.MybatisConfiguration;
+import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
+import com.ruoyi.system.api.model.LoginUserApplet;
+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.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.when;
+
+/**
+ * TChargingOrderServiceImpl 小程序接口对 VIN 码充电订单(t_vin_charging_order)的兜底逻辑测试:
+ * getChargingDetails(正在充电详情)、getEndOfChargePageInfo(结束页详情)。
+ */
+@ExtendWith(MockitoExtension.class)
+class TChargingOrderServiceImplVinFallbackTest {
+
+    @InjectMocks
+    private TChargingOrderServiceImpl service;
+    @Mock
+    private TChargingOrderMapper mapper;
+    @Mock
+    private TVinChargingOrderMapper vinChargingOrderMapper;
+    @Mock
+    private TokenService tokenService;
+    @Mock
+    private ChargingGunClient chargingGunClient;
+    @Mock
+    private ChargingPileClient chargingPileClient;
+    @Mock
+    private SiteClient siteClient;
+    @Mock
+    private UploadRealTimeMonitoringDataService uploadRealTimeMonitoringDataService;
+    @Mock
+    private AppUserIntegralChangeClient appUserIntegralChangeClient;
+
+    /**
+     * 纯单测无 Spring 容器,MyBatis-Plus 的 lambda 列缓存需手工初始化,
+     * 否则 LambdaQueryWrapper 解析 getter 引用时抛 "can not find lambda cache"
+     */
+    @BeforeAll
+    static void initMybatisPlusTableInfo() {
+        MapperBuilderAssistant assistant = new MapperBuilderAssistant(new MybatisConfiguration(), "");
+        TableInfoHelper.initTableInfo(assistant, TChargingOrder.class);
+        TableInfoHelper.initTableInfo(assistant, TVinChargingOrder.class);
+    }
+
+    @BeforeEach
+    void setUp() {
+        ReflectionTestUtils.setField(service, "baseMapper", mapper);
+    }
+
+    @Test
+    void getChargingDetailsFallsBackToVinOrderAndAssemblesRealTimeData() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        // 普通订单表查不到
+        when(mapper.selectOne(any())).thenReturn(null);
+        TVinChargingOrder vinOrder = new TVinChargingOrder();
+        vinOrder.setId(1001L);
+        vinOrder.setAppUserId(88L);
+        vinOrder.setChargingGunId(5);
+        vinOrder.setStatus(3);
+        vinOrder.setCode("VD20261013001");
+        vinOrder.setResidualAmount(new BigDecimal("66.60"));
+        when(vinChargingOrderMapper.selectOne(any())).thenReturn(vinOrder);
+        TChargingGun gun = new TChargingGun();
+        gun.setChargingPileId(7);
+        when(chargingGunClient.getChargingGunById(5)).thenReturn(R.ok(gun));
+        TChargingPile pile = new TChargingPile();
+        pile.setName("1号桩");
+        pile.setSiteId(9);
+        when(chargingPileClient.getChargingPileById(7)).thenReturn(R.ok(pile));
+        Site site = new Site();
+        site.setName("城东站");
+        when(siteClient.getSiteByIds(Arrays.asList(9))).thenReturn(R.ok(Collections.singletonList(site)));
+        UploadRealTimeMonitoringData data = new UploadRealTimeMonitoringData();
+        data.setOutput_current(new BigDecimal("10"));
+        data.setOutput_voltage(new BigDecimal("220"));
+        data.setSoc(50);
+        data.setTime_remaining(30);
+        data.setCharging_degree(new BigDecimal("5.5"));
+        data.setCumulative_charging_time(75);
+        when(uploadRealTimeMonitoringDataService.getLastDataById("VD20261013001")).thenReturn(data);
+
+        ChargingDetails details = service.getChargingDetails(5);
+
+        assertEquals("1001", details.getId());
+        assertEquals("城东站-1号桩", details.getName());
+        assertEquals("VD20261013001", details.getCode());
+        assertEquals(Integer.valueOf(3), details.getStatus());
+        assertEquals(0, new BigDecimal("66.60").compareTo(details.getChargingCost()));
+        assertEquals(0, new BigDecimal("10").compareTo(details.getChargeCurrent()));
+        assertEquals(0, new BigDecimal("220").compareTo(details.getChargeVoltage()));
+        assertEquals(0, new BigDecimal("2.200").compareTo(details.getChargePower()));
+        assertEquals(Integer.valueOf(50), details.getCompletionRatio());
+        assertEquals(Integer.valueOf(30), details.getRemainingChargeTime());
+        assertEquals(0, new BigDecimal("5.5").compareTo(details.getChargedDegrees()));
+        assertEquals("01:15", details.getChargedTime());
+    }
+
+    @Test
+    void getChargingDetailsReturnsNullWhenNeitherNormalNorVinOrderExists() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        when(mapper.selectOne(any())).thenReturn(null);
+        when(vinChargingOrderMapper.selectOne(any())).thenReturn(null);
+
+        assertNull(service.getChargingDetails(5));
+    }
+
+    @Test
+    void getEndOfChargePageInfoFallsBackToVinOrder() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        when(mapper.selectById("1001")).thenReturn(null);
+        TVinChargingOrder vinOrder = new TVinChargingOrder();
+        vinOrder.setId(1001L);
+        vinOrder.setAppUserId(88L);
+        vinOrder.setCode("VD20261013001");
+        vinOrder.setPaymentAmount(new BigDecimal("18.50"));
+        vinOrder.setRechargeAmount(new BigDecimal("50.00"));
+        vinOrder.setRefundAmount(new BigDecimal("31.50"));
+        vinOrder.setStartTime(LocalDateTime.of(2026, 10, 13, 10, 0));
+        vinOrder.setEndTime(LocalDateTime.of(2026, 10, 13, 11, 30));
+        when(vinChargingOrderMapper.selectById("1001")).thenReturn(vinOrder);
+        when(appUserIntegralChangeClient.getTAppUserIntegralChangeByOrderCode("VD20261013001"))
+                .thenReturn(R.ok(null));
+
+        EndOfChargePageInfo info = service.getEndOfChargePageInfo("1001");
+
+        assertEquals("1001", info.getId());
+        assertEquals("VD20261013001", info.getCode());
+        assertEquals(0, new BigDecimal("18.50").compareTo(info.getPaymentAmount()));
+        assertEquals(0, new BigDecimal("50.00").compareTo(info.getRechargeAmount()));
+        assertEquals(0, new BigDecimal("31.50").compareTo(info.getRefundAmount()));
+        assertEquals(Integer.valueOf(0), info.getIntegral());
+        assertEquals("10:00", info.getStartHour());
+        assertEquals("11:30", info.getEndHour());
+        assertEquals("1小时30分钟", info.getDuration());
+    }
+
+    @Test
+    void getEndOfChargePageInfoThrowsWhenOrderMissingInBothTables() {
+        // 两表均查不到订单时在获取登录用户之前即抛异常,tokenService 无需 stub
+        when(mapper.selectById("1001")).thenReturn(null);
+        when(vinChargingOrderMapper.selectById("1001")).thenReturn(null);
+
+        RuntimeException exception = null;
+        try {
+            service.getEndOfChargePageInfo("1001");
+        } catch (RuntimeException ex) {
+            exception = ex;
+        }
+        assertNotNull(exception);
+        assertEquals("充电订单不存在", exception.getMessage());
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TVinChargingOrderAppletServiceTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TVinChargingOrderAppletServiceTest.java
index 2f39d2d..04af75d 100644
--- a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TVinChargingOrderAppletServiceTest.java
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/service/impl/TVinChargingOrderAppletServiceTest.java
@@ -2,9 +2,15 @@
 
 import com.ruoyi.account.api.feignClient.VinManagementClient;
 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.core.web.page.PageInfo;
+import com.ruoyi.integration.api.feignClient.SendMessageClient;
+import com.ruoyi.integration.api.model.PlatformStopCharging;
 import com.ruoyi.system.api.model.LoginUserApplet;
 import com.ruoyi.common.security.service.TokenService;
 import com.ruoyi.order.api.model.TVinChargingOrder;
@@ -25,7 +31,10 @@
 import java.util.Map;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyList;
 import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
@@ -48,6 +57,10 @@
     private SiteClient siteClient;
     @Mock
     private ChargingGunClient chargingGunClient;
+    @Mock
+    private ChargingPileClient chargingPileClient;
+    @Mock
+    private SendMessageClient sendMessageClient;
     @Mock
     private UploadRealTimeMonitoringDataService monitoringDataService;
 
@@ -149,4 +162,72 @@
         assertTrue(service.appletDetail(1001L).isEmpty());
         verify(mapper, never()).accountingStrategyList(1001L);
     }
+
+    @Test
+    void stopChargingByAppletStopsChargingOrderForBoundVinOwner() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        when(vinManagementClient.getVinIdsByAppUserId(88L)).thenReturn(R.ok(Collections.singletonList(11L)));
+        TVinChargingOrder order = new TVinChargingOrder();
+        order.setId(1001L);
+        order.setVinId(11L);
+        order.setAppUserId(88L);
+        order.setStatus(3);
+        order.setChargingGunId(5);
+        order.setChargingPileId(7);
+        when(mapper.selectById(1001L)).thenReturn(order);
+        TChargingGun gun = new TChargingGun();
+        gun.setCode("GUN_5");
+        when(chargingGunClient.getChargingGunById(5)).thenReturn(R.ok(gun));
+        TChargingPile pile = new TChargingPile();
+        pile.setCode("PILE_7");
+        when(chargingPileClient.getChargingPileById(7)).thenReturn(R.ok(pile));
+        when(mapper.updateById(order)).thenReturn(1);
+
+        AjaxResult result = service.stopChargingByApplet("1001");
+
+        assertTrue(result.isSuccess());
+        assertEquals(4, order.getStatus());
+        assertEquals(1, order.getEndMode());
+        assertNotNull(order.getEndTime());
+        verify(sendMessageClient).platformStopCharging(any(PlatformStopCharging.class));
+    }
+
+    @Test
+    void stopChargingByAppletRejectsWhenVinNotBoundToCurrentUser() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        when(vinManagementClient.getVinIdsByAppUserId(88L)).thenReturn(R.ok(Collections.emptyList()));
+        TVinChargingOrder order = new TVinChargingOrder();
+        order.setId(1001L);
+        order.setVinId(11L);
+        when(mapper.selectById(1001L)).thenReturn(order);
+
+        AjaxResult result = service.stopChargingByApplet("1001");
+
+        assertFalse(result.isSuccess());
+        verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class));
+        verify(mapper, never()).updateById(any(TVinChargingOrder.class));
+    }
+
+    @Test
+    void stopChargingByAppletRejectsFinishedOrder() {
+        LoginUserApplet loginUser = new LoginUserApplet();
+        loginUser.setUserId(88L);
+        when(tokenService.getLoginUserApplet()).thenReturn(loginUser);
+        when(vinManagementClient.getVinIdsByAppUserId(88L)).thenReturn(R.ok(Collections.singletonList(11L)));
+        TVinChargingOrder order = new TVinChargingOrder();
+        order.setId(1001L);
+        order.setVinId(11L);
+        order.setAppUserId(88L);
+        order.setStatus(5);
+        when(mapper.selectById(1001L)).thenReturn(order);
+
+        AjaxResult result = service.stopChargingByApplet("1001");
+
+        assertFalse(result.isSuccess());
+        verify(sendMessageClient, never()).platformStopCharging(any(PlatformStopCharging.class));
+    }
 }

--
Gitblit v1.7.1