From 3080d55319ab40fe99064aa4689dbe0c38301e9d Mon Sep 17 00:00:00 2001
From: puzhibing <393733352@qq.com>
Date: 星期五, 31 七月 2026 18:49:55 +0800
Subject: [PATCH] 修改管理后台 1、修改退款后没有重新添加新的分账记录

---
 ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java         |  146 +++++++++++++++++++++++++++++++++++-
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java                 |    3 
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java |   20 ++++-
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java             |   60 ++++++++++-----
 4 files changed, 198 insertions(+), 31 deletions(-)

diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
index 7951a95..3f5c578 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/controller/TAccountingStrategyController.java
@@ -398,11 +398,17 @@
     @ApiOperation(tags = {"后台-计费策略"},value = "删除计费策略")
     @DeleteMapping(value = "/deleteById")
     public AjaxResult<Boolean> deleteById(@RequestParam("id") Integer id) {
-        // 判断策略是否在使用
+        // 判断策略是否被站点使用
         long count = siteService.count(Wrappers.lambdaQuery(Site.class)
                 .eq(Site::getAccountingStrategyId, id));
         if(count>0){
-            return AjaxResult.error("该计费策略正在使用中,无法删除");
+            return AjaxResult.error("该计费策略正在被站点使用,无法删除");
+        }
+        // 判断策略是否被充电枪使用
+        long gunCount = chargingGunService.count(Wrappers.lambdaQuery(TChargingGun.class)
+                .eq(TChargingGun::getAccountingStrategyId, id));
+        if(gunCount>0){
+            return AjaxResult.error("该计费策略正在被充电枪使用,无法删除");
         }
         // 刪除计费策略明细信息
         accountingStrategyDetailService.remove(Wrappers.lambdaQuery(TAccountingStrategyDetail.class)
@@ -419,11 +425,17 @@
     @DeleteMapping(value = "/deleteByIds")
     public AjaxResult<Boolean> deleteByIds(@RequestParam String ids) {
         String[] split = ids.split(",");
-        // 判断策略是否在使用
+        // 判断策略是否被站点使用
         long count = siteService.count(Wrappers.lambdaQuery(Site.class)
                 .in(Site::getAccountingStrategyId, Arrays.asList(split)));
         if(count>0){
-            return AjaxResult.error("该计费策略正在使用中,无法删除");
+            return AjaxResult.error("该计费策略正在被站点使用,无法删除");
+        }
+        // 判断策略是否被充电枪使用
+        long gunCount = chargingGunService.count(Wrappers.lambdaQuery(TChargingGun.class)
+                .in(TChargingGun::getAccountingStrategyId, Arrays.asList(split)));
+        if(gunCount>0){
+            return AjaxResult.error("该计费策略正在被充电枪使用,无法删除");
         }
         // 刪除计费策略明细信息
         accountingStrategyDetailService.remove(Wrappers.lambdaQuery(TAccountingStrategyDetail.class)
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java
index a4f0d43..3af2da3 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java
@@ -958,29 +958,49 @@
 	@Override
 	@Transactional(rollbackFor = Exception.class)
 	public void setAccountingStrategy(Integer id, Integer accountingStrategyId) {
-		Site site = this.getById(id);
-		site.setAccountingStrategyId(accountingStrategyId);
-		this.updateById(site);
-		List<TChargingGun> list = chargingGunService.list(new LambdaQueryWrapper<TChargingGun>().in(TChargingGun::getSiteId, id).eq(TChargingGun::getDelFlag, 0));
-		for (TChargingGun tChargingGun : list) {
-			TChargingGun tChargingGun1 = new TChargingGun();
-			tChargingGun1.setId(tChargingGun.getId());
-			tChargingGun1.setAccountingStrategyId(accountingStrategyId);
-			chargingGunService.updateById(tChargingGun1);
-		}
+		// 只更新计费策略字段,避免 updateById 全字段更新导致 SQL 过长
+		this.lambdaUpdate()
+				.eq(Site::getId, id)
+				.set(Site::getAccountingStrategyId, accountingStrategyId)
+				.update();
+		chargingGunService.lambdaUpdate()
+				.eq(TChargingGun::getSiteId, id)
+				.eq(TChargingGun::getDelFlag, 0)
+				.set(TChargingGun::getAccountingStrategyId, accountingStrategyId)
+				.update();
 		
-		//下发硬件数据
+		//下发硬件数据 - 异步执行,避免事务长时间占用数据库连接导致 Communications link failure
 		List<TAccountingStrategyDetail> accountingStrategyDetails = accountingStrategyDetailService.list(new LambdaQueryWrapper<TAccountingStrategyDetail>().eq(TAccountingStrategyDetail::getAccountingStrategyId, accountingStrategyId));
-		List<TChargingPile> list1 = chargingPileService.lambdaQuery().eq(TChargingPile::getSiteId, id).eq(TChargingPile::getDelFlag, 0).list();
-		for (TChargingPile tChargingPile : list1) {
-			integrationClient.setupBillingModel1(tChargingPile.getCode(), JSON.toJSONString(accountingStrategyDetails));
-			chengDuJianGuanClient.notificationStationFee(tChargingPile.getId());
-		}
+		List<TChargingPile> chargingPiles = chargingPileService.lambdaQuery().eq(TChargingPile::getSiteId, id).eq(TChargingPile::getDelFlag, 0).list();
+		Site site = this.lambdaQuery()
+				.select(Site::getId, Site::getIsOpenSynchronization)
+				.eq(Site::getId, id)
+				.one();
+		boolean needSync = site != null && site.getIsOpenSynchronization() != null && site.getIsOpenSynchronization() == 1;
 		
-		if(1 == site.getIsOpenSynchronization()){
-			//推送三方平台
-			tcecClient.notificationStationChange(Arrays.asList(id));
-		}
+		new Thread(() -> {
+			for (TChargingPile tChargingPile : chargingPiles) {
+				try {
+					integrationClient.setupBillingModel1(tChargingPile.getCode(), JSON.toJSONString(accountingStrategyDetails));
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+				try {
+					chengDuJianGuanClient.notificationStationFee(tChargingPile.getId());
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+			
+			if (needSync) {
+				//推送三方平台
+				try {
+					tcecClient.notificationStationChange(Arrays.asList(id));
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		}).start();
 	}
 	@Override
 	@Transactional(rollbackFor = Exception.class)
diff --git a/ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java b/ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java
index 4c4ef7f..d333b88 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java
@@ -1,33 +1,76 @@
 package com.ruoyi.chargingPile.service.impl;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.ruoyi.chargingPile.api.dto.GetSiteListDTO;
+import com.ruoyi.chargingPile.api.model.Site;
+import com.ruoyi.chargingPile.api.model.TAccountingStrategyDetail;
+import com.ruoyi.chargingPile.api.model.TChargingGun;
+import com.ruoyi.chargingPile.api.model.TChargingPile;
 import com.ruoyi.chargingPile.mapper.SiteMapper;
 import com.ruoyi.chargingPile.service.IPartnerService;
+import com.ruoyi.chargingPile.service.ITAccountingStrategyDetailService;
+import com.ruoyi.chargingPile.service.ITChargingGunService;
+import com.ruoyi.chargingPile.service.ITChargingPileService;
 import com.ruoyi.common.core.domain.R;
+import com.ruoyi.integration.api.feignClient.ChengDuJianGuanClient;
+import com.ruoyi.integration.api.feignClient.IntegrationClient;
+import com.ruoyi.integration.api.feignClient.TcecClient;
 import com.ruoyi.system.api.domain.SysUser;
 import com.ruoyi.system.api.feignClient.SysUserClient;
+import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.springframework.test.util.ReflectionTestUtils;
 
+import java.util.ArrayList;
 import java.util.Collections;
+import java.util.List;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.atLeastOnce;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.timeout;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 class SiteServiceImplTest {
 
+    private SiteServiceImpl service;
+    private SiteMapper siteMapper;
+    private ITChargingGunService chargingGunService;
+    private ITAccountingStrategyDetailService accountingStrategyDetailService;
+    private ITChargingPileService chargingPileService;
+    private IntegrationClient integrationClient;
+    private ChengDuJianGuanClient chengDuJianGuanClient;
+    private TcecClient tcecClient;
+
+    @BeforeEach
+    void setUp() {
+        service = new SiteServiceImpl();
+        siteMapper = mock(SiteMapper.class);
+        chargingGunService = mock(ITChargingGunService.class);
+        accountingStrategyDetailService = mock(ITAccountingStrategyDetailService.class);
+        chargingPileService = mock(ITChargingPileService.class);
+        integrationClient = mock(IntegrationClient.class);
+        chengDuJianGuanClient = mock(ChengDuJianGuanClient.class);
+        tcecClient = mock(TcecClient.class);
+
+        ReflectionTestUtils.setField(service, "baseMapper", siteMapper);
+        ReflectionTestUtils.setField(service, "chargingGunService", chargingGunService);
+        ReflectionTestUtils.setField(service, "accountingStrategyDetailService", accountingStrategyDetailService);
+        ReflectionTestUtils.setField(service, "chargingPileService", chargingPileService);
+        ReflectionTestUtils.setField(service, "integrationClient", integrationClient);
+        ReflectionTestUtils.setField(service, "chengDuJianGuanClient", chengDuJianGuanClient);
+        ReflectionTestUtils.setField(service, "tcecClient", tcecClient);
+    }
+
     @Test
     void partnerSiteListIncludesAllSitesRelatedToCurrentPartner() {
-        SiteServiceImpl service = new SiteServiceImpl();
-        SiteMapper mapper = mock(SiteMapper.class);
         IPartnerService partnerService = mock(IPartnerService.class);
         SysUserClient sysUserClient = mock(SysUserClient.class);
-        ReflectionTestUtils.setField(service, "baseMapper", mapper);
         ReflectionTestUtils.setField(service, "partnerService", partnerService);
         ReflectionTestUtils.setField(service, "sysUserClient", sysUserClient);
 
@@ -37,13 +80,106 @@
         sysUser.setObjectId(715);
 
         when(sysUserClient.getSysUser(100L)).thenReturn(R.ok(sysUser));
-        when(mapper.getSiteOptions(isNull(), eq(715)))
+        when(siteMapper.getSiteOptions(isNull(), eq(715)))
                 .thenReturn(Collections.<GetSiteListDTO>emptyList());
 
         service.getSiteList1(100L);
 
-        verify(mapper).getSiteOptions(isNull(), eq(715));
+        verify(siteMapper).getSiteOptions(isNull(), eq(715));
         verify(partnerService, never()).authSite(any(), any());
         verify(sysUserClient, never()).isAdmin(any());
     }
+
+    /**
+     * 验证 setAccountingStrategy 使用 lambdaUpdate 只更新 accountingStrategyId 字段,
+     * 并且远程调用在异步线程中执行,避免事务长时间占用数据库连接
+     */
+    @Test
+    void setAccountingStrategyUpdatesOnlyTargetFieldAndAsyncRemoteCalls() throws Exception {
+        Integer siteId = 1;
+        Integer accountingStrategyId = 100;
+
+        // mock 充电枪列表
+        List<TChargingGun> guns = new ArrayList<>();
+        TChargingGun gun = new TChargingGun();
+        gun.setId(10);
+        gun.setSiteId(siteId);
+        guns.add(gun);
+
+        // mock 充电桩列表
+        List<TChargingPile> piles = new ArrayList<>();
+        TChargingPile pile = new TChargingPile();
+        pile.setCode("PILE001");
+        pile.setId(200);
+        pile.setSiteId(siteId);
+        piles.add(pile);
+
+        // mock 计费策略明细
+        List<TAccountingStrategyDetail> details = new ArrayList<>();
+
+        // mock Site 查询(用于 isOpenSynchronization)
+        Site mockSite = new Site();
+        mockSite.setId(siteId);
+        mockSite.setIsOpenSynchronization(1);
+
+        when(siteMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Collections.singletonList(mockSite));
+        when(chargingGunService.list(any(LambdaQueryWrapper.class))).thenReturn(guns);
+        when(accountingStrategyDetailService.list(any(LambdaQueryWrapper.class))).thenReturn(details);
+
+        assertDoesNotThrow(() -> service.setAccountingStrategy(siteId, accountingStrategyId));
+
+        // 验证数据库操作:站点和充电枪都通过 lambdaUpdate 更新
+        verify(siteMapper, atLeastOnce()).update(any(), any());
+        verify(siteMapper).selectList(any(LambdaQueryWrapper.class));
+
+        // 异步线程执行需要等待一小段时间
+        Thread.sleep(500);
+
+        // 验证远程调用在异步线程中执行
+        verify(integrationClient, timeout(1000)).setupBillingModel1(eq("PILE001"), any());
+        verify(chengDuJianGuanClient, timeout(1000)).notificationStationFee(eq(200));
+        verify(tcecClient, timeout(1000)).notificationStationChange(any());
+    }
+
+    /**
+     * 验证 setAccountingStrategy 在站点无需同步时不会调用三方平台推送
+     */
+    @Test
+    void setAccountingStrategySkipsSyncWhenNotOpen() throws Exception {
+        Integer siteId = 2;
+        Integer accountingStrategyId = 200;
+
+        // mock 充电枪列表
+        TChargingGun gun = new TChargingGun();
+        gun.setId(20);
+        gun.setSiteId(siteId);
+        List<TChargingGun> guns = Collections.singletonList(gun);
+
+        // mock 充电桩列表
+        TChargingPile pile = new TChargingPile();
+        pile.setCode("PILE002");
+        pile.setId(201);
+        pile.setSiteId(siteId);
+        List<TChargingPile> piles = Collections.singletonList(pile);
+
+        List<TAccountingStrategyDetail> details = new ArrayList<>();
+
+        // isOpenSynchronization = 0,不需要同步
+        Site mockSite = new Site();
+        mockSite.setId(siteId);
+        mockSite.setIsOpenSynchronization(0);
+
+        when(siteMapper.selectList(any(LambdaQueryWrapper.class))).thenReturn(Collections.singletonList(mockSite));
+        when(chargingGunService.list(any(LambdaQueryWrapper.class))).thenReturn(guns);
+        when(accountingStrategyDetailService.list(any(LambdaQueryWrapper.class))).thenReturn(details);
+
+        assertDoesNotThrow(() -> service.setAccountingStrategy(siteId, accountingStrategyId));
+
+        Thread.sleep(500);
+
+        verify(integrationClient, timeout(1000)).setupBillingModel1(eq("PILE002"), any());
+        verify(chengDuJianGuanClient, timeout(1000)).notificationStationFee(eq(201));
+        // tcecClient.notificationStationChange 不应被调用
+        verify(tcecClient, never()).notificationStationChange(any());
+    }
 }
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 840aeda..9dfa2a4 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
@@ -8022,9 +8022,8 @@
 
     private boolean isNeedChargingOrderShare(TChargingOrder chargingOrder) {
         return chargingOrder != null
-                && Integer.valueOf(5).equals(chargingOrder.getStatus())
                 && Integer.valueOf(2).equals(chargingOrder.getRechargePaymentStatus())
-                && Integer.valueOf(2).equals(chargingOrder.getRefundStatus())
+                && (Integer.valueOf(2).equals(chargingOrder.getRefundStatus()) || Integer.valueOf(5).equals(chargingOrder.getStatus()))
                 && getChargingOrderShareBaseAmount(chargingOrder).compareTo(BigDecimal.ZERO) > 0;
     }
 

--
Gitblit v1.7.1