zqy
2 天以前 28cf0afe5cb951bf34a60a1ee0f36d38c592a8c0
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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
package com.ruoyi.service.impl;
 
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.entity.SysMenu;
import com.ruoyi.domain.ModuleSearchResult;
import com.ruoyi.service.ModuleSearchable;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.logging.Log;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
 
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.Collectors;
 
@Service
@Slf4j
public class InterfaceBasedSearchRouter {
 
    private final Map<String, ModuleSearchable> moduleSearchMap;
 
 
    /**
     * 自动收集所有实现ModuleSearchable接口的Bean
     */
    @Autowired
    public InterfaceBasedSearchRouter(List<ModuleSearchable> searchServices) {
        this.moduleSearchMap = searchServices.stream()
            .collect(Collectors.toMap(
                ModuleSearchable::getModuleCode,
                Function.identity(),
                (existing, replacement) -> {
                    log.warn("发现重复的模块编码: {}, 使用先注册的服务", existing.getModuleCode());
                    return existing;
                }
            ));
 
        log.info("已注册搜索模块: {}", moduleSearchMap.keySet());
    }
 
    @Async // 声明此方法异步执行,将使用我们上面配置的Executor
    public CompletableFuture<ModuleSearchResult> searchModuleAsync(String moduleCode, ModuleSearchable service, String companion, Date startTime, Date endTime) {
        // 将原来在lambda表达式中的搜索逻辑移到这里
        long start = System.currentTimeMillis();
        try {
            List<?> data = service.search(companion, startTime, endTime);
            long searchTime = System.currentTimeMillis() - start;
            ModuleSearchResult result = ModuleSearchResult.success(moduleCode, service.getModuleName(), data, data.size(), searchTime);
            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            log.error("模块[{}]搜索失败: {}", moduleCode, e.getMessage());
            ModuleSearchResult result = ModuleSearchResult.error(moduleCode, e.getMessage());
            return CompletableFuture.completedFuture(result);
        }
    }
    /**
     * 通用的路由搜索请求(支持不同参数类型)
     */
    /**
     * 通用的路由搜索请求
     */
    public AjaxResult routeSearch(String moduleCode, Object... args) {
        log.info("路由搜索: moduleCode={}, args={}", moduleCode, Arrays.toString(args));
 
        // 全模块搜索:当moduleCode为空或特定标识时
        if (moduleCode == null || moduleCode.isEmpty() || "all".equalsIgnoreCase(moduleCode)) {
            return searchAllModules(args);
        }
 
        // 单个模块搜索(原有逻辑)
        ModuleSearchable searchService = moduleSearchMap.get(moduleCode);
        if (searchService == null) {
            String availableModules = String.join(", ", moduleSearchMap.keySet());
            return AjaxResult.error("不支持的搜索模块: " + moduleCode + "。可用模块: [" + availableModules + "]");
        }
 
        try {
            if (args.length == 3) {
                return handleFourArgs(searchService, args);
            } else {
                return AjaxResult.error("不支持的参数数量,需要3个参数,实际收到: " + args.length);
            }
        } catch (Exception e) {
            log.error("搜索执行失败: moduleCode={}", moduleCode, e);
            return AjaxResult.error("搜索执行失败: " + e.getMessage());
        }
    }
 
    private AjaxResult handleFourArgs(ModuleSearchable searchService, Object[] args) {
        String companion = null;
        Date happenStartTime = null;
        Date happenEndTime = null;
 
        // 处理companion参数
        if (args[0] instanceof String) {
            companion = (String) args[0];
        } else if (args[0] != null) {
            companion = args[0].toString();
        }
 
        // 处理时间参数
        if (args[1] instanceof Date) {
            happenStartTime = (Date) args[1];
        }
 
        if (args[2] instanceof Date) {
            happenEndTime = (Date) args[2];
        }
 
        // 判断搜索类型
        boolean hasTimeRange = happenStartTime != null && happenEndTime != null;
 
        List<?> result;
        if (hasTimeRange) {
            // 有时间范围:执行时间范围搜索
            log.info("执行时间范围搜索: companion={}, startTime={}, endTime={}",
                companion, happenStartTime, happenEndTime);
            result = searchService.search(companion, happenStartTime, happenEndTime);
        } else {
            // 无时间范围:只按companion搜索
            log.info("执行companion搜索: companion={}, 时间范围为空", companion);
            result = searchService.search(companion, null, null);
        }
 
        return AjaxResult.success("搜索成功", result);
    }
 
    /**
     * 全模块搜索:获取所有模块的数据并分类
     */
    private AjaxResult searchAllModules(Object[] args) {
        log.info("执行全模块搜索,参数数量: {}", args.length);
 
        try {
            Map<String, Object> result = new LinkedHashMap<>();
            int totalCount = 0;
            int successCount = 0;
 
            // 处理搜索参数
            String companion = extractCompanion(args);
            Date happenStartTime = extractStartTime(args);
            Date happenEndTime = extractEndTime(args);
 
            System.out.println("全模块搜索 ------ 同伴: " + companion);
            System.out.println("全模块搜索 ------ 开始时间: " + happenStartTime);
            System.out.println("全模块搜索 ------ 结束时间: " + happenEndTime);
 
            // 并行处理所有模块搜索(提高性能)
            List<CompletableFuture<ModuleSearchResult>> futures = moduleSearchMap.entrySet().stream().map(entry ->
                searchModuleAsync(entry.getKey(),
                    entry.getValue(), companion,
                    happenStartTime, happenEndTime))
                .collect(Collectors.toList());
 
 
            // 等待所有搜索完成
            CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
 
            // 收集结果
            for (CompletableFuture<ModuleSearchResult> future : futures) {
                ModuleSearchResult moduleResult = future.get();
                if (moduleResult.isSuccess()) {
                    // 创建一个可变的HashMap并填入数据
                    Map<String, Object> resultMap = new HashMap<>();
                    resultMap.put("data", moduleResult.getData());
                    resultMap.put("count", moduleResult.getCount());
                    resultMap.put("searchTime", moduleResult.getSearchTime());
 
                    // 将构建好的Map放入最终结果中
                    result.put(moduleResult.getModuleCode(), resultMap);
 
                    successCount++;
                    totalCount += moduleResult.getCount();
                } else {
                    Map<String, Object> errorInfo = new HashMap<>();
                    errorInfo.put("error", moduleResult.getErrorMessage());
                    errorInfo.put("success", false);
                    result.put(moduleResult.getModuleCode(), errorInfo);
                }
            }
 
            // 构建返回结果
            Map<String, Object> finalResult = new LinkedHashMap<>();
            finalResult.put("totalModules", moduleSearchMap.size());
            finalResult.put("successModules", successCount);
            finalResult.put("totalRecords", totalCount);
            finalResult.put("searchTime", new Date());
            finalResult.put("modules", result);
 
            log.info("全模块搜索完成: 成功{}/{}个模块,总计{}条记录",
                successCount, moduleSearchMap.size(), totalCount);
 
            return AjaxResult.success("全模块搜索成功", finalResult);
 
        } catch (Exception e) {
            log.error("全模块搜索执行失败", e);
            return AjaxResult.error("全模块搜索失败: " + e.getMessage());
        }
    }
 
    /**
     * 单个模块搜索包装方法
     */
    private ModuleSearchResult searchSingleModule(String moduleCode, ModuleSearchable service,
                                                  String companion, Date startTime, Date endTime) {
        long start = System.currentTimeMillis();
        try {
            List<?> data = service.search(companion, startTime, endTime);
            long searchTime = System.currentTimeMillis() - start;
 
            return ModuleSearchResult.success(moduleCode, service.getModuleName(),
                data, data.size(), searchTime);
 
        } catch (Exception e) {
            log.error("模块[{}]搜索失败: {}", moduleCode, e.getMessage());
            return ModuleSearchResult.error(moduleCode, e.getMessage());
        }
    }
//
//    /**
//     * 获取所有可搜索的模块信息
//     */
//    public List<SysMenu> getAvailableModules() {
//        return moduleSearchMap.values().stream()
//            .map(service -> SysMenu.builder()
//                .moduleCode(service.getModuleCode())
//                .moduleName(service.getModuleName())
//                .build())
//            .collect(Collectors.toList());
//    }
 
    /**
     * 参数提取辅助方法
     */
    private String extractCompanion(Object[] args) {
        if (args.length > 0 && args[0] instanceof String) {
            return (String) args[0];
        } else if (args.length > 0 && args[0] != null) {
            return args[0].toString();
        }
        return null;
    }
 
    private Date extractStartTime(Object[] args) {
        return (args.length > 1 && args[1] instanceof Date) ? (Date) args[1] : null;
    }
 
    private Date extractEndTime(Object[] args) {
        return (args.length > 2 && args[2] instanceof Date) ? (Date) args[2] : null;
    }
}