1
dd
2025-11-29 9437600612eb0243a3371ff1e4fa3689cce8c83a
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
package com.ruoyi.web.controller.system;
 
import cn.hutool.core.util.ObjectUtil;
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.service.UserPolicyService;
import com.ruoyi.im.service.impl.InsurancePositionServiceImpl;
import com.ruoyi.system.domain.UserAccount;
import com.ruoyi.system.domain.UserPolicy;
import com.ruoyi.system.domain.out.HomePageStatisticsOut;
import com.ruoyi.system.service.UserAccountService;
import org.springframework.beans.factory.annotation.Autowired;
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.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
 
@RestController
@RequestMapping("/system/home")
public class HomePageStatisticsController {
 
    @Autowired
    UserAccountService userAccountService;
 
    @Autowired
    InsurancePositionServiceImpl insurancePositionService;
 
    @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) {
        HomePageStatisticsOut statisticsOut = new HomePageStatisticsOut();
 
        // 标志是否统计所有用户
        boolean isStatisticsAll = StringUtils.isEmpty(invitationCode);
 
        // 所有需要统计的下级账号列表
        List<String> allSubordinateAccounts = new ArrayList<>();
 
        if (!isStatisticsAll) {
            // 如果指定了邀请码,只统计该邀请码对应的用户及其下级
            UserAccount userAccount = userAccountService.getOne(new LambdaQueryWrapper<UserAccount>()
                    .eq(UserAccount::getInvitationCode, invitationCode)
                    .select(UserAccount::getAccount) // 只查询需要的字段
            );
            if (ObjectUtil.isNotEmpty(userAccount)) {
                String rootAccount = userAccount.getAccount();
                // 使用内存缓存获取所有下级账号
                allSubordinateAccounts = getCachedSubordinateAccounts(rootAccount);
            }
        }
        // 如果 invitationCode 为 null,isStatisticsAll 为 true,将不设置 allSubordinateAccounts
        // 在后续查询中,如果 allSubordinateAccounts 为空且 isStatisticsAll 为 true,则查询所有用户
 
        // 并行执行统计查询
        try {
            List<String> finalAllSubordinateAccounts = allSubordinateAccounts;
            CompletableFuture<Long> todayRegisterFuture = CompletableFuture.supplyAsync(() ->
                    getTodayRegister(finalAllSubordinateAccounts, isStatisticsAll));
            List<String> finalAllSubordinateAccounts1 = allSubordinateAccounts;
            CompletableFuture<Long> totalRegisterFuture = CompletableFuture.supplyAsync(() ->
                    getTotalRegister(finalAllSubordinateAccounts1, isStatisticsAll));
            List<String> finalAllSubordinateAccounts2 = allSubordinateAccounts;
            CompletableFuture<Long> todayActivateFuture = CompletableFuture.supplyAsync(() ->
                    getTodayActivate(finalAllSubordinateAccounts2, isStatisticsAll));
            List<String> finalAllSubordinateAccounts3 = allSubordinateAccounts;
            CompletableFuture<Long> totalActivateFuture = CompletableFuture.supplyAsync(() ->
                    getTotalActivate(finalAllSubordinateAccounts3, isStatisticsAll));
 
            // 等待所有查询完成
            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, boolean isStatisticsAll) {
        LambdaQueryWrapper<UserAccount> wrapper = new LambdaQueryWrapper<>();
        wrapper.apply("DATE(create_time) = CURDATE()");
 
        // 如果指定了邀请码,按账号列表过滤;否则查询所有
        if (!isStatisticsAll && !subordinateAccounts.isEmpty()) {
            wrapper.in(UserAccount::getAccount, subordinateAccounts);
        }
        // 如果 isStatisticsAll 为 true,不添加账号过滤条件,查询所有用户
 
        return userAccountService.count(wrapper);
    }
 
    private Long getTotalRegister(List<String> subordinateAccounts, boolean isStatisticsAll) {
        LambdaQueryWrapper<UserAccount> wrapper = new LambdaQueryWrapper<>();
 
        // 如果指定了邀请码,按账号列表过滤;否则查询所有
        if (!isStatisticsAll && !subordinateAccounts.isEmpty()) {
            wrapper.in(UserAccount::getAccount, subordinateAccounts);
        }
        // 如果 isStatisticsAll 为 true,不添加账号过滤条件,查询所有用户
 
        return userAccountService.count(wrapper);
    }
 
    private Long getTodayActivate(List<String> subordinateAccounts, boolean isStatisticsAll) {
        LambdaQueryWrapper<UserPolicy> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserPolicy::getApprovalStatus, 1)
                .apply("DATE(updated_at) = CURDATE()");
 
        // 如果指定了邀请码,按账号列表过滤;否则查询所有
        if (!isStatisticsAll && !subordinateAccounts.isEmpty()) {
            wrapper.in(UserPolicy::getAccount, subordinateAccounts);
        }
        // 如果 isStatisticsAll 为 true,不添加账号过滤条件,查询所有用户
 
        return userPolicyService.count(wrapper);
    }
 
    private Long getTotalActivate(List<String> subordinateAccounts, boolean isStatisticsAll) {
        LambdaQueryWrapper<UserPolicy> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(UserPolicy::getApprovalStatus, 1);
 
        // 如果指定了邀请码,按账号列表过滤;否则查询所有
        if (!isStatisticsAll && !subordinateAccounts.isEmpty()) {
            wrapper.in(UserPolicy::getAccount, subordinateAccounts);
        }
        // 如果 isStatisticsAll 为 true,不添加账号过滤条件,查询所有用户
 
        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;
    }
}