| | |
| | | @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+(天|周|个月|月|年)"); |
| | |
| | | } |
| | | |
| | | 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, |
| | |
| | | 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, "查询", "查看", "看下", "看看", "获取"); |
| | |
| | | } |
| | | |
| | | 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; |
| | | } |
| | |
| | | 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, "重新提交")) { |
| | |
| | | private String extractKeyword(String text) { |
| | | String cleaned = text |
| | | .replace("查询", "") |
| | | .replace("查看", "") |
| | | .replace("列出", "") |
| | | .replace("帮我", "") |
| | | .replace("审批", "") |
| | | .replace("单据", "") |
| | | .replace("待办", "") |
| | | .replace("列表", "") |
| | | .replace("流程编号", "") |
| | | .replace("流程号", "") |
| | | .replace("前10条", "") |
| | | .replace("最近10条", "") |
| | | .trim(); |
| | |
| | | } |
| | | |
| | | 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; |
| | | } |
| | |
| | | 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; |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | 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) { |