1
dd
2025-10-16 978915a43878ae4e9166a54596a2f1ad4433adbc
1
1 files modified
209 ■■■■ changed files
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HomePageStatisticsController.java 209 ●●●● patch | view | raw | blame | history
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/HomePageStatisticsController.java
@@ -4,33 +4,26 @@
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.im.comm.Result;
import com.ruoyi.im.service.UserPolicyService;
import com.ruoyi.im.service.impl.InsurancePositionServiceImpl;
import com.ruoyi.im.util.UserPolicyUtils;
import com.ruoyi.system.domain.InsurancePosition;
import com.ruoyi.system.domain.UserAccount;
import com.ruoyi.system.domain.UserPolicy;
import com.ruoyi.system.domain.dto.SubordinateInformationDto;
import com.ruoyi.system.domain.out.HomePageStatisticsOut;
import com.ruoyi.system.domain.out.UserTeamAndPositionOut;
import com.ruoyi.system.service.UserAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
 * @program: ruoyiim
 * @description:首页统计
 * @create: 2025-10-13 17:21
 **/
@RestController
@RequestMapping("/system/home")
public class HomePageStatisticsController {
@@ -44,57 +37,173 @@
    @Autowired
    UserPolicyService userPolicyService;
    /**
     * 首页统计
     */
    // 使用内存缓存替代Redis
    private final Map<String, CacheEntry<List<String>>> subordinateCache = new ConcurrentHashMap<>();
    private static final long CACHE_EXPIRE_MINUTES = 5; // 缓存5分钟
    // 缓存条目类,包含数据和过期时间
    private static class CacheEntry<T> {
        private final T data;
        private final long expireTime;
        public CacheEntry(T data, long expireTime) {
            this.data = data;
            this.expireTime = expireTime;
        }
        public T getData() {
            return data;
        }
        public boolean isExpired() {
            return System.currentTimeMillis() > expireTime;
        }
    }
    @GetMapping("/getStatistics")
    public AjaxResult getUserTeamAndPosition(@RequestParam(value = "invitationCode",required = false) String invitationCode) {
    public AjaxResult getUserTeamAndPosition(@RequestParam(value = "invitationCode", required = false) String invitationCode) {
        HomePageStatisticsOut statisticsOut = new HomePageStatisticsOut();
        //邀请人账号
        String account = null;
        if(StringUtils.isNotEmpty(invitationCode)){
        // 所有需要统计的下级账号列表
        List<String> allSubordinateAccounts = new ArrayList<>();
        if (StringUtils.isNotEmpty(invitationCode)) {
            UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>()
                    .eq(UserAccount::getInvitationCode, invitationCode)
                    .select(UserAccount::getAccount) // 只查询需要的字段
            );
            if(ObjectUtil.isNotEmpty(userAccount)){
                account = userAccount.getAccount();
            if (ObjectUtil.isNotEmpty(userAccount)) {
                String rootAccount = userAccount.getAccount();
                // 使用内存缓存获取所有下级账号
                allSubordinateAccounts = getCachedSubordinateAccounts(rootAccount);
            }
        }
        // 如果下级账号数量为0,直接返回空结果,避免不必要的查询
        if (allSubordinateAccounts.isEmpty()) {
            return AjaxResult.success(statisticsOut);
        }
        // 今日注册
        LambdaQueryWrapper<UserAccount> todayWrapper = new LambdaQueryWrapper<>();
        todayWrapper.apply("DATE(create_time) = CURDATE()");
        todayWrapper.eq(StringUtils.isNotEmpty(account),UserAccount::getInvitationAccount,account);
        statisticsOut.setTodayRegister(userAccountService.count(todayWrapper));
        // 并行执行统计查询
        try {
            List<String> finalAllSubordinateAccounts = allSubordinateAccounts;
            CompletableFuture<Long> todayRegisterFuture = CompletableFuture.supplyAsync(() -> getTodayRegister(finalAllSubordinateAccounts));
            List<String> finalAllSubordinateAccounts2 = allSubordinateAccounts;
            CompletableFuture<Long> totalRegisterFuture = CompletableFuture.supplyAsync(() -> getTotalRegister(finalAllSubordinateAccounts2));
            List<String> finalAllSubordinateAccounts1 = allSubordinateAccounts;
            CompletableFuture<Long> todayActivateFuture = CompletableFuture.supplyAsync(() -> getTodayActivate(finalAllSubordinateAccounts1));
            List<String> finalAllSubordinateAccounts3 = allSubordinateAccounts;
            CompletableFuture<Long> totalActivateFuture = CompletableFuture.supplyAsync(() -> getTotalActivate(finalAllSubordinateAccounts3));
        // 总注册
        statisticsOut.setTotalRegister(userAccountService.count(new LambdaQueryWrapper<UserAccount>().eq(StringUtils.isNotEmpty(account),UserAccount::getInvitationAccount,account)));
        List<UserAccount> userAccounts = userAccountService.list(new LambdaQueryWrapper<UserAccount>()
                .eq(StringUtils.isNotEmpty(account), UserAccount::getInvitationAccount, account)
        );
        List<String> accounts = userAccounts.stream().map(UserAccount::getAccount).collect(Collectors.toList());
        // 今日激活
        LambdaQueryWrapper<UserPolicy> userPolicyTodayWrapper = new LambdaQueryWrapper<>();
        userPolicyTodayWrapper
                .eq(UserPolicy::getApprovalStatus, 1)
                .in(!CollectionUtils.isEmpty(accounts),UserPolicy::getAccount,accounts)
                .apply("DATE(created_at) = CURDATE()");
        statisticsOut.setTodayActivate(userPolicyService.count(userPolicyTodayWrapper));
        // 总激活
        LambdaQueryWrapper<UserPolicy> userPolicyTotalWrapper = new LambdaQueryWrapper<>();
        userPolicyTotalWrapper
                .in(!CollectionUtils.isEmpty(accounts),UserPolicy::getAccount,accounts)
                .eq(UserPolicy::getApprovalStatus, 1);
        statisticsOut.setTotalActivate(userPolicyService.count(userPolicyTotalWrapper));
            // 等待所有查询完成
            statisticsOut.setTodayRegister(todayRegisterFuture.get());
            statisticsOut.setTotalRegister(totalRegisterFuture.get());
            statisticsOut.setTodayActivate(todayActivateFuture.get());
            statisticsOut.setTotalActivate(totalActivateFuture.get());
        } catch (Exception e) {
            throw new RuntimeException("统计查询失败", e);
        }
        return AjaxResult.success(statisticsOut);
    }
}
    // 分离的查询方法,便于并行执行
    private Long getTodayRegister(List<String> subordinateAccounts) {
        LambdaQueryWrapper<UserAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("DATE(create_time) = CURDATE()")
                .in(UserAccount::getAccount, subordinateAccounts);
        return userAccountService.count(wrapper);
    }
    private Long getTotalRegister(List<String> subordinateAccounts) {
        LambdaQueryWrapper<UserAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(UserAccount::getAccount, subordinateAccounts);
        return userAccountService.count(wrapper);
    }
    private Long getTodayActivate(List<String> subordinateAccounts) {
        LambdaQueryWrapper<UserPolicy> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserPolicy::getApprovalStatus, 1)
                .in(UserPolicy::getAccount, subordinateAccounts)
                .apply("DATE(created_at) = CURDATE()");
        return userPolicyService.count(wrapper);
    }
    private Long getTotalActivate(List<String> subordinateAccounts) {
        LambdaQueryWrapper<UserPolicy> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserPolicy::getApprovalStatus, 1)
                .in(UserPolicy::getAccount, subordinateAccounts);
        return userPolicyService.count(wrapper);
    }
    // 使用内存缓存的下级账号查询
    private List<String> getCachedSubordinateAccounts(String rootAccount) {
        String cacheKey = "subordinate:" + rootAccount;
        // 尝试从缓存获取
        CacheEntry<List<String>> cacheEntry = subordinateCache.get(cacheKey);
        if (cacheEntry != null && !cacheEntry.isExpired()) {
            return new ArrayList<>(cacheEntry.getData()); // 返回副本
        }
        // 缓存不存在或已过期,查询数据库
        List<String> subordinateAccounts = getAllSubordinateAccountsOptimized(rootAccount);
        // 存入缓存
        long expireTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(CACHE_EXPIRE_MINUTES);
        subordinateCache.put(cacheKey, new CacheEntry<>(new ArrayList<>(subordinateAccounts), expireTime));
        // 定期清理过期缓存(简单实现)
        cleanExpiredCache();
        return subordinateAccounts;
    }
    // 清理过期缓存
    private void cleanExpiredCache() {
        // 每100次访问清理一次缓存,避免频繁清理
        if (System.currentTimeMillis() % 100 == 0) {
            subordinateCache.entrySet().removeIf(entry -> entry.getValue().isExpired());
        }
    }
    // 优化版本:使用Queue进行广度优先搜索,避免递归深度问题
    public List<String> getAllSubordinateAccountsOptimized(String rootAccount) {
        if (StringUtils.isEmpty(rootAccount)) {
            return new ArrayList<>();
        }
        List<String> result = new ArrayList<>();
        Queue<String> queue = new LinkedList<>();
        queue.offer(rootAccount);
        // 一次性查询所有用户关系,减少数据库访问
        List<UserAccount> allUsers = userAccountService.list(
                new LambdaQueryWrapper<UserAccount>()
                        .select(UserAccount::getAccount, UserAccount::getInvitationAccount)
        );
        // 构建邀请关系映射
        Map<String, List<String>> invitationMap = allUsers.stream()
                .filter(user -> StringUtils.isNotEmpty(user.getInvitationAccount()))
                .collect(Collectors.groupingBy(
                        UserAccount::getInvitationAccount,
                        Collectors.mapping(UserAccount::getAccount, Collectors.toList())
                ));
        // 广度优先遍历
        while (!queue.isEmpty()) {
            String currentAccount = queue.poll();
            List<String> subordinates = invitationMap.get(currentAccount);
            if (subordinates != null) {
                for (String subordinate : subordinates) {
                    result.add(subordinate);
                    queue.offer(subordinate);
                }
            }
        }
        return result;
    }
}