| | |
| | | |
| | | 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 java.util.Arrays; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | 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; |
| | | |
| | |
| | | public class InterfaceBasedSearchRouter { |
| | | |
| | | private final Map<String, ModuleSearchable> moduleSearchMap; |
| | | |
| | | |
| | | /** |
| | | * 自动收集所有实现ModuleSearchable接口的Bean |
| | |
| | | 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()); |
| | |
| | | } |
| | | |
| | | try { |
| | | // 根据参数数量进行路由 |
| | | if (args.length == 3) { |
| | | return handleFourArgs(searchService, args); |
| | | } else { |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 处理3个参数的情况(companion + happenStartTime + happenEndTime) |
| | | */ |
| | | private AjaxResult handleFourArgs(ModuleSearchable searchService, Object[] args) { |
| | | String companion = null; |
| | | Date happenStartTime = 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 boolean supports(String moduleCode) { |
| | | return moduleSearchMap.containsKey(moduleCode); |
| | | 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; |
| | | } |
| | | |
| | | /** |
| | | * 获取模块服务实例 |
| | | */ |
| | | public ModuleSearchable getModuleService(String moduleCode) { |
| | | return moduleSearchMap.get(moduleCode); |
| | | 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; |
| | | } |
| | | } |