package com.ruoyi.service.impl;
|
|
import com.ruoyi.common.core.domain.AjaxResult;
|
import com.ruoyi.domain.ModuleSearchResult;
|
import com.ruoyi.domain.PeopleSea;
|
import com.ruoyi.service.ModuleSearchable;
|
import lombok.extern.slf4j.Slf4j;
|
import org.apache.commons.lang3.StringUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.scheduling.annotation.Async;
|
import org.springframework.stereotype.Service;
|
import org.springframework.util.CollectionUtils;
|
|
import java.util.*;
|
import java.util.concurrent.CompletableFuture;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
@Service
|
@Slf4j
|
public class InterfaceBasedSearchRouter {
|
|
private final Map<String, ModuleSearchable> moduleSearchMap;
|
|
// 模块分割符
|
private static final String MODULE_SEPARATOR = ",";
|
|
// 全模块标识
|
private static final String ALL_MODULES_FLAG = "all";
|
|
/**
|
* 自动收集所有实现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
|
public CompletableFuture<ModuleSearchResult> searchModuleAsync(String moduleCode, ModuleSearchable service,
|
String companion, Date startTime, Date endTime,
|
String hasAttachment) {
|
long start = System.currentTimeMillis();
|
try {
|
// 调用搜索方法,返回 List<?>
|
List<?> data = service.search(companion, startTime, endTime, hasAttachment);
|
long searchTime = System.currentTimeMillis() - start;
|
|
int count = 0;
|
if (data != null) {
|
count = data.size();
|
}
|
|
// 获取模块名称
|
String moduleName = getModuleName(moduleCode);
|
|
ModuleSearchResult result = ModuleSearchResult.success(
|
moduleCode, moduleName, data, count, searchTime
|
);
|
return CompletableFuture.completedFuture(result);
|
} catch (Exception e) {
|
log.error("模块[{}]搜索失败: {}", moduleCode, e.getMessage(), e);
|
String errorMessage = e.getMessage();
|
if (e.getCause() != null) {
|
errorMessage += " (" + e.getCause().getMessage() + ")";
|
}
|
ModuleSearchResult result = ModuleSearchResult.error(moduleCode, errorMessage);
|
return CompletableFuture.completedFuture(result);
|
}
|
}
|
|
/**
|
* 通用的路由搜索请求
|
*/
|
public AjaxResult routeSearch(PeopleSea peopleS, Integer pageNum, Integer pageSize) {
|
String moduleCode = null;
|
|
// 安全处理 String[] 类型的 modules
|
if (peopleS != null && peopleS.getModules() != null && peopleS.getModules().length != 0) {
|
String[] modulesArray = peopleS.getModules();
|
|
// 过滤掉空字符串和空白字符
|
List<String> validModules = Arrays.stream(modulesArray)
|
.filter(StringUtils::isNotBlank)
|
.map(String::trim)
|
.collect(Collectors.toList());
|
|
if (!validModules.isEmpty()) {
|
// 用逗号连接有效的模块代码
|
moduleCode = String.join(",", validModules);
|
}
|
}
|
|
log.info("路由搜索: moduleCode={}, peopleS={}, pageNum={}, pageSize={}",
|
moduleCode, peopleS, pageNum, pageSize);
|
|
// 解析模块代码
|
List<String> targetModules = parseModuleCodes(moduleCode);
|
|
if (targetModules.isEmpty()) {
|
// 不选模块的情况
|
return handleNoModuleSelected();
|
}
|
|
// 统一处理:单个模块、多个模块都使用同样的处理逻辑
|
return executeModulesSearch(targetModules, peopleS, pageNum, pageSize);
|
}
|
|
/**
|
* 统一执行模块搜索
|
*/
|
private AjaxResult executeModulesSearch(List<String> moduleCodes, PeopleSea peopleS,
|
Integer pageNum, Integer pageSize) {
|
log.info("执行模块搜索: moduleCodes={}, 模块数量={}", moduleCodes, moduleCodes.size());
|
|
// 验证所有模块是否存在
|
List<String> invalidModules = moduleCodes.stream()
|
.filter(code -> !moduleSearchMap.containsKey(code))
|
.collect(Collectors.toList());
|
|
if (!invalidModules.isEmpty()) {
|
String availableModules = String.join(", ", moduleSearchMap.keySet());
|
String errorMsg = String.format("以下模块不支持: %s。可用模块: [%s]",
|
invalidModules, availableModules);
|
return AjaxResult.error(errorMsg);
|
}
|
|
// 提取参数
|
String companion = extractCompanion(peopleS);
|
Date startTime = extractStartTime(peopleS);
|
Date endTime = extractEndTime(peopleS);
|
String hasAttachment = extractHasAttachment(peopleS);
|
|
// 设置分页默认值
|
if (pageNum == null || pageNum <= 0) {
|
pageNum = 1;
|
}
|
if (pageSize == null || pageSize <= 0) {
|
pageSize = 10;
|
}
|
|
// 并发搜索
|
|
List<CompletableFuture<ModuleSearchResult>> futures = moduleCodes.stream()
|
.map(code -> searchModuleAsync(code, moduleSearchMap.get(code),
|
companion, startTime, endTime, hasAttachment))
|
.collect(Collectors.toList());
|
|
// 等待完成
|
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
|
|
// 合并数据
|
List<Object> allData = new ArrayList<>();
|
for (CompletableFuture<ModuleSearchResult> future : futures) {
|
try {
|
ModuleSearchResult result = future.get();
|
if (result.isSuccess() && result.getData() != null) {
|
allData.addAll(result.getData());
|
}
|
} catch (Exception e) {
|
// 记录错误但继续处理其他模块
|
log.error("获取模块搜索结果失败", e);
|
}
|
}
|
|
// 对数据进行排序(如果需要)
|
sortData(allData);
|
|
// 分页
|
int startIndex = (pageNum - 1) * pageSize;
|
List<Object> paginatedData = allData.stream()
|
.skip(startIndex)
|
.limit(pageSize)
|
.collect(Collectors.toList());
|
|
// 返回结果
|
Map<String, Object> data = new HashMap<>();
|
data.put("list", paginatedData);
|
data.put("total", allData.size());
|
data.put("pageNum", pageNum);
|
data.put("pageSize", pageSize);
|
|
return AjaxResult.success(data);
|
}
|
|
/**
|
* 对数据进行排序
|
* 默认按创建时间降序排列
|
*/
|
private void sortData(List<Object> allData) {
|
if (CollectionUtils.isEmpty(allData)) {
|
return;
|
}
|
|
// 如果数据是 Map 类型,尝试按 createTime 排序
|
if (allData.get(0) instanceof Map) {
|
allData.sort((a, b) -> {
|
Map<String, Object> mapA = (Map<String, Object>) a;
|
Map<String, Object> mapB = (Map<String, Object>) b;
|
|
Object timeA = mapA.get("createTime");
|
Object timeB = mapB.get("createTime");
|
|
if (timeA instanceof Date && timeB instanceof Date) {
|
// 降序排列:最新的在前
|
return ((Date) timeB).compareTo((Date) timeA);
|
} else if (timeA instanceof String && timeB instanceof String) {
|
// 如果时间是字符串,尝试解析
|
try {
|
Date dateA = parseDate((String) timeA);
|
Date dateB = parseDate((String) timeB);
|
if (dateA != null && dateB != null) {
|
return dateB.compareTo(dateA);
|
}
|
} catch (Exception e) {
|
// 解析失败,不排序
|
}
|
}
|
return 0;
|
});
|
}
|
}
|
|
/**
|
* 解析日期字符串
|
*/
|
private Date parseDate(String dateStr) {
|
if (dateStr == null || dateStr.isEmpty()) {
|
return null;
|
}
|
|
try {
|
// 尝试常见的日期格式
|
String[] formats = {
|
"yyyy-MM-dd'T'HH:mm:ss.SSSXXX",
|
"yyyy-MM-dd HH:mm:ss",
|
"yyyy-MM-dd"
|
};
|
|
for (String format : formats) {
|
try {
|
return new java.text.SimpleDateFormat(format).parse(dateStr);
|
} catch (Exception e) {
|
// 继续尝试下一个格式
|
}
|
}
|
} catch (Exception e) {
|
log.warn("无法解析日期字符串: {}", dateStr, e);
|
}
|
|
return null;
|
}
|
|
/**
|
* 获取模块名称
|
*/
|
private String getModuleName(String moduleCode) {
|
ModuleSearchable service = moduleSearchMap.get(moduleCode);
|
return service != null ? service.getModuleName() : "未知模块";
|
}
|
|
/**
|
* 安全的参数提取方法
|
*/
|
private String extractCompanion(PeopleSea peopleS) {
|
if (peopleS == null) {
|
return "";
|
}
|
return peopleS.getPeoples() == null ? "" : peopleS.getPeoples().trim();
|
}
|
|
private Date extractStartTime(PeopleSea peopleS) {
|
if (peopleS == null) {
|
return null;
|
}
|
return peopleS.getStartTime();
|
}
|
|
private Date extractEndTime(PeopleSea peopleS) {
|
if (peopleS == null) {
|
return null;
|
}
|
return peopleS.getEndTime();
|
}
|
|
private String extractHasAttachment(PeopleSea peopleS) {
|
if (peopleS == null) {
|
return "";
|
}
|
return peopleS.getHasAttachment() == null ? "" : peopleS.getHasAttachment().trim();
|
}
|
|
/**
|
* 解析模块代码
|
*/
|
private List<String> parseModuleCodes(String moduleCode) {
|
if (StringUtils.isBlank(moduleCode)) {
|
// 空/空白/null -> 全模块搜索
|
return new ArrayList<>(moduleSearchMap.keySet());
|
}
|
|
String trimmedCode = moduleCode.trim();
|
|
// 处理全模块标识
|
if (ALL_MODULES_FLAG.equalsIgnoreCase(trimmedCode)) {
|
return new ArrayList<>(moduleSearchMap.keySet());
|
}
|
|
// 处理不选模块的情况
|
if ("none".equalsIgnoreCase(trimmedCode) || "null".equalsIgnoreCase(trimmedCode)) {
|
return Collections.emptyList();
|
}
|
|
// 检查是否包含逗号(多个模块)
|
if (trimmedCode.contains(MODULE_SEPARATOR)) {
|
String[] moduleArray = trimmedCode.split(MODULE_SEPARATOR);
|
return Arrays.stream(moduleArray)
|
.map(String::trim)
|
.filter(code -> !code.isEmpty())
|
.collect(Collectors.toList());
|
}
|
|
// 单个模块
|
return Collections.singletonList(trimmedCode);
|
}
|
|
/**
|
* 不选模块的处理逻辑
|
*/
|
private AjaxResult handleNoModuleSelected() {
|
Map<String, Object> result = new LinkedHashMap<>();
|
result.put("list", Collections.emptyList());
|
result.put("total", 0);
|
result.put("pageNum", 1);
|
result.put("pageSize", 10);
|
|
return AjaxResult.success("未选择搜索模块,请选择要搜索的模块", result);
|
}
|
|
/**
|
* 获取可用模块列表
|
*/
|
public AjaxResult getAvailableModules() {
|
Map<String, Object> result = new HashMap<>();
|
|
List<Map<String, Object>> modules = moduleSearchMap.values().stream()
|
.map(service -> {
|
Map<String, Object> moduleInfo = new HashMap<>();
|
moduleInfo.put("moduleCode", service.getModuleCode());
|
moduleInfo.put("moduleName", service.getModuleName());
|
return moduleInfo;
|
})
|
.collect(Collectors.toList());
|
|
result.put("modules", modules);
|
result.put("total", modules.size());
|
|
return AjaxResult.success("获取可用模块成功", result);
|
}
|
|
/**
|
* 验证模块代码是否存在
|
*/
|
public boolean validateModule(String moduleCode) {
|
if (StringUtils.isBlank(moduleCode)) {
|
return false;
|
}
|
|
// 处理多个模块的情况
|
if (moduleCode.contains(MODULE_SEPARATOR)) {
|
String[] moduleArray = moduleCode.split(MODULE_SEPARATOR);
|
for (String code : moduleArray) {
|
String trimmedCode = code.trim();
|
if (!moduleSearchMap.containsKey(trimmedCode) &&
|
!ALL_MODULES_FLAG.equalsIgnoreCase(trimmedCode)) {
|
return false;
|
}
|
}
|
return true;
|
}
|
|
// 单个模块的情况
|
String trimmedCode = moduleCode.trim();
|
return moduleSearchMap.containsKey(trimmedCode) ||
|
ALL_MODULES_FLAG.equalsIgnoreCase(trimmedCode);
|
}
|
}
|