| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.assistant; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.ruoyi.ai.tools.ApproveTodoTools; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | import java.util.regex.Matcher; |
| | | import java.util.regex.Pattern; |
| | | |
| | | @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 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+(天|å¨|个æ|æ|å¹´)"); |
| | | private static final Pattern HALF_RANGE_PATTERN = Pattern.compile("(æè¿|è¿)?å(个)?(æ|å¹´)"); |
| | | private static final Pattern EXPLICIT_RANGE_PATTERN = Pattern.compile(".*(å°|è³).*"); |
| | | |
| | | private final ApproveTodoTools approveTodoTools; |
| | | |
| | | public ApproveTodoIntentExecutor(ApproveTodoTools approveTodoTools) { |
| | | this.approveTodoTools = approveTodoTools; |
| | | } |
| | | |
| | | public String tryExecute(String memoryId, String message) { |
| | | if (!StringUtils.hasText(message)) { |
| | | return null; |
| | | } |
| | | |
| | | String text = message.trim(); |
| | | String approveId = extractApproveId(text); |
| | | |
| | | if (isStatsIntent(text)) { |
| | | return approveTodoTools.getTodoStats( |
| | | memoryId, |
| | | extractStartDate(text), |
| | | extractEndDate(text), |
| | | extractTimeRange(text) |
| | | ); |
| | | } |
| | | if (containsAny(text, "æµè½¬", "è¿åº¦", "èç¹", "æ¥å¿", "å¡å¨", "å¡å°", "å½å审æ¹äºº", "å¤çè®°å½")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.getTodoProgress(memoryId, approveId) |
| | | : missingApproveId("todo_progress", "æ¥è¯¢å®¡æ¹è¿åº¦éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "详æ
", "æç»") && !containsAny(text, "å表")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.getTodoDetail(memoryId, approveId) |
| | | : missingApproveId("todo_detail", "æ¥è¯¢å®¡æ¹è¯¦æ
éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "åæ¶å®¡æ ¸", "æ¤éå®¡æ ¸", "åéå®¡æ ¸", "æ¤é审æ¹", "æ¤å审æ¹") |
| | | || (containsAny(text, "æ¤é", "æ¤å") && containsAny(text, "å®¡æ¹æä½", "å®¡æ ¸æä½"))) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.cancelReviewTodo(memoryId, approveId, firstNonBlank(extractTail(text, "åå "), extractTail(text, "夿³¨"))) |
| | | : missingApproveId("cancel_review_action", "åæ¶å®¡æ ¸éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å é¤", "ç§»é¤")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.deleteTodo(memoryId, approveId) |
| | | : missingApproveId("delete_action", "å é¤å®¡æ¹åéè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "驳å", "æç»")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.reviewTodo(memoryId, approveId, "reject", firstNonBlank(extractTail(text, "åå "), extractTail(text, "夿³¨"))) |
| | | : missingApproveId("review_action", "驳å审æ¹éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å®¡æ ¸éè¿", "审æ¹éè¿", "éè¿å®¡æ¹", "åæå®¡æ¹", "审æ¹åæ")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractTail(text, "夿³¨")) |
| | | : missingApproveId("review_action", "审æ¹éè¿éè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (StringUtils.hasText(approveId) |
| | | && containsAny(text, "éè¿", "åæ") |
| | | && !containsAny(text, "æªéè¿", "éè¿ç", "审æ¹éè¿ç", "å®¡æ ¸éè¿ç")) { |
| | | return approveTodoTools.reviewTodo(memoryId, approveId, "approve", extractTail(text, "夿³¨")); |
| | | } |
| | | if (containsAny(text, "ä¿®æ¹", "æ´æ°", "åæ´")) { |
| | | return StringUtils.hasText(approveId) |
| | | ? approveTodoTools.updateTodo( |
| | | memoryId, |
| | | approveId, |
| | | extractValue(text, "æ é¢"), |
| | | extractDateValue(text, "å¼å§æ¥æ"), |
| | | extractDateValue(text, "ç»ææ¥æ"), |
| | | extractBigDecimalValue(text, "éé¢"), |
| | | extractValue(text, "å°ç¹"), |
| | | extractIntegerValue(text, "ç±»å"), |
| | | extractValue(text, "夿³¨")) |
| | | : missingApproveId("update_action", "ä¿®æ¹å®¡æ¹åéè¦æä¾æµç¨ç¼å·ã"); |
| | | } |
| | | if (containsAny(text, "å表", "å¾
å", "æ¥è¯¢å®¡æ¹", "åæ®", "æµç¨", "å®¡æ¹æ¹")) { |
| | | return approveTodoTools.listTodos( |
| | | memoryId, |
| | | extractStatus(text), |
| | | extractApproveType(text), |
| | | extractKeyword(text), |
| | | extractLimit(text), |
| | | extractScope(text)); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private boolean isStatsIntent(String text) { |
| | | if (containsAny(text, "ç»è®¡", "åæ", "å¾è¡¨", "è¶å¿", "å æ¯", "æ±æ»", "æ»é", "åå¸", "åæå¤å°", "æå¤å°")) { |
| | | return true; |
| | | } |
| | | boolean hasQueryWord = containsAny(text, "æ¥è¯¢", "æ¥ç", "çä¸", "çç", "è·å"); |
| | | boolean hasDataWord = containsAny(text, "æ°æ®", "æ¥è¡¨", "æ
åµ", "æ°é", "éé¢"); |
| | | boolean hasTimeWord = containsAny(text, "ä»å¤©", "æ¨æ¥", "æ¨å¤©", "æ¬å¨", "ä¸å¨", "æ¬æ", "䏿", "æ¬å¹´", "ä»å¹´", "å»å¹´") |
| | | || DATE_PATTERN.matcher(text).find() |
| | | || RECENT_RANGE_PATTERN.matcher(text).find() |
| | | || HALF_RANGE_PATTERN.matcher(text).find() |
| | | || EXPLICIT_RANGE_PATTERN.matcher(text).matches(); |
| | | return hasQueryWord && hasDataWord && hasTimeWord; |
| | | } |
| | | |
| | | private boolean containsAny(String text, String... keywords) { |
| | | for (String keyword : keywords) { |
| | | if (text.contains(keyword)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private String extractApproveId(String text) { |
| | | Matcher matcher = APPROVE_ID_PATTERN.matcher(text); |
| | | return matcher.find() ? matcher.group() : null; |
| | | } |
| | | |
| | | private Integer extractLimit(String text) { |
| | | Matcher matcher = LIMIT_PATTERN.matcher(text); |
| | | return matcher.find() ? Integer.parseInt(matcher.group(2)) : 10; |
| | | } |
| | | |
| | | private String extractStatus(String text) { |
| | | if (containsAny(text, "å¾
å®¡æ ¸", "å¾
审æ¹")) { |
| | | return "pending"; |
| | | } |
| | | if (containsAny(text, "å®¡æ ¸ä¸", "å¤çä¸", "å¤çä¸ç", "åçä¸")) { |
| | | return "processing"; |
| | | } |
| | | if (containsAny(text, "å·²éè¿", "éè¿", "å®¡æ ¸å®æ", "审æ¹å®æ")) { |
| | | return "approved"; |
| | | } |
| | | if (containsAny(text, "æªéè¿", "驳å", "已驳å", "æç»")) { |
| | | return "rejected"; |
| | | } |
| | | if (containsAny(text, "éæ°æäº¤")) { |
| | | return "resubmitted"; |
| | | } |
| | | return "all"; |
| | | } |
| | | |
| | | private Integer extractApproveType(String text) { |
| | | if (text.contains("å
¬åº")) { |
| | | return 1; |
| | | } |
| | | if (text.contains("请å")) { |
| | | return 2; |
| | | } |
| | | if (text.contains("åºå·®")) { |
| | | return 3; |
| | | } |
| | | if (text.contains("æ¥é")) { |
| | | return 4; |
| | | } |
| | | if (text.contains("éè´")) { |
| | | return 5; |
| | | } |
| | | if (text.contains("æ¥ä»·")) { |
| | | return 6; |
| | | } |
| | | if (text.contains("åè´§")) { |
| | | return 7; |
| | | } |
| | | if (text.contains("å±é©ä½ä¸")) { |
| | | return 8; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private String extractKeyword(String text) { |
| | | String cleaned = text |
| | | .replace("æ¥è¯¢", "") |
| | | .replace("æ¥ç", "") |
| | | .replace("ååº", "") |
| | | .replace("帮æ", "") |
| | | .replace("审æ¹", "") |
| | | .replace("åæ®", "") |
| | | .replace("å¾
å", "") |
| | | .replace("å表", "") |
| | | .replace("å10æ¡", "") |
| | | .replace("æè¿10æ¡", "") |
| | | .trim(); |
| | | return cleaned.length() >= 2 ? cleaned : null; |
| | | } |
| | | |
| | | private String extractValue(String text, String fieldName) { |
| | | Pattern pattern = Pattern.compile(fieldName + "(æ¹ä¸º|ä¿®æ¹ä¸º|æ¯)[:ï¼]?[\\s]*([^,ï¼ãï¼;\\s]+)"); |
| | | Matcher matcher = pattern.matcher(text); |
| | | return matcher.find() ? matcher.group(2) : null; |
| | | } |
| | | |
| | | private String extractDateValue(String text, String fieldName) { |
| | | int index = text.indexOf(fieldName); |
| | | if (index < 0) { |
| | | return null; |
| | | } |
| | | Matcher matcher = DATE_PATTERN.matcher(text.substring(index)); |
| | | return matcher.find() ? matcher.group(1) : null; |
| | | } |
| | | |
| | | private String extractStartDate(String text) { |
| | | Matcher matcher = DATE_PATTERN.matcher(text); |
| | | return matcher.find() ? matcher.group(1) : null; |
| | | } |
| | | |
| | | private String extractEndDate(String text) { |
| | | Matcher matcher = DATE_PATTERN.matcher(text); |
| | | if (!matcher.find()) { |
| | | return null; |
| | | } |
| | | return matcher.find() ? matcher.group(1) : null; |
| | | } |
| | | |
| | | private String extractTimeRange(String text) { |
| | | if (containsAny(text, "ä»å¤©", "æ¨æ¥", "æ¨å¤©", "æ¬å¨", "ä¸å¨", "æ¬æ", "䏿", "æ¬å¹´", "ä»å¹´", "å»å¹´")) { |
| | | return text; |
| | | } |
| | | if (RECENT_RANGE_PATTERN.matcher(text).find()) { |
| | | return text; |
| | | } |
| | | if (HALF_RANGE_PATTERN.matcher(text).find()) { |
| | | return text; |
| | | } |
| | | if (EXPLICIT_RANGE_PATTERN.matcher(text).matches()) { |
| | | return text; |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Integer extractIntegerValue(String text, String fieldName) { |
| | | if (!text.contains(fieldName)) { |
| | | return null; |
| | | } |
| | | Matcher matcher = Pattern.compile(fieldName + "(æ¹ä¸º|ä¿®æ¹ä¸º|æ¯)[:ï¼]?[\\s]*(\\d{1,2})").matcher(text); |
| | | return matcher.find() ? Integer.parseInt(matcher.group(2)) : null; |
| | | } |
| | | |
| | | private BigDecimal extractBigDecimalValue(String text, String fieldName) { |
| | | int index = text.indexOf(fieldName); |
| | | if (index < 0) { |
| | | return null; |
| | | } |
| | | Matcher matcher = NUMBER_PATTERN.matcher(text.substring(index)); |
| | | return matcher.find() ? new BigDecimal(matcher.group(1)) : null; |
| | | } |
| | | |
| | | private String extractTail(String text, String key) { |
| | | Pattern pattern = Pattern.compile(key + "(æ¯|为)?[:ï¼]?[\\s]*(.+)"); |
| | | Matcher matcher = pattern.matcher(text); |
| | | return matcher.find() ? matcher.group(2).trim() : null; |
| | | } |
| | | |
| | | private String extractScope(String text) { |
| | | if (containsAny(text, "æåèµ·", "ææäº¤", "æç³è¯·", "ç³è¯·äººæ¯æ")) { |
| | | return "applicant"; |
| | | } |
| | | if (containsAny(text, "å¾
æå®¡æ¹", "å¾
æå®¡æ ¸", "æå¤ç", "æå®¡æ¹", "å½åå¾
æ", "éè¦æå¤ç")) { |
| | | return "approver"; |
| | | } |
| | | return "related"; |
| | | } |
| | | |
| | | private String firstNonBlank(String first, String second) { |
| | | return StringUtils.hasText(first) ? first : second; |
| | | } |
| | | |
| | | private String missingApproveId(String type, String description) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("success", false); |
| | | result.put("type", type); |
| | | result.put("description", description); |
| | | result.put("summary", Map.of()); |
| | | result.put("data", Map.of()); |
| | | result.put("charts", Map.of()); |
| | | return JSON.toJSONString(result); |
| | | } |
| | | } |