From 976ff3fe092ee2f9515013dbfab77d25b2944b75 Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期三, 14 十月 2026 11:18:29 +0800
Subject: [PATCH] 修改bug

---
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java |  179 +++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 137 insertions(+), 42 deletions(-)

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 eaeb5b5..80c4878 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
@@ -1936,13 +1936,7 @@
         chargingOrder.setPayTime(callbackPayTime);
 
         //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据
-        PreChargeCheck preChargeCheck = new PreChargeCheck();
-        preChargeCheck.setElectronicLockLock(false);
-        preChargeCheck.setInsulationTesting(false);
-        preChargeCheck.setSecureConnectionDetection(false);
-        preChargeCheck.setStartupSuccess(1);
-        preChargeCheck.setStartCommandSent(false);
-        preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now());
+        PreChargeCheck preChargeCheck = newPreChargeCheck(chargingOrder);
         String key = "AQJC_" + chargingOrder.getChargingGunId();
         redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS);
 
@@ -2082,6 +2076,33 @@
                 && Boolean.TRUE.equals(check.getSecureConnectionDetection());
     }
 
+    /**
+     * 查询充电枪进行中的待启动订单(支付完成、启动中)
+     */
+    private TChargingOrder getPendingStartOrder(Integer chargingGunId) {
+        return this.getOne(new LambdaQueryWrapper<TChargingOrder>()
+                .eq(TChargingOrder::getChargingGunId, chargingGunId)
+                .eq(TChargingOrder::getRechargePaymentStatus, 2)
+                .eq(TChargingOrder::getStatus, 2)
+                .eq(TChargingOrder::getDelFlag, 0)
+                .orderByDesc(TChargingOrder::getCreateTime)
+                .last("limit 1"));
+    }
+
+    /**
+     * 初始化安全检测等待对象(与建单/支付完成时的初始状态保持一致)
+     */
+    private PreChargeCheck newPreChargeCheck(TChargingOrder chargingOrder) {
+        PreChargeCheck preChargeCheck = new PreChargeCheck();
+        preChargeCheck.setElectronicLockLock(false);
+        preChargeCheck.setInsulationTesting(false);
+        preChargeCheck.setSecureConnectionDetection(false);
+        preChargeCheck.setStartupSuccess(1);
+        preChargeCheck.setStartCommandSent(false);
+        preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now());
+        return preChargeCheck;
+    }
+
     private String normalizeVin(String vin) {
         return StringUtils.hasLength(vin) ? vin.replaceAll("\\s+", "").toUpperCase(Locale.ROOT) : null;
     }
@@ -2119,6 +2140,44 @@
         }
     }
 
+    /**
+     * 启动超时兜底:订单支付完成后长时间未真正开始充电(等待安全检测无进展、启动指令未生效),
+     * 自动取消订单并释放锁定额度,避免订单永久停留在「启动中」导致该枪无法再次启动。
+     */
+    @Scheduled(fixedDelay = 30000L)
+    public void cancelStartupTimeoutOrders() {
+        LocalDateTime deadline = LocalDateTime.now().minusMinutes(3);
+        List<TChargingOrder> timeoutOrders = this.list(new LambdaQueryWrapper<TChargingOrder>()
+                .eq(TChargingOrder::getRechargePaymentStatus, 2)
+                .eq(TChargingOrder::getStatus, 2)
+                .eq(TChargingOrder::getDelFlag, 0)
+                .isNull(TChargingOrder::getStartTime)
+                .isNull(TChargingOrder::getReservationTime)
+                .lt(TChargingOrder::getPayTime, deadline)
+                .last("limit 100"));
+        for (TChargingOrder order : timeoutOrders) {
+            try {
+                PreChargeCheck check = redisService.getCacheObject("AQJC_" + order.getChargingGunId());
+                if (null != check && Boolean.TRUE.equals(check.getStartCommandSent())) {
+                    // 已下发启动指令,交给既有启动结果检测(timingDetection)处理,避免误取消
+                    continue;
+                }
+                log.warn("订单编号={}:等待安全检测超时,自动取消订单并释放额度,充电枪id={}", order.getCode(), order.getChargingGunId());
+                refund(order.getCode());
+                TChargingOrder finish = new TChargingOrder();
+                finish.setId(order.getId());
+                finish.setAppUserId(order.getAppUserId());
+                finish.setStatus(5);
+                finish.setEndMode(0);
+                finish.setEndTime(LocalDateTime.now());
+                this.updateById(finish);
+                redisService.deleteObject("AQJC_" + order.getChargingGunId());
+            } catch (Exception e) {
+                log.error("启动超时订单自动取消失败,orderCode={}", order.getCode(), e);
+            }
+        }
+    }
+
     private void startChargingAfterSafety(TChargingOrder chargingOrder, PreChargeCheck check) {
         RLock lock = redissonClient.getLock("START_AFTER_SAFETY:" + chargingOrder.getId());
         try {
@@ -2152,12 +2211,18 @@
             command.setCharging_gun_code(gun.getCode());
             command.setCard_number(latest.getId().toString());
             command.setAccount_balance(latest.getChargeAmount());
-            check.setStartCommandSent(true);
             latest.setActualVin(actualVin);
             this.updateById(latest);
-            redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS);
             log.info(latest.getCode() + ":安全检测及VIN校验通过,下发启动充电命令:" + command);
-            sendMessageClient.platformStartCharging(command);
+            try {
+                sendMessageClient.platformStartCharging(command);
+            } catch (Exception e) {
+                // 下发失败不置 startCommandSent:保留后续安全检测上报重试机会,超时订单由兜底任务取消
+                log.error(latest.getCode() + ":启动充电命令下发失败,等待下次安全检测上报重试", e);
+                return;
+            }
+            check.setStartCommandSent(true);
+            redisService.setCacheObject("AQJC_" + latest.getChargingGunId(), check, 24L, TimeUnit.HOURS);
             ScheduledExecutorService resultScheduler = Executors.newScheduledThreadPool(1);
             resultScheduler.scheduleAtFixedRate(() -> {
                 if (timingDetection(latest.getId())) {
@@ -2196,8 +2261,13 @@
             Integer failure_cause = platformStartChargingReply.getFailure_cause();
             Integer counter = counter_map.get(code);
             PreChargeCheck preChargeCheck1 = redisService.getCacheObject(key);
-            //15分钟内还未插枪则取消充电,退回金额。
-            if (failure_cause == 5 && (null == counter || counter < 60 * 15)) {
+            if (null == preChargeCheck1) {
+                // 缓存缺失时用临时对象推进,避免启动结果补偿因空缓存中断
+                preChargeCheck1 = new PreChargeCheck();
+                preChargeCheck1.setStartupSuccess(1);
+            }
+            //15分钟内还未插枪则取消充电,退回金额。按10秒探测一次,90次约15分钟
+            if (failure_cause == 5 && (null == counter || counter < 90)) {
                 counter = (null == counter ? 0 : counter) + 1;
                 counter_map.put(code, counter);
                 //启动失败
@@ -2248,6 +2318,11 @@
             return true;
         } else {
             PreChargeCheck preChargeCheck1 = redisService.getCacheObject(key);
+            if (null == preChargeCheck1) {
+                // 缓存缺失时用临时对象推进,避免启动结果补偿因空缓存中断
+                preChargeCheck1 = new PreChargeCheck();
+                preChargeCheck1.setStartupSuccess(1);
+            }
             UploadRealTimeMonitoringData uploadRealTimeMonitoringData = uploadRealTimeMonitoringDataService.getDataByOrderCode(code, "desc");
             if (null != uploadRealTimeMonitoringData) {
                 //启动成功
@@ -2279,8 +2354,8 @@
             } else {
                 Integer counter = boot_failed_map.get(code);
                 log.info(code + ":-------------------未上传开启充电结果-------------------" + counter);
-                //15分钟内未启动成功,退回金额。
-                if (null == counter || counter < 60 * 15) {
+                //15分钟内未启动成功,退回金额。按10秒探测一次,90次约15分钟
+                if (null == counter || counter < 90) {
                     counter = (null == counter ? 0 : counter) + 1;
                     boot_failed_map.put(code, counter);
                     //启动失败
@@ -2332,25 +2407,43 @@
         GetChargingGunByCode code = new GetChargingGunByCode();
         code.setCharging_pile_code(securityDetection.getCharging_pile_code());
         code.setCharging_gun_code(securityDetection.getCharging_gun_code());
-        TChargingGun chargingGun = chargingGunClient.getChargingGunByCode(code).getData();
-        PreChargeCheck preChargeCheck1 = redisService.getCacheObject("AQJC_" + chargingGun.getId());
-        if (null != preChargeCheck1) {
-            preChargeCheck1.setElectronicLockLock(Integer.valueOf(1).equals(securityDetection.getElectronic_lock_lock()));
-            preChargeCheck1.setInsulationTesting(Integer.valueOf(0).equals(securityDetection.getNsulation_detection()));
-            preChargeCheck1.setSecureConnectionDetection(Integer.valueOf(1).equals(securityDetection.getSecure_connection()));
-            preChargeCheck1.setActualVin(normalizeVin(securityDetection.getVin()));
-            redisService.setCacheObject("AQJC_" + chargingGun.getId(), preChargeCheck1, 24L, TimeUnit.HOURS);
-            if (isSafetyCheckPassed(preChargeCheck1)) {
-                TChargingOrder pendingOrder = this.getOne(new LambdaQueryWrapper<TChargingOrder>()
-                        .eq(TChargingOrder::getChargingGunId, chargingGun.getId())
-                        .eq(TChargingOrder::getRechargePaymentStatus, 2)
-                        .eq(TChargingOrder::getStatus, 2)
-                        .eq(TChargingOrder::getDelFlag, 0)
-                        .orderByDesc(TChargingOrder::getCreateTime)
-                        .last("limit 1"));
-                if (pendingOrder != null) {
-                    startChargingAfterSafety(pendingOrder, preChargeCheck1);
-                }
+        TChargingGun chargingGun = null;
+        try {
+            chargingGun = chargingGunClient.getChargingGunByCode(code).getData();
+        } catch (Exception e) {
+            log.error("安全检测上报-查询充电枪异常,charging_pile_code={},charging_gun_code={}",
+                    securityDetection.getCharging_pile_code(), securityDetection.getCharging_gun_code(), e);
+        }
+        if (null == chargingGun) {
+            log.warn("安全检测上报-未匹配到充电枪,忽略,charging_pile_code={},charging_gun_code={}",
+                    securityDetection.getCharging_pile_code(), securityDetection.getCharging_gun_code());
+            return;
+        }
+        String key = "AQJC_" + chargingGun.getId();
+        PreChargeCheck preChargeCheck1 = redisService.getCacheObject(key);
+        if (null == preChargeCheck1) {
+            // 缓存缺失(等待窗口内未收到上报、缓存被清理)时按该枪进行中的待启动订单重建,
+            // 保证设备后续安全检测上报仍能推进启动,避免订单永久卡在启动中
+            TChargingOrder pendingOrder = getPendingStartOrder(chargingGun.getId());
+            if (null == pendingOrder) {
+                log.info("安全检测上报-无待启动订单,忽略,充电枪id={},charging_pile_code={},charging_gun_code={}",
+                        chargingGun.getId(), securityDetection.getCharging_pile_code(), securityDetection.getCharging_gun_code());
+                return;
+            }
+            preChargeCheck1 = newPreChargeCheck(pendingOrder);
+            log.warn("安全检测上报-AQJC缓存缺失,已按待启动订单重建,订单编号={},充电枪id={}", pendingOrder.getCode(), chargingGun.getId());
+        }
+        preChargeCheck1.setElectronicLockLock(Integer.valueOf(1).equals(securityDetection.getElectronic_lock_lock()));
+        preChargeCheck1.setInsulationTesting(Integer.valueOf(0).equals(securityDetection.getNsulation_detection()));
+        preChargeCheck1.setSecureConnectionDetection(Integer.valueOf(1).equals(securityDetection.getSecure_connection()));
+        preChargeCheck1.setActualVin(normalizeVin(securityDetection.getVin()));
+        // 刷新等待基准时间,避免前端等待窗口/超时判断误伤正在推进的安全检测
+        preChargeCheck1.setAddTime(LocalDateTime.now());
+        redisService.setCacheObject(key, preChargeCheck1, 24L, TimeUnit.HOURS);
+        if (isSafetyCheckPassed(preChargeCheck1)) {
+            TChargingOrder pendingOrder = getPendingStartOrder(chargingGun.getId());
+            if (pendingOrder != null) {
+                startChargingAfterSafety(pendingOrder, preChargeCheck1);
             }
         }
     }
@@ -2375,6 +2468,11 @@
             preChargeCheck1.setElectronicLockLock(true);
             preChargeCheck1.setInsulationTesting(true);
             preChargeCheck1.setStartupSuccess(1);
+        } else {
+            // 缓存缺失时用临时对象推进订单状态,避免设备已启动成功而订单因缓存为空无法流转
+            preChargeCheck1 = new PreChargeCheck();
+            preChargeCheck1.setElectronicLockLock(true);
+            preChargeCheck1.setInsulationTesting(true);
         }
         if (0 == startup_result) {
             //启动失败
@@ -2769,19 +2867,22 @@
     @Override
     public ChargingDetails getChargingDetails(Integer id) {
         Long userId = tokenService.getLoginUserApplet().getUserId();
+        // 状态包含已结束(5):订单结束后小程序需继续拿到该笔订单,用于关闭「请勿拔枪,正在结束充电」
+        // 并展示「充电完成,可拔枪」;仅取最近一笔,避免历史完成订单长期滞留结束态
         TChargingOrder one = this.getOne(new LambdaQueryWrapper<TChargingOrder>().eq(TChargingOrder::getAppUserId, userId).eq(TChargingOrder::getChargingGunId, id)
                 .eq(TChargingOrder::getRechargePaymentStatus, 2)
-                .in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4))
+                .in(TChargingOrder::getStatus, Arrays.asList(2, 3, 4, 5))
                 .eq(TChargingOrder::getDelFlag, 0)
                 .orderByDesc(TChargingOrder::getCreateTime)
                 .last(" limit 0, 1"));
         if (null == one || !one.getAppUserId().equals(userId)) {
             // VIN 码充电订单兜底:VD 订单存于 t_vin_charging_order,且后付模式无支付动作,
             // 不按 rechargePaymentStatus 过滤;TVinChargingOrder 继承 TChargingOrder,后续组装原样复用
+            // 状态同样包含已结束(5),保证 VIN 订单结束后前端仍能拿到完成态
             TVinChargingOrder vinOrder = vinChargingOrderMapper.selectOne(new LambdaQueryWrapper<TVinChargingOrder>()
                     .eq(TVinChargingOrder::getAppUserId, userId)
                     .eq(TVinChargingOrder::getChargingGunId, id)
-                    .in(TVinChargingOrder::getStatus, Arrays.asList(2, 3, 4))
+                    .in(TVinChargingOrder::getStatus, Arrays.asList(2, 3, 4, 5))
                     .eq(TVinChargingOrder::getDelFlag, 0)
                     .orderByDesc(TVinChargingOrder::getCreateTime)
                     .last(" limit 0, 1"));
@@ -8240,13 +8341,7 @@
 
 
         //添加安全检测数据到缓存中,每步安全检测完成后需要更新缓存数据
-        PreChargeCheck preChargeCheck = new PreChargeCheck();
-        preChargeCheck.setElectronicLockLock(false);
-        preChargeCheck.setInsulationTesting(false);
-        preChargeCheck.setSecureConnectionDetection(false);
-        preChargeCheck.setStartupSuccess(1);
-        preChargeCheck.setStartCommandSent(false);
-        preChargeCheck.setAddTime(chargingOrder.getReservationTime() != null ? chargingOrder.getReservationTime() : LocalDateTime.now());
+        PreChargeCheck preChargeCheck = newPreChargeCheck(chargingOrder);
         String key = "AQJC_" + chargingOrder.getChargingGunId();
         redisService.setCacheObject(key, preChargeCheck, 24L, TimeUnit.HOURS);
 

--
Gitblit v1.7.1