From 607e4ab169b003991ed4788c9dbe0bd1fefc69f3 Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期三, 29 七月 2026 23:26:56 +0800
Subject: [PATCH] 各类修复

---
 ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/PartnerTypeRoleService.java               |  122 ++++++++++++
 ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java |   49 ++++
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/impl/TChargingOrderServiceImpl.java         |    4 
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/util/task/AccountSettlementIncomeTest.java          |    8 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java                    |    4 
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java                             |   14 
 docs/superpowers/plans/2026-07-29-partner-type-role-sync.md                                                 |   50 +++++
 ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java                 |    4 
 ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml                      |   27 ++
 ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java           |  140 ++++++++++++++
 sql/20260728_account_settlement_v2.sql                                                                      |    1 
 ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java                 |   13 +
 ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java                  |    2 
 ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java                           |    2 
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java                |    6 
 ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/service/impl/SiteServiceImpl.java     |   28 --
 ruoyi-modules/ruoyi-system/pom.xml                                                                          |    7 
 ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml                          |   68 ++++--
 ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/AccountSettlementStatementMapperTest.java    |   45 ++++
 19 files changed, 531 insertions(+), 63 deletions(-)

diff --git a/docs/superpowers/plans/2026-07-29-partner-type-role-sync.md b/docs/superpowers/plans/2026-07-29-partner-type-role-sync.md
new file mode 100644
index 0000000..8eccdf6
--- /dev/null
+++ b/docs/superpowers/plans/2026-07-29-partner-type-role-sync.md
@@ -0,0 +1,50 @@
+# Partner Type Role Sync Implementation Plan
+
+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**Goal:** Ensure partner accounts automatically receive the enabled system role matching their partner type whenever user permissions are loaded.
+
+**Architecture:** Add a focused system-service component that resolves the current partner type through the existing charging-pile Feign client and synchronizes only the three built-in type roles in `sys_user_role`. Call it before login/current-user permissions are calculated, then reload the user so its role collection and menu permissions reflect the synchronized database state.
+
+**Tech Stack:** Java 8, Spring Boot, MyBatis-Plus, OpenFeign, JUnit 5, Mockito
+
+## Global Constraints
+
+- Partner type `1` maps to role key `independent_partner`.
+- Partner type `2` maps to role key `site_owner`.
+- Partner type `3` maps to role key `service_provider`.
+- Preserve roles outside these three built-in type roles.
+- Remove stale built-in type roles when the partner type changes.
+- Do not modify unrelated existing workspace changes.
+
+---
+
+### Task 1: Partner type role synchronizer
+
+**Files:**
+- Create: `ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/PartnerTypeRoleService.java`
+- Test: `ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java`
+
+**Interfaces:**
+- Consumes: `SiteClient.getPartnerR(Integer)`, `ISysRoleService`, and `ISysUserRoleService`.
+- Produces: `void synchronize(SysUser user)`.
+
+- [x] **Step 1: Write failing tests for all three mappings, custom-role preservation, stale built-in-role removal, non-partner bypass, and missing-role failure.**
+- [x] **Step 2: Run `mvn -pl ruoyi-modules/ruoyi-system -am -Dtest=PartnerTypeRoleServiceTest -Dsurefire.failIfNoSpecifiedTests=false test` and verify the new tests fail before implementation.**
+- [x] **Step 3: Implement role-key mapping and idempotent `sys_user_role` synchronization.**
+- [x] **Step 4: Run the focused test and verify it passes.**
+
+### Task 2: Permission-loading integration
+
+**Files:**
+- Modify: `ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java`
+- Test: `ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java`
+
+**Interfaces:**
+- Consumes: `PartnerTypeRoleService.synchronize(SysUser)`.
+- Produces: login and current-user responses whose roles and permissions include the matching partner-type role.
+
+- [x] **Step 1: Invoke synchronization in both `/user/info/{username}` and `/user/getInfo` before permission calculation.**
+- [x] **Step 2: Reload `SysUser` after synchronization so `SysUser.roles` is current.**
+- [x] **Step 3: Run the focused test and compile the system module.**
+- [x] **Step 4: Review the diff to confirm unrelated working-tree changes remain untouched.**
diff --git a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
index 9d65389..04f0900 100644
--- a/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
+++ b/ruoyi-api/ruoyi-api-order/src/main/java/com/ruoyi/order/api/vo/AccountListVO.java
@@ -1,9 +1,7 @@
 package com.ruoyi.order.api.vo;
 
 import com.ruoyi.common.core.web.page.PageInfo;
-import com.ruoyi.order.api.model.TChargingBill;
 import com.ruoyi.order.api.model.TChargingOrder;
-import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
diff --git a/ruoyi-modules/ruoyi-system/pom.xml b/ruoyi-modules/ruoyi-system/pom.xml
index 37af99a..56a85a6 100644
--- a/ruoyi-modules/ruoyi-system/pom.xml
+++ b/ruoyi-modules/ruoyi-system/pom.xml
@@ -145,6 +145,11 @@
 		    <groupId>com.ruoyi</groupId>
 		    <artifactId>ruoyi-api-chargingPile</artifactId>
 	    </dependency>
+	    <dependency>
+		    <groupId>org.springframework.boot</groupId>
+		    <artifactId>spring-boot-starter-test</artifactId>
+		    <scope>test</scope>
+	    </dependency>
 
     </dependencies>
 
@@ -177,4 +182,4 @@
         </resources>
     </build>
    
-</project>
\ No newline at end of file
+</project>
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
index 275c851..17d73e9 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysRoleController.java
@@ -339,8 +339,8 @@
 	@ApiOperation(value = "编辑角色", tags = {"管理后台-角色管理"})
 	public AjaxResult roleUpdate(@Validated @RequestBody RoleUpdateDto dto) {
 		SysRole oldRole = roleService.selectRoleById(dto.getRoleId());
-		if (isSystemBuiltinRole(oldRole)) {
-			return AjaxResult.error("系统内置角色不可编辑");
+		if (oldRole != null && oldRole.isAdmin()) {
+			return AjaxResult.error("超级管理员角色不可编辑");
 		}
 		SysRole role = new SysRole();
 		role.setRoleName(dto.getRoleName());
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
index 9788e50..d983e2a 100644
--- a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/controller/SysUserController.java
@@ -72,6 +72,9 @@
     private ISysPermissionService permissionService;
 
     @Resource
+    private PartnerTypeRoleService partnerTypeRoleService;
+
+    @Resource
     private ISysConfigService configService;
 
     @Resource
@@ -505,6 +508,7 @@
         if (StringUtils.isNull(sysUser)) {
             return R.fail("用户名/密码错误");
         }
+        sysUser = synchronizePartnerTypeRole(sysUser);
         // 角色集合
         Set<String> roles = permissionService.getRolePermission(sysUser);
         // 权限集合
@@ -539,6 +543,7 @@
     @GetMapping("/getInfo")
     public AjaxResult getInfo() {
         SysUser user = userService.selectUserById(tokenService.getLoginUser().getUserid());
+        user = synchronizePartnerTypeRole(user);
         // 角色集合
         Set<String> roles = permissionService.getRolePermission(user);
         // 权限集合
@@ -550,6 +555,14 @@
         return ajax;
     }
 
+    private SysUser synchronizePartnerTypeRole(SysUser user) {
+        partnerTypeRoleService.synchronize(user);
+        if (user != null && Integer.valueOf(2).equals(user.getRoleType()) && user.getObjectId() != null) {
+            return userService.selectUserById(user.getUserId());
+        }
+        return user;
+    }
+
 
     /**
      * 发送重置密码验证码
diff --git a/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/PartnerTypeRoleService.java b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/PartnerTypeRoleService.java
new file mode 100644
index 0000000..1d4cde2
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/main/java/com/ruoyi/system/service/PartnerTypeRoleService.java
@@ -0,0 +1,122 @@
+package com.ruoyi.system.service;
+
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.ruoyi.chargingPile.api.feignClient.SiteClient;
+import com.ruoyi.chargingPile.api.model.Partner;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.domain.SysUserRole;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Synchronizes the built-in role granted to a partner account from its partner type.
+ */
+@Service
+public class PartnerTypeRoleService {
+
+    static final String INDEPENDENT_PARTNER_ROLE_KEY = "independent_partner";
+    static final String SITE_OWNER_ROLE_KEY = "site_owner";
+    static final String SERVICE_PROVIDER_ROLE_KEY = "service_provider";
+
+    private static final Set<String> PARTNER_ROLE_KEYS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            INDEPENDENT_PARTNER_ROLE_KEY,
+            SITE_OWNER_ROLE_KEY,
+            SERVICE_PROVIDER_ROLE_KEY
+    )));
+
+    private static final Map<Integer, String> PARTNER_TYPE_ROLE_KEYS;
+
+    static {
+        Map<Integer, String> roleKeys = new HashMap<>();
+        roleKeys.put(1, INDEPENDENT_PARTNER_ROLE_KEY);
+        roleKeys.put(2, SITE_OWNER_ROLE_KEY);
+        roleKeys.put(3, SERVICE_PROVIDER_ROLE_KEY);
+        PARTNER_TYPE_ROLE_KEYS = Collections.unmodifiableMap(roleKeys);
+    }
+
+    private final SiteClient siteClient;
+    private final ISysRoleService roleService;
+    private final ISysUserRoleService userRoleService;
+
+    public PartnerTypeRoleService(SiteClient siteClient,
+                                  ISysRoleService roleService,
+                                  ISysUserRoleService userRoleService) {
+        this.siteClient = siteClient;
+        this.roleService = roleService;
+        this.userRoleService = userRoleService;
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public void synchronize(SysUser user) {
+        if (!isPartnerAccount(user)) {
+            return;
+        }
+
+        R<Partner> partnerResult = siteClient.getPartnerR(user.getObjectId());
+        Partner partner = partnerResult == null ? null : partnerResult.getData();
+        if (partner == null) {
+            throw new ServiceException("当前账号关联的合作商不存在");
+        }
+
+        String targetRoleKey = PARTNER_TYPE_ROLE_KEYS.get(partner.getPartnerType());
+        if (targetRoleKey == null) {
+            throw new ServiceException("当前合作商类型无效");
+        }
+
+        List<SysRole> builtInRoles = roleService.list(Wrappers.lambdaQuery(SysRole.class)
+                .in(SysRole::getRoleKey, PARTNER_ROLE_KEYS)
+                .eq(SysRole::getDelFlag, "0")
+                .eq(SysRole::getStatus, "0"));
+        Map<String, SysRole> roleByKey = builtInRoles.stream()
+                .collect(Collectors.toMap(SysRole::getRoleKey, role -> role, (first, ignored) -> first));
+        SysRole targetRole = roleByKey.get(targetRoleKey);
+        if (targetRole == null) {
+            throw new ServiceException("系统角色【" + targetRoleKey + "】不存在或已停用,请先配置角色");
+        }
+
+        Set<Long> builtInRoleIds = builtInRoles.stream()
+                .map(SysRole::getRoleId)
+                .collect(Collectors.toSet());
+        List<SysUserRole> existingRoles = userRoleService.list(Wrappers.lambdaQuery(SysUserRole.class)
+                .eq(SysUserRole::getUserId, user.getUserId())
+                .in(SysUserRole::getRoleId, builtInRoleIds));
+
+        boolean hasTargetRole = false;
+        for (SysUserRole existingRole : existingRoles) {
+            Long existingRoleId = existingRole.getRoleId();
+            if (targetRole.getRoleId().equals(existingRoleId)) {
+                hasTargetRole = true;
+            } else if (builtInRoleIds.contains(existingRoleId)) {
+                userRoleService.remove(Wrappers.lambdaQuery(SysUserRole.class)
+                        .eq(SysUserRole::getUserId, user.getUserId())
+                        .eq(SysUserRole::getRoleId, existingRoleId));
+            }
+        }
+
+        if (!hasTargetRole) {
+            SysUserRole userRole = new SysUserRole();
+            userRole.setUserId(user.getUserId());
+            userRole.setRoleId(targetRole.getRoleId());
+            userRoleService.save(userRole);
+        }
+    }
+
+    private boolean isPartnerAccount(SysUser user) {
+        return user != null
+                && user.getUserId() != null
+                && Integer.valueOf(2).equals(user.getRoleType())
+                && user.getObjectId() != null;
+    }
+}
diff --git a/ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java b/ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java
new file mode 100644
index 0000000..487f11c
--- /dev/null
+++ b/ruoyi-modules/ruoyi-system/src/test/java/com/ruoyi/system/service/PartnerTypeRoleServiceTest.java
@@ -0,0 +1,140 @@
+package com.ruoyi.system.service;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.ruoyi.chargingPile.api.feignClient.SiteClient;
+import com.ruoyi.chargingPile.api.model.Partner;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.core.exception.ServiceException;
+import com.ruoyi.system.api.domain.SysRole;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.domain.SysUserRole;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.mockito.ArgumentCaptor;
+
+import java.util.Arrays;
+import java.util.Collections;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class PartnerTypeRoleServiceTest {
+
+    @ParameterizedTest
+    @CsvSource({
+            "1, independent_partner, 11",
+            "2, site_owner, 12",
+            "3, service_provider, 13"
+    })
+    void addsRoleMatchingPartnerType(Integer partnerType, String expectedRoleKey, Long expectedRoleId) {
+        Fixture fixture = new Fixture(partnerType);
+        fixture.stubBuiltInRoles();
+        when(fixture.userRoleService.list(any(Wrapper.class))).thenReturn(Collections.emptyList());
+
+        fixture.service.synchronize(fixture.user);
+
+        ArgumentCaptor<SysUserRole> captor = ArgumentCaptor.forClass(SysUserRole.class);
+        verify(fixture.userRoleService).save(captor.capture());
+        assertEquals(expectedRoleId, captor.getValue().getRoleId());
+        assertEquals(expectedRoleKey, fixture.roleKey(expectedRoleId));
+    }
+
+    @Test
+    void replacesStaleBuiltInRoleButKeepsUnrelatedCustomRole() {
+        Fixture fixture = new Fixture(2);
+        fixture.stubBuiltInRoles();
+        SysUserRole oldTypeRole = userRole(100L, 11L);
+        SysUserRole customRole = userRole(100L, 99L);
+        when(fixture.userRoleService.list(any(Wrapper.class)))
+                .thenReturn(Arrays.asList(oldTypeRole, customRole));
+
+        fixture.service.synchronize(fixture.user);
+
+        verify(fixture.userRoleService).remove(any(Wrapper.class));
+        ArgumentCaptor<SysUserRole> captor = ArgumentCaptor.forClass(SysUserRole.class);
+        verify(fixture.userRoleService).save(captor.capture());
+        assertEquals(Long.valueOf(12L), captor.getValue().getRoleId());
+    }
+
+    @Test
+    void doesNothingForPlatformUser() {
+        Fixture fixture = new Fixture(1);
+        fixture.user.setRoleType(1);
+
+        fixture.service.synchronize(fixture.user);
+
+        verify(fixture.siteClient, never()).getPartnerR(any());
+        verify(fixture.roleService, never()).list(any(Wrapper.class));
+        verify(fixture.userRoleService, never()).save(any(SysUserRole.class));
+    }
+
+    @Test
+    void failsClearlyWhenConfiguredRoleIsMissing() {
+        Fixture fixture = new Fixture(3);
+        when(fixture.roleService.list(any(Wrapper.class)))
+                .thenReturn(Arrays.asList(role(11L, "independent_partner"), role(12L, "site_owner")));
+
+        ServiceException exception = assertThrows(ServiceException.class,
+                () -> fixture.service.synchronize(fixture.user));
+
+        assertEquals("系统角色【service_provider】不存在或已停用,请先配置角色", exception.getMessage());
+        verify(fixture.userRoleService, never()).save(any(SysUserRole.class));
+    }
+
+    private static SysRole role(Long roleId, String roleKey) {
+        SysRole role = new SysRole();
+        role.setRoleId(roleId);
+        role.setRoleKey(roleKey);
+        return role;
+    }
+
+    private static SysUserRole userRole(Long userId, Long roleId) {
+        SysUserRole userRole = new SysUserRole();
+        userRole.setUserId(userId);
+        userRole.setRoleId(roleId);
+        return userRole;
+    }
+
+    private static class Fixture {
+        private final SiteClient siteClient = mock(SiteClient.class);
+        private final ISysRoleService roleService = mock(ISysRoleService.class);
+        private final ISysUserRoleService userRoleService = mock(ISysUserRoleService.class);
+        private final PartnerTypeRoleService service =
+                new PartnerTypeRoleService(siteClient, roleService, userRoleService);
+        private final SysUser user = new SysUser();
+
+        private Fixture(Integer partnerType) {
+            user.setUserId(100L);
+            user.setRoleType(2);
+            user.setObjectId(715);
+            Partner partner = new Partner();
+            partner.setId(715);
+            partner.setPartnerType(partnerType);
+            when(siteClient.getPartnerR(715)).thenReturn(R.ok(partner));
+        }
+
+        private void stubBuiltInRoles() {
+            when(roleService.list(any(Wrapper.class))).thenReturn(Arrays.asList(
+                    role(11L, "independent_partner"),
+                    role(12L, "site_owner"),
+                    role(13L, "service_provider")
+            ));
+        }
+
+        private String roleKey(Long roleId) {
+            if (Long.valueOf(11L).equals(roleId)) {
+                return "independent_partner";
+            }
+            if (Long.valueOf(12L).equals(roleId)) {
+                return "site_owner";
+            }
+            return "service_provider";
+        }
+    }
+}
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java
index 435801d..6119b8c 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/java/com/ruoyi/chargingPile/mapper/SiteMapper.java
@@ -32,6 +32,12 @@
 	List<GetSiteListDTO> getSiteList(PageInfo<GetSiteListDTO> pageInfo, @Param("item") GetSiteList siteList, @Param("ids") Set<Integer> ids);
 
 	/**
+	 * 获取下拉框使用的轻量站点列表。
+	 */
+	List<GetSiteListDTO> getSiteOptions(@Param("ids") Set<Integer> ids,
+	                                    @Param("partnerId") Integer partnerId);
+
+	/**
 	 * 获取站点分页列表
 	 * @return
 	 */
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 e53fd32..4950352 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
@@ -369,36 +369,24 @@
 
 	@Override
 	public PageInfo<GetSiteListDTO> getSiteList1(Long userId) {
-		GetSiteList siteList = new GetSiteList();
-		siteList.setPageCurr(1);
-		siteList.setPageSize(9999);
 		Set<Integer> ids = null;
-		//校验合作商权限
 		SysUser sysUser = sysUserClient.getSysUser(userId).getData();
 		Integer roleType = sysUser.getRoleType();
-		Integer objectId = sysUser.getObjectId();
-		R<Integer> admin = sysUserClient.isAdmin(tokenService.getLoginUser().getUserid());
+		Integer partnerId = null;
 
-		//合作商
 		if(roleType == 2){
-			ids = partnerService.authSite(objectId, SiteMenu.SITE_LIST);
+			partnerId = sysUser.getObjectId();
 		}else{
-			//非管理员需要根据角色和用户配置查询允许的站点数据
+			R<Integer> admin = sysUserClient.isAdmin(userId);
 			if(admin.getData()!=1){
 				ids = getAssignedSiteIds(sysUser.getUserId());
+				if (ids.isEmpty()) {
+					ids.add(-1);
+				}
 			}
 		}
-		PageInfo<GetSiteListDTO> pageInfo = new PageInfo<>(siteList.getPageCurr(), siteList.getPageSize());
-		List<GetSiteListDTO> list = this.baseMapper.getSiteList(pageInfo, siteList, ids);
-		for (GetSiteListDTO getSiteListDTO : list) {
-			Integer siteId = getSiteListDTO.getId();
-			getSiteListDTO.setAuthAccountingStrategy(roleType == 1 ? true : partnerService.authMenu(objectId, siteId, SiteMenu.SITE_ACCOUNTING_STRATEGY));
-			//平台账号才有此权限
-			getSiteListDTO.setAuthPartner(roleType == 1 ? true : false);
-			getSiteListDTO.setAuthUpdate(roleType == 1 ? true : partnerService.authMenu(objectId, siteId, SiteMenu.SITE_UPDATE));
-			getSiteListDTO.setAuthDelete(roleType == 1 ? true : partnerService.authMenu(objectId, siteId, SiteMenu.SITE_DELETE));
-		}
-		return pageInfo.setRecords(list);
+		PageInfo<GetSiteListDTO> pageInfo = new PageInfo<>(1, 9999);
+		return pageInfo.setRecords(this.baseMapper.getSiteOptions(ids, partnerId));
 	}
 
 	private Set<Integer> getAssignedSiteIds(Long userId) {
diff --git a/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml b/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
index ae5fc07..28e17bc 100644
--- a/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
+++ b/ruoyi-service/ruoyi-chargingPile/src/main/resources/mapper/chargingPile/SiteMapper.xml
@@ -128,6 +128,33 @@
 		order by a.sort desc, a.create_time desc
 	</select>
 
+	<select id="getSiteOptions" resultType="com.ruoyi.chargingPile.api.dto.GetSiteListDTO">
+		select a.id,
+		       a.code,
+		       a.name,
+		       a.partner_id as partnerId,
+		       b.partner_type as partnerType,
+		       b.name as partnerName,
+		       a.venue_partner_id as venuePartnerId,
+		       vp.name as venuePartnerName,
+		       a.site_type as siteType,
+		       a.status
+		from t_site a
+		left join t_partner b on a.partner_id = b.id
+		left join t_partner vp on a.venue_partner_id = vp.id
+		where a.del_flag = 0
+		<if test="null != partnerId">
+			and (a.partner_id = #{partnerId} or a.venue_partner_id = #{partnerId})
+		</if>
+		<if test="null == partnerId and null != ids and ids.size() > 0">
+			and a.id in
+			<foreach collection="ids" item="siteId" open="(" separator="," close=")">
+				#{siteId}
+			</foreach>
+		</if>
+		order by a.sort desc, a.create_time desc
+	</select>
+
 	<select id="pageList" resultType="com.ruoyi.chargingPile.api.vo.SiteVO">
 		select
 		ts.id, ts.partner_id, ts.code, ts.`name`, ts.site_type, ts.business_category, ts.member_discount_flag as memberDiscountFlag, ts.status, ts.construction_site, ts.img_url,
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
new file mode 100644
index 0000000..4c4ef7f
--- /dev/null
+++ b/ruoyi-service/ruoyi-chargingPile/src/test/java/com/ruoyi/chargingPile/service/impl/SiteServiceImplTest.java
@@ -0,0 +1,49 @@
+package com.ruoyi.chargingPile.service.impl;
+
+import com.ruoyi.chargingPile.api.dto.GetSiteListDTO;
+import com.ruoyi.chargingPile.mapper.SiteMapper;
+import com.ruoyi.chargingPile.service.IPartnerService;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.system.api.domain.SysUser;
+import com.ruoyi.system.api.feignClient.SysUserClient;
+import org.junit.jupiter.api.Test;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.util.Collections;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class SiteServiceImplTest {
+
+    @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);
+
+        SysUser sysUser = new SysUser();
+        sysUser.setUserId(100L);
+        sysUser.setRoleType(2);
+        sysUser.setObjectId(715);
+
+        when(sysUserClient.getSysUser(100L)).thenReturn(R.ok(sysUser));
+        when(mapper.getSiteOptions(isNull(), eq(715)))
+                .thenReturn(Collections.<GetSiteListDTO>emptyList());
+
+        service.getSiteList1(100L);
+
+        verify(mapper).getSiteOptions(isNull(), eq(715));
+        verify(partnerService, never()).authSite(any(), any());
+        verify(sysUserClient, never()).isAdmin(any());
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
index a6d8839..54ead5a 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/mapper/TChargingOrderMapper.java
@@ -279,6 +279,6 @@
 	 * @param startTime
 	 * @param endTime
 	 */
-	List<Map<String, Object>> getAccountSettlementStatement(@Param("startTime") String startTime, @Param("endTime") String endTime,
-															@Param("rechargePaymentType") Integer rechargePaymentType);
+	List<Map<String, Object>> getAccountSettlementStatement(@Param("startTime") String startTime,
+															@Param("endTime") String endTime);
 }
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
index 58221c3..c06525b 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/service/TChargingOrderService.java
@@ -447,7 +447,7 @@
 	 * @param endTime
 	 * @return
 	 */
-	List<Map<String, Object>> getAccountSettlementStatement(String startTime, String endTime, Integer rechargePaymentType);
+	List<Map<String, Object>> getAccountSettlementStatement(String startTime, String endTime);
 
     /**
      * 手动处理退款数据
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 d611237..df83cbc 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
@@ -7206,8 +7206,8 @@
 	 * @return
 	 */
 	@Override
-	public List<Map<String, Object>> getAccountSettlementStatement(String startTime, String endTime, Integer rechargePaymentType) {
-		return this.baseMapper.getAccountSettlementStatement(startTime, endTime, rechargePaymentType);
+	public List<Map<String, Object>> getAccountSettlementStatement(String startTime, String endTime) {
+		return this.baseMapper.getAccountSettlementStatement(startTime, endTime);
 	}
 
 
diff --git a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
index 5077b98..4e96a5f 100644
--- a/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
+++ b/ruoyi-service/ruoyi-order/src/main/java/com/ruoyi/order/util/task/TaskUtil.java
@@ -279,7 +279,7 @@
 
     @Scheduled(fixedRate = 1000 * 60)
     public void taskOneMinutes() {
-        if(null != port && port == 5400){
+        if (isScheduledTaskPort(port)) {
             List<TChargingOrder> upStatusChargingOrder = chargingOrderService.findUpStatusChargingOrder();
             log.info("定时任务执行,查询需要手动修改状态的订单数量:{}", upStatusChargingOrder.size());
             for (TChargingOrder order : upStatusChargingOrder) {
@@ -297,7 +297,7 @@
     //十分钟循环执行的定时任务
     @Scheduled(fixedRate = 1000 * 60 * 10)
     public void taskTenMinutes() {
-        if(null != port && port == 5400){
+        if (isScheduledTaskPort(port)) {
             List<TChargingOrder> failedStartupOrder = chargingOrderService.findFailedStartupOrder();
             log.info("定时任务执行,查询到启动失败的订单数量:{}", failedStartupOrder.size());
             for (TChargingOrder order : failedStartupOrder) {
@@ -424,7 +424,7 @@
     //定时任务添加结算账单汇总数据
     @Scheduled(cron = "0 30 0 * * ?")
     public void taskMonth() {
-        if(null != port && port == 5400){
+        if (isScheduledTaskPort(port)) {
             //推送三方引流平台数据
             pushOrderDrainagePlatform();
             //添加充电算账单汇总数据
@@ -437,7 +437,7 @@
     // 每个月最后一天23点执行的定时任务
     @Scheduled(cron = "0 0 23 * * ?")
     public void taskLastDay() {
-        if(null != port && port == 5400){
+        if (isScheduledTaskPort(port)) {
             try {
                 int dayOfMonth = LocalDate.now().getDayOfMonth();
                 int lastDayOfMonth = LocalDate.now().with(TemporalAdjusters.lastDayOfMonth()).getDayOfMonth();
@@ -460,6 +460,10 @@
     public void onApplicationEvent(WebServerInitializedEvent event) {
         port = event.getWebServer().getPort();
         System.out.println("端口号:" + port);
+    }
+
+    static boolean isScheduledTaskPort(Integer serverPort) {
+        return Integer.valueOf(5400).equals(serverPort) || Integer.valueOf(9400).equals(serverPort);
     }
 
 
@@ -1161,7 +1165,7 @@
                     accountSettlementStatement.setCode("JSD"+string+randomDigits);
 
                     //根据业务订单查询汇总数据
-                    List<Map<String, Object>> mapList = chargingOrderService.getAccountSettlementStatement(startTime, endTime, i);
+                    List<Map<String, Object>> mapList = chargingOrderService.getAccountSettlementStatement(startTime, endTime);
                     BigDecimal rechargeAmount = BigDecimal.ZERO;
                     BigDecimal refundAmount = BigDecimal.ZERO;
                     BigDecimal platformFee = BigDecimal.ZERO;
diff --git a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
index 5209388..bba3673 100644
--- a/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
+++ b/ruoyi-service/ruoyi-order/src/main/resources/mapper/order/TChargingOrderMapper.xml
@@ -2710,33 +2710,47 @@
 
 
     <select id="getAccountSettlementStatement" resultType="map">
-        select
-        aa.site_id as siteId,
-        sum(aa.recharge_amount) as rechargeAmount,
-        sum(aa.refund_amount) as refundAmount,
-        sum(aa.serviceAmount) as serviceAmount
-        from (
-            select
-            b.id as orderId,
-            b.site_id,
-            ifnull(b.payment_amount, 0) as recharge_amount,
-            ifnull(c.refund_amount, 0) as refund_amount,
-            sum(ifnull(a.period_service_price, 0)) as serviceAmount
-            from t_charging_order_accounting_strategy a
-            left join t_charging_order b on (a.charging_order_id = b.id)
-            left join (
-                select charging_order_id, sum(ifnull(refund_amount, 0)) as refund_amount
-                from t_charging_order_refund
-                where refund_status = 2
-                group by charging_order_id
-            ) c on b.id = c.charging_order_id
-            where b.`status` = 5
-              and b.del_flag = 0
-              and b.order_source = 1
-              and b.source_name = 'ICBC'
-              and b.start_time between #{startTime} and #{endTime}
-            group by orderId, site_id, recharge_amount, refund_amount
-        ) as aa group by aa.site_id order by aa.site_id
+        SELECT
+            b.site_id AS siteId,
+            SUM(CASE
+                    WHEN sr.charging_order_id IS NOT NULL THEN sr.paid_amount
+                    ELSE IFNULL(b.payment_amount, 0)
+                END) AS rechargeAmount,
+            SUM(IFNULL(r.refund_amount, 0)) AS refundAmount,
+            SUM(CASE
+                    WHEN sr.charging_order_id IS NOT NULL THEN sr.paid_service_amount
+                    ELSE IFNULL(a.service_amount, 0)
+                END) AS serviceAmount
+        FROM t_charging_order b
+        LEFT JOIN (
+            SELECT charging_order_id,
+                   SUM(IFNULL(period_service_price, 0)) AS service_amount
+            FROM t_charging_order_accounting_strategy
+            GROUP BY charging_order_id
+        ) a ON a.charging_order_id = b.id
+        LEFT JOIN (
+            SELECT charging_order_id,
+                   SUM(IFNULL(channel_pay_amount, 0)) AS paid_amount,
+                   SUM(IFNULL(channel_service_amount, 0)) AS paid_service_amount
+            FROM t_charging_order_share_record
+            WHERE pay_channel = 'ICBC'
+              AND del_flag = 0
+            GROUP BY charging_order_id
+        ) sr ON sr.charging_order_id = b.id
+        LEFT JOIN (
+            SELECT charging_order_id,
+                   SUM(IFNULL(refund_amount, 0)) AS refund_amount
+            FROM t_charging_order_refund
+            WHERE refund_status = 2
+            GROUP BY charging_order_id
+        ) r ON r.charging_order_id = b.id
+        WHERE b.status = 5
+          AND b.del_flag = 0
+          AND b.order_source = 1
+          AND b.source_name = 'ICBC'
+          AND b.start_time BETWEEN #{startTime} AND #{endTime}
+        GROUP BY b.site_id
+        ORDER BY b.site_id
     </select>
 
     <sql id="enterpriseChargingBaseWhere">
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/AccountSettlementStatementMapperTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/AccountSettlementStatementMapperTest.java
new file mode 100644
index 0000000..aeb8bf6
--- /dev/null
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/mapper/AccountSettlementStatementMapperTest.java
@@ -0,0 +1,45 @@
+package com.ruoyi.order.mapper;
+
+import org.junit.jupiter.api.Test;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.nio.charset.StandardCharsets;
+import java.util.stream.Collectors;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class AccountSettlementStatementMapperTest {
+
+    @Test
+    void usesIcbcPaidAmountsAndKeepsLegacyFieldsForCompatibility() throws IOException {
+        String statementMapper = resource("mapper/order/TAccountSettlementStatementMapper.xml");
+        assertTrue(statementMapper.contains("AND account_type = 3"));
+        assertTrue(statementMapper.contains("SUM(platform_income)"));
+        assertTrue(statementMapper.contains("SUM(partner_income)"));
+        assertTrue(statementMapper.contains("platform_fee"));
+        assertTrue(statementMapper.contains("tripartite_commission"));
+        assertTrue(statementMapper.contains("recorded_amount"));
+
+        String orderMapper = resource("mapper/order/TChargingOrderMapper.xml");
+        String settlementQuery = orderMapper.substring(
+                orderMapper.indexOf("<select id=\"getAccountSettlementStatement\""),
+                orderMapper.indexOf("<sql id=\"enterpriseChargingBaseWhere\""));
+        assertTrue(settlementQuery.contains("b.order_source = 1"));
+        assertTrue(settlementQuery.contains("b.source_name = 'ICBC'"));
+        assertTrue(settlementQuery.contains("pay_channel = 'ICBC'"));
+        assertTrue(settlementQuery.contains("channel_pay_amount"));
+        assertTrue(settlementQuery.contains("channel_service_amount"));
+    }
+
+    private String resource(String path) throws IOException {
+        InputStream input = getClass().getClassLoader().getResourceAsStream(path);
+        assertNotNull(input);
+        try (BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8))) {
+            return reader.lines().collect(Collectors.joining("\n"));
+        }
+    }
+}
diff --git a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/util/task/AccountSettlementIncomeTest.java b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/util/task/AccountSettlementIncomeTest.java
index 24424a4..fa5bc01 100644
--- a/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/util/task/AccountSettlementIncomeTest.java
+++ b/ruoyi-service/ruoyi-order/src/test/java/com/ruoyi/order/util/task/AccountSettlementIncomeTest.java
@@ -9,6 +9,14 @@
 class AccountSettlementIncomeTest {
 
     @Test
+    void enablesScheduledTasksOnLegacyAndOrderPorts() {
+        assertEquals(true, TaskUtil.isScheduledTaskPort(5400));
+        assertEquals(true, TaskUtil.isScheduledTaskPort(9400));
+        assertEquals(false, TaskUtil.isScheduledTaskPort(9500));
+        assertEquals(false, TaskUtil.isScheduledTaskPort(null));
+    }
+
+    @Test
     void calculatesPartnerAndPlatformIncomeForMiniProgramOrder() {
         BigDecimal partnerIncome = TaskUtil.calculateAccountPartnerIncome(
                 new BigDecimal("10.00"), new BigDecimal("30"), false);
diff --git a/sql/20260728_account_settlement_v2.sql b/sql/20260728_account_settlement_v2.sql
index bfa7cb7..7c874d4 100644
--- a/sql/20260728_account_settlement_v2.sql
+++ b/sql/20260728_account_settlement_v2.sql
@@ -1,4 +1,3 @@
 ALTER TABLE `t_account_settlement_statement`
     ADD COLUMN `platform_income` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '平台收入' AFTER `recorded_amount`,
     ADD COLUMN `partner_income` decimal(10, 2) NOT NULL DEFAULT 0.00 COMMENT '合作商收入' AFTER `platform_income`;
-

--
Gitblit v1.7.1