5 天以前 0d7d874912d0147376826b55667a1deb6547ed91
src/main/java/com/ruoyi/ai/assistant/ApproveTodoIntentExecutor.java
@@ -14,8 +14,9 @@
@Component
public class ApproveTodoIntentExecutor {
    private static final Pattern APPROVE_ID_PATTERN = Pattern.compile("\\b[A-Za-z]*\\d{8,}\\b");
    private static final Pattern LIMIT_PATTERN = Pattern.compile("(前|最近)?(\\d{1,2})条");
    private static final Pattern APPROVE_ID_BY_LABEL_PATTERN = Pattern.compile("(流程编号|流程号|流程ID|审批编号|编号)\\s*[::]?\\s*([A-Za-z0-9_-]{2,64})");
    private static final Pattern APPROVE_ID_PATTERN = Pattern.compile("\\b[A-Za-z]*\\d{6,}[A-Za-z0-9_-]*\\b");
    private static final Pattern LIMIT_PATTERN = Pattern.compile("(前|最近)?\\s*(\\d{1,2})\\s*条");
    private static final Pattern DATE_PATTERN = Pattern.compile("(\\d{4}-\\d{2}-\\d{2})");
    private static final Pattern NUMBER_PATTERN = Pattern.compile("(\\d+(?:\\.\\d+)?)");
    private static final Pattern RECENT_RANGE_PATTERN = Pattern.compile("近\\d+(天|周|个月|月|年)");
@@ -34,53 +35,63 @@
        }
        String text = message.trim();
        String quickPromptResponse = tryExecuteQuickPrompt(memoryId, text);
        if (StringUtils.hasText(quickPromptResponse)) {
            return quickPromptResponse;
        }
        String approveId = extractApproveId(text);
        boolean hasApproveId = StringUtils.hasText(approveId) && !isPlaceholderApproveId(approveId);
        String startDate = extractStartDate(text);
        String endDate = extractEndDate(text);
        String timeRange = extractTimeRange(text);
        if (isStatsIntent(text)) {
            return approveTodoTools.getTodoStats(
                    memoryId,
                    extractStartDate(text),
                    extractEndDate(text),
                    extractTimeRange(text)
                    startDate,
                    endDate,
                    timeRange
            );
        }
        if (containsAny(text, "流转", "进度", "节点", "日志")) {
            return StringUtils.hasText(approveId)
        if (containsAny(text, "流转", "进度", "节点", "日志", "卡在", "卡到", "当前审批人", "处理记录")) {
            return hasApproveId
                    ? approveTodoTools.getTodoProgress(memoryId, approveId)
                    : missingApproveId("todo_progress", "查询审批进度需要提供流程编号。");
        }
        if (containsAny(text, "详情", "明细") && !containsAny(text, "列表")) {
            return StringUtils.hasText(approveId)
            return hasApproveId
                    ? approveTodoTools.getTodoDetail(memoryId, approveId)
                    : missingApproveId("todo_detail", "查询审批详情需要提供流程编号。");
        }
        if (containsAny(text, "取消审核", "撤销审核", "回退审核")) {
            return StringUtils.hasText(approveId)
                    ? approveTodoTools.cancelReviewTodo(memoryId, approveId, extractTail(text, "原因"))
        if (containsAny(text, "取消审核", "撤销审核", "回退审核", "撤销审批", "撤回审批")
                || (containsAny(text, "撤销", "撤回") && containsAny(text, "审批操作", "审核操作"))) {
            return hasApproveId
                    ? approveTodoTools.cancelReviewTodo(memoryId, approveId, extractRemark(text))
                    : missingApproveId("cancel_review_action", "取消审核需要提供流程编号。");
        }
        if (containsAny(text, "删除")) {
            return StringUtils.hasText(approveId)
        if (containsAny(text, "删除", "移除")) {
            return hasApproveId
                    ? approveTodoTools.deleteTodo(memoryId, approveId)
                    : missingApproveId("delete_action", "删除审批单需要提供流程编号。");
        }
        if (containsAny(text, "驳回", "拒绝")) {
            return StringUtils.hasText(approveId)
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", extractTail(text, "原因"))
            return hasApproveId
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", extractRemark(text))
                    : missingApproveId("review_action", "驳回审批需要提供流程编号。");
        }
        if (containsAny(text, "审核通过", "审批通过", "通过审批", "同意审批", "审批同意")) {
            return StringUtils.hasText(approveId)
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractTail(text, "备注"))
            return hasApproveId
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractRemark(text))
                    : missingApproveId("review_action", "审批通过需要提供流程编号。");
        }
        if (StringUtils.hasText(approveId)
        if (hasApproveId
                && containsAny(text, "通过", "同意")
                && !containsAny(text, "未通过", "通过率", "审批通过率", "审核通过率")) {
            return approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractTail(text, "备注"));
            return approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractRemark(text));
        }
        if (containsAny(text, "修改")) {
            return StringUtils.hasText(approveId)
        if (containsAny(text, "修改", "更新", "变更")) {
            return hasApproveId
                    ? approveTodoTools.updateTodo(
                    memoryId,
                    approveId,
@@ -93,19 +104,95 @@
                    extractValue(text, "备注"))
                    : missingApproveId("update_action", "修改审批单需要提供流程编号。");
        }
        if (containsAny(text, "列表", "待办", "查询审批")) {
        if (containsAny(text, "列表", "待办", "查询审批", "单据", "流程", "审批批")) {
            return approveTodoTools.listTodos(
                    memoryId,
                    extractStatus(text),
                    extractApproveType(text),
                    extractKeyword(text),
                    extractLimit(text));
                    extractLimit(text),
                    extractScope(text),
                    startDate,
                    endDate,
                    timeRange);
        }
        return null;
    }
    private String tryExecuteQuickPrompt(String memoryId, String text) {
        String normalized = normalizeForMatch(text);
        String approveId = extractApproveId(text);
        boolean hasApproveId = StringUtils.hasText(approveId) && !isPlaceholderApproveId(approveId);
        if ("我当前有哪些审批待办需要处理".equals(normalized)) {
            return approveTodoTools.listTodos(memoryId, "pending", null, null, 10, "approver", null, null, null);
        }
        if ("帮我列出今天新增的审批待办".equals(normalized)) {
            return approveTodoTools.listTodos(memoryId, "all", null, null, 10, "related", null, null, "今天");
        }
        if ("当前待我审批的单据按时间倒序列出来".equals(normalized)) {
            return approveTodoTools.listTodos(memoryId, "pending", null, null, 10, "approver", null, null, null);
        }
        if ("我发起的审批里哪些还在处理中".equals(normalized)) {
            return approveTodoTools.listTodos(memoryId, "processing", null, null, 10, "applicant", null, null, null);
        }
        if ("近7天我的审批待办统计情况怎么样".equals(normalized)) {
            return approveTodoTools.getTodoStats(memoryId, null, null, "近7天");
        }
        if ("本月我的审批中通过驳回处理中各有多少".equals(normalized)) {
            return approveTodoTools.getTodoStats(memoryId, null, null, "本月");
        }
        if ("近30天各类型审批数量分布是什么".equals(normalized)) {
            return approveTodoTools.getTodoStats(memoryId, null, null, "近30天");
        }
        if (normalized.startsWith("查询流程编号") && normalized.contains("审批详情")) {
            return hasApproveId
                    ? approveTodoTools.getTodoDetail(memoryId, approveId)
                    : missingApproveId("todo_detail", "查询审批详情需要提供真实流程编号。");
        }
        if (normalized.startsWith("流程编号")
                && normalized.contains("卡在哪个审批节点")
                && normalized.contains("当前审批人是谁")) {
            return hasApproveId
                    ? approveTodoTools.getTodoProgress(memoryId, approveId)
                    : missingApproveId("todo_progress", "查询审批进度需要提供真实流程编号。");
        }
        if (normalized.startsWith("帮我查看流程编号") && normalized.contains("审批流转记录")) {
            return hasApproveId
                    ? approveTodoTools.getTodoProgress(memoryId, approveId)
                    : missingApproveId("todo_progress", "查询审批流转记录需要提供真实流程编号。");
        }
        if (normalized.startsWith("帮我审批通过流程编号")) {
            return hasApproveId
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractRemark(text))
                    : missingApproveId("review_action", "审批通过需要提供真实流程编号。");
        }
        if (normalized.startsWith("帮我驳回流程编号")) {
            return hasApproveId
                    ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", extractRemark(text))
                    : missingApproveId("review_action", "驳回审批需要提供真实流程编号。");
        }
        if (normalized.startsWith("撤销我刚刚对流程编号") && normalized.contains("审批操作")) {
            return hasApproveId
                    ? approveTodoTools.cancelReviewTodo(memoryId, approveId, extractRemark(text))
                    : missingApproveId("cancel_review_action", "撤销审批操作需要提供真实流程编号。");
        }
        if (normalized.startsWith("帮我修改流程编号") && normalized.contains("备注为")) {
            return hasApproveId
                    ? approveTodoTools.updateTodo(memoryId, approveId, null, null, null, null, null, null, extractRemark(text))
                    : missingApproveId("update_action", "修改审批单需要提供真实流程编号。");
        }
        if (normalized.startsWith("删除我发起的流程编号")) {
            return hasApproveId
                    ? approveTodoTools.deleteTodo(memoryId, approveId)
                    : missingApproveId("delete_action", "删除审批单需要提供真实流程编号。");
        }
        return null;
    }
    private boolean isStatsIntent(String text) {
        if (containsAny(text, "统计", "分析", "图表", "趋势", "占比", "汇总", "总量")) {
        if (containsAny(text, "统计", "分析", "图表", "趋势", "占比", "汇总", "总量", "分布", "各有多少", "有多少")) {
            return true;
        }
        boolean hasQueryWord = containsAny(text, "查询", "查看", "看下", "看看", "获取");
@@ -128,6 +215,10 @@
    }
    private String extractApproveId(String text) {
        Matcher keywordMatcher = APPROVE_ID_BY_LABEL_PATTERN.matcher(text);
        if (keywordMatcher.find()) {
            return keywordMatcher.group(2);
        }
        Matcher matcher = APPROVE_ID_PATTERN.matcher(text);
        return matcher.find() ? matcher.group() : null;
    }
@@ -141,13 +232,13 @@
        if (containsAny(text, "待审核", "待审批")) {
            return "pending";
        }
        if (containsAny(text, "审核中")) {
        if (containsAny(text, "审核中", "处理中", "处理中的", "办理中")) {
            return "processing";
        }
        if (containsAny(text, "已通过", "审核完成")) {
        if (containsAny(text, "已通过", "通过", "审核完成", "审批完成")) {
            return "approved";
        }
        if (containsAny(text, "未通过", "驳回")) {
        if (containsAny(text, "未通过", "驳回", "已驳回", "拒绝")) {
            return "rejected";
        }
        if (containsAny(text, "重新提交")) {
@@ -187,9 +278,15 @@
    private String extractKeyword(String text) {
        String cleaned = text
                .replace("查询", "")
                .replace("查看", "")
                .replace("列出", "")
                .replace("帮我", "")
                .replace("审批", "")
                .replace("单据", "")
                .replace("待办", "")
                .replace("列表", "")
                .replace("流程编号", "")
                .replace("流程号", "")
                .replace("前10条", "")
                .replace("最近10条", "")
                .trim();
@@ -197,7 +294,7 @@
    }
    private String extractValue(String text, String fieldName) {
        Pattern pattern = Pattern.compile(fieldName + "(改为|修改为|是)[::]?[\\s]*([^,,。;;\\s]+)");
        Pattern pattern = Pattern.compile(fieldName + "(改为|修改为|为|是)[::]?[\\s]*([^,,。;;\\s]+)");
        Matcher matcher = pattern.matcher(text);
        return matcher.find() ? matcher.group(2) : null;
    }
@@ -244,7 +341,7 @@
        if (!text.contains(fieldName)) {
            return null;
        }
        Matcher matcher = Pattern.compile(fieldName + "(改为|修改为|是)[::]?[\\s]*(\\d{1,2})").matcher(text);
        Matcher matcher = Pattern.compile(fieldName + "(改为|修改为|为|是)[::]?[\\s]*(\\d{1,2})").matcher(text);
        return matcher.find() ? Integer.parseInt(matcher.group(2)) : null;
    }
@@ -258,9 +355,87 @@
    }
    private String extractTail(String text, String key) {
        Pattern quotedPattern = Pattern.compile(key + "(是|为)?[::]?[\\s]*[“\"]([^”\"]+)[”\"]");
        Matcher quotedMatcher = quotedPattern.matcher(text);
        if (quotedMatcher.find()) {
            return cleanContent(quotedMatcher.group(2));
        }
        Pattern pattern = Pattern.compile(key + "(是|为)?[::]?[\\s]*(.+)");
        Matcher matcher = pattern.matcher(text);
        return matcher.find() ? matcher.group(2).trim() : null;
        return matcher.find() ? cleanContent(matcher.group(2)) : null;
    }
    private String extractScope(String text) {
        if (containsAny(text, "我发起", "我提交", "我申请", "申请人是我")) {
            return "applicant";
        }
        if (containsAny(text, "待我审批", "待我审核", "我处理", "我审批", "当前待我", "需要我处理", "需要处理")) {
            return "approver";
        }
        return "related";
    }
    private String extractRemark(String text) {
        return firstNonBlank(firstNonBlank(extractTail(text, "备注"), extractTail(text, "原因")), extractQuotedContent(text));
    }
    private String extractQuotedContent(String text) {
        Matcher matcher = Pattern.compile("[“\"]([^”\"]+)[”\"]").matcher(text);
        return matcher.find() ? cleanContent(matcher.group(1)) : null;
    }
    private String normalizeForMatch(String text) {
        if (!StringUtils.hasText(text)) {
            return "";
        }
        return text.replace(",", "")
                .replace(",", "")
                .replace("。", "")
                .replace(".", "")
                .replace("!", "")
                .replace("!", "")
                .replace("?", "")
                .replace("?", "")
                .replace(":", "")
                .replace(":", "")
                .replace(";", "")
                .replace(";", "")
                .replace("“", "")
                .replace("”", "")
                .replace("\"", "")
                .replace(" ", "")
                .trim();
    }
    private boolean isPlaceholderApproveId(String approveId) {
        if (!StringUtils.hasText(approveId)) {
            return true;
        }
        String value = approveId.trim();
        return "xxx".equalsIgnoreCase(value)
                || value.matches("[xX]{2,}")
                || "流程编号".equals(value)
                || "编号".equals(value)
                || value.contains("示例")
                || value.contains("请输入");
    }
    private String cleanContent(String text) {
        if (!StringUtils.hasText(text)) {
            return null;
        }
        return text.trim()
                .replace("“", "")
                .replace("”", "")
                .replace("\"", "")
                .replace("。", "")
                .replace(";", "")
                .replace(";", "")
                .trim();
    }
    private String firstNonBlank(String first, String second) {
        return StringUtils.hasText(first) ? first : second;
    }
    private String missingApproveId(String type, String description) {