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;
|
}
|
}
|