zqy
3 天以前 ee02f5032743f3c0bd217cb6cf8ba03ee663c629
zhang-content/src/main/java/com/ruoyi/service/impl/InterfaceBasedSearchRouter.java
@@ -1,20 +1,18 @@
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.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 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;
@@ -50,54 +48,61 @@
    @Async
    public CompletableFuture<ModuleSearchResult> searchModuleAsync(String moduleCode, ModuleSearchable service,
                                                                   String companion, Date startTime, Date endTime,String hasAttachment) {
                                                                   String companion, Date startTime, Date endTime,
                                                                   String hasAttachment, Integer pageNum, Integer pageSize) {
        long start = System.currentTimeMillis();
        try {
            List<?> data = service.search(companion, startTime, endTime,hasAttachment);
            // 调用搜索方法,返回 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, service.getModuleName(), data, data.size(), searchTime
                moduleCode, moduleName, data, count, searchTime
            );
            return CompletableFuture.completedFuture(result);
        } catch (Exception e) {
            log.error("模块[{}]搜索失败: {}", moduleCode, e.getMessage());
            ModuleSearchResult result = ModuleSearchResult.error(moduleCode, e.getMessage());
            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);
        }
    }
    /**
     * 通用的路由搜索请求 - 统一数据结构版
     * 支持功能:
     * 1. 单个模块: "module1"
     * 2. 多个模块: "module1,module2,module3" (逗号分隔)
     * 3. 全模块:   "all" 或 null 或 空字符串
     * 4. 不选模块: 指定为特定值,如"none"(可选功能)
     *
     * 统一返回数据结构:
     * {
     *   "success": true,
     *   "data": {
     *     "modules": {
     *       "module1": { "data": [...], "count": 10, "searchTime": 100, "success": true },
     *       "module2": { "error": "错误信息", "success": false }
     *     },
     *     "totalSelectedModules": 2,
     *     "successModules": 1,
     *     "errorModules": ["module2"],
     *     "totalRecords": 10,
     *     "searchTime": "2023-01-01T00:00:00.000+00:00",
     *     "message": "搜索完成: 成功1/2个模块,总计10条记录"
     *   }
     * }
     * 通用的路由搜索请求
     */
    public AjaxResult routeSearch(String moduleCode, Object... args) {
        log.info("路由搜索: moduleCode={}, args={}", moduleCode, Arrays.toString(args));
    public AjaxResult routeSearch(PeopleSea peopleS, Integer pageNum, Integer pageSize) {
        String moduleCode = null;
        // 验证参数数量
        if (args.length != 4) {
            return buildErrorResponse("参数数量错误,需要4个参数,实际收到: " + args.length, 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);
@@ -108,14 +113,14 @@
        }
        // 统一处理:单个模块、多个模块都使用同样的处理逻辑
        return executeModulesSearch(targetModules, args);
        return executeModulesSearch(targetModules, peopleS, pageNum, pageSize);
    }
    /**
     * 统一执行模块搜索
     * 无论是单个模块还是多个模块,都使用统一的数据结构返回
     */
    private AjaxResult executeModulesSearch(List<String> moduleCodes, Object... args) {
    private AjaxResult executeModulesSearch(List<String> moduleCodes, PeopleSea peopleS,
                                            Integer pageNum, Integer pageSize) {
        log.info("执行模块搜索: moduleCodes={}, 模块数量={}", moduleCodes, moduleCodes.size());
        // 验证所有模块是否存在
@@ -127,80 +132,134 @@
            String availableModules = String.join(", ", moduleSearchMap.keySet());
            String errorMsg = String.format("以下模块不支持: %s。可用模块: [%s]",
                invalidModules, availableModules);
            return buildErrorResponse(errorMsg, moduleCodes);
            return AjaxResult.error(errorMsg);
        }
        // 提取搜索参数
        String companion = extractCompanion(args);
        Date happenStartTime = extractStartTime(args);
        Date happenEndTime = extractEndTime(args);
        String hasAttachment = extractHasAttachment(args);
        // 提取参数
        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;
        }
        // 并发搜索
        Integer finalPageNum = pageNum;
        Integer finalPageSize = pageSize;
        List<CompletableFuture<ModuleSearchResult>> futures = moduleCodes.stream()
            .map(code -> searchModuleAsync(code, moduleSearchMap.get(code),
                companion, happenStartTime, happenEndTime,hasAttachment))
                companion, startTime, endTime, hasAttachment, finalPageNum, finalPageSize))
            .collect(Collectors.toList());
        // 等待所有搜索完成
        // 等待完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        // 收集结果
        Map<String, Object> modulesResult = new LinkedHashMap<>();
        int successCount = 0;
        int totalCount = 0;
        List<String> errorModules = new ArrayList<>();
        for (int i = 0; i < futures.size(); i++) {
        // 合并数据
        List<Object> allData = new ArrayList<>();
        for (CompletableFuture<ModuleSearchResult> future : futures) {
            try {
                ModuleSearchResult moduleResult = futures.get(i).get();
                String moduleCode = moduleCodes.get(i);
                if (moduleResult.isSuccess()) {
                    Map<String, Object> moduleData = new HashMap<>();
                    moduleData.put("data", moduleResult.getData());
                    moduleData.put("count", moduleResult.getCount());
                    moduleData.put("searchTime", moduleResult.getSearchTime());
                    moduleData.put("success", true);
                    modulesResult.put(moduleCode, moduleData);
                    successCount++;
                    totalCount += moduleResult.getCount();
                } else {
                    Map<String, Object> errorInfo = new HashMap<>();
                    errorInfo.put("error", moduleResult.getErrorMessage());
                    errorInfo.put("success", false);
                    errorInfo.put("moduleName", getModuleName(moduleCode));
                    modulesResult.put(moduleCode, errorInfo);
                    errorModules.add(moduleCode);
                ModuleSearchResult result = future.get();
                if (result.isSuccess() && result.getData() != null) {
                    allData.addAll(result.getData());
                }
            } catch (Exception e) {
                log.error("获取模块[{}]搜索结果失败", moduleCodes.get(i), e);
                String moduleCode = moduleCodes.get(i);
                Map<String, Object> errorInfo = new HashMap<>();
                errorInfo.put("error", "获取结果异常: " + e.getMessage());
                errorInfo.put("success", false);
                errorInfo.put("moduleName", getModuleName(moduleCode));
                modulesResult.put(moduleCode, errorInfo);
                errorModules.add(moduleCode);
                // 记录错误但继续处理其他模块
                log.error("获取模块搜索结果失败", e);
            }
        }
        // 构建最终返回结果
        Map<String, Object> finalResult = new LinkedHashMap<>();
        finalResult.put("modules", modulesResult);
        finalResult.put("totalSelectedModules", moduleCodes.size());
        finalResult.put("successModules", successCount);
        finalResult.put("errorModules", errorModules);
        finalResult.put("totalRecords", totalCount);
        finalResult.put("searchTime", new Date());
        // 对数据进行排序(如果需要)
        sortData(allData);
        // 构建消息
        String message = buildSuccessMessage(moduleCodes.size(), successCount, errorModules, totalCount);
        finalResult.put("message", message);
        // 分页
        int startIndex = (pageNum - 1) * pageSize;
        List<Object> paginatedData = allData.stream()
            .skip(startIndex)
            .limit(pageSize)
            .collect(Collectors.toList());
        return AjaxResult.success(message, finalResult);
        // 返回结果
        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;
    }
    /**
@@ -212,52 +271,38 @@
    }
    /**
     * 构建成功消息
     * 安全的参数提取方法
     */
    private String buildSuccessMessage(int totalSelected, int successCount,
                                       List<String> errorModules, int totalRecords) {
        if (errorModules.isEmpty()) {
            if (totalSelected == 1) {
                return String.format("搜索成功: 找到%s条记录", totalRecords);
            } else {
                return String.format("搜索完成: 成功%s/%s个模块,总计%s条记录",
                    successCount, totalSelected, totalRecords);
            }
        } else {
            return String.format("搜索完成: 成功%s/%s个模块,总计%s条记录,失败模块: %s",
                successCount, totalSelected, totalRecords, errorModules);
    private String extractCompanion(PeopleSea peopleS) {
        if (peopleS == null) {
            return "";
        }
        return peopleS.getPeoples() == null ? "" : peopleS.getPeoples().trim();
    }
    /**
     * 构建错误响应
     */
    private AjaxResult buildErrorResponse(String errorMessage, List<String> moduleCodes) {
        Map<String, Object> errorData = new HashMap<>();
        errorData.put("message", errorMessage);
        errorData.put("success", false);
        if (moduleCodes != null) {
            errorData.put("selectedModules", moduleCodes);
    private Date extractStartTime(PeopleSea peopleS) {
        if (peopleS == null) {
            return null;
        }
        return peopleS.getStartTime();
    }
        errorData.put("totalSelectedModules", moduleCodes != null ? moduleCodes.size() : 0);
        errorData.put("successModules", 0);
        errorData.put("errorModules", moduleCodes != null ? moduleCodes : Collections.emptyList());
        errorData.put("totalRecords", 0);
        errorData.put("searchTime", new Date());
    private Date extractEndTime(PeopleSea peopleS) {
        if (peopleS == null) {
            return null;
        }
        return peopleS.getEndTime();
    }
        return AjaxResult.error(errorMessage, errorData);
    private String extractHasAttachment(PeopleSea peopleS) {
        if (peopleS == null) {
            return "";
        }
        return peopleS.getHasAttachment() == null ? "" : peopleS.getHasAttachment().trim();
    }
    /**
     * 解析模块代码
     * 支持多种格式:
     * 1. 空/空白/null -> 全模块搜索
     * 2. "all" -> 全模块搜索
     * 3. 单个模块 -> 返回单个模块
     * 4. 逗号分隔的多个模块 -> 返回模块列表
     * 5. 其他特殊标识(如"none")-> 返回空列表表示不选模块
     */
    private List<String> parseModuleCodes(String moduleCode) {
        if (StringUtils.isBlank(moduleCode)) {
@@ -272,7 +317,7 @@
            return new ArrayList<>(moduleSearchMap.keySet());
        }
        // 处理不选模块的情况(可选扩展)
        // 处理不选模块的情况
        if ("none".equalsIgnoreCase(trimmedCode) || "null".equalsIgnoreCase(trimmedCode)) {
            return Collections.emptyList();
        }
@@ -291,18 +336,14 @@
    }
    /**
     * 不选模块的处理逻辑 - 使用统一数据结构
     * 不选模块的处理逻辑
     */
    private AjaxResult handleNoModuleSelected() {
        Map<String, Object> result = new LinkedHashMap<>();
        result.put("modules", Collections.emptyMap());
        result.put("totalSelectedModules", 0);
        result.put("successModules", 0);
        result.put("errorModules", Collections.emptyList());
        result.put("totalRecords", 0);
        result.put("searchTime", new Date());
        result.put("message", "未选择搜索模块");
        result.put("availableModules", new ArrayList<>(moduleSearchMap.keySet()));
        result.put("list", Collections.emptyList());
        result.put("total", 0);
        result.put("pageNum", 1);
        result.put("pageSize", 10);
        return AjaxResult.success("未选择搜索模块,请选择要搜索的模块", result);
    }
@@ -354,29 +395,4 @@
        return moduleSearchMap.containsKey(trimmedCode) ||
            ALL_MODULES_FLAG.equalsIgnoreCase(trimmedCode);
    }
    /**
     * 参数提取辅助方法
     */
    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;
    }
    private String extractHasAttachment(Object[] args) {
        return (args.length > 3 && args[3] instanceof String) ? (String) args[3] : null;
}
}