package com.ruoyi.ai.assistant; import com.ruoyi.ai.tools.FinancialAgentTools; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import java.time.LocalDate; import java.time.YearMonth; import java.time.format.DateTimeFormatter; import java.util.regex.Matcher; import java.util.regex.Pattern; @Component public class FinancialIntentExecutor { private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd"); 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 RELATIVE_DAY_PATTERN = Pattern.compile("(近|最近)?\\s*(\\d{1,3})\\s*天"); private final FinancialAgentTools financialAgentTools; public FinancialIntentExecutor(FinancialAgentTools financialAgentTools) { this.financialAgentTools = financialAgentTools; } public String tryExecute(String memoryId, String message) { if (!StringUtils.hasText(message)) { return null; } String text = message.trim(); String quickPromptResponse = tryExecuteQuickPrompt(memoryId, text); if (StringUtils.hasText(quickPromptResponse)) { return quickPromptResponse; } DateRange dateRange = extractDateRange(text); Integer limit = extractLimit(text); String keyword = extractKeyword(text); String startDate = dateRange.startDate(); String endDate = dateRange.endDate(); String timeRange = dateRange.label(); if (containsAny(text, "成本核算", "产品成本", "工序成本", "人工成本", "折旧", "材料损耗")) { return financialAgentTools.calculateIntelligentCost(memoryId, startDate, endDate, timeRange, keyword, limit); } if (containsAny(text, "利润分析", "订单利润", "亏损订单", "低利润", "最赚钱客户", "哪个客户最赚钱", "客户最赚钱", "利润最高客户", "利润贡献最高客户", "利润下降")) { return financialAgentTools.analyzeOrderProfit(memoryId, startDate, endDate, timeRange, keyword, limit); } if (containsAny(text, "库存资金", "库存积压", "呆滞库存", "资金占用", "周转率", "库存周转")) { return financialAgentTools.analyzeInventoryCapital(memoryId, startDate, endDate, timeRange, keyword, limit); } if (containsAny(text, "现金流", "回款风险", "付款压力", "资金缺口", "应收", "应付", "回款预测")) { return financialAgentTools.forecastCashFlow(memoryId, startDate, endDate, timeRange, limit); } if (containsAny(text, "异常预警", "经营异常", "风险预警", "成本异常", "利润异常", "回款异常", "订单风险")) { return financialAgentTools.detectBusinessAnomalies(memoryId, startDate, endDate, timeRange, limit); } if (containsAny(text, "驾驶舱", "经营看板", "经营总览", "经营仪表盘", "经营大盘")) { return financialAgentTools.getBusinessCockpit(memoryId, startDate, endDate, timeRange); } if (containsAny(text, "日报", "周报", "经营报告", "分析报告")) { return financialAgentTools.generateOperationReport(memoryId, startDate, endDate, timeRange, containsAny(text, "周报") ? "weekly" : "daily"); } if (containsAny(text, "业财融合", "业财联动", "口径", "指标解释", "为什么")) { return financialAgentTools.retrieveFinancialKnowledge(memoryId, text); } return null; } private String tryExecuteQuickPrompt(String memoryId, String text) { String normalized = normalizeForMatch(text); if ("查看本月经营驾驶舱".equals(normalized) || "查看经营驾驶舱".equals(normalized)) { DateRange range = monthRange(); return financialAgentTools.getBusinessCockpit(memoryId, range.startDate(), range.endDate(), range.label()); } if ("查询近30天亏损订单".equals(normalized) || "哪个订单亏损".equals(normalized)) { DateRange range = recentDaysRange(30); return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20); } if ("生成本周经营周报".equals(normalized) || "生成周报".equals(normalized)) { DateRange range = weekRange(); return financialAgentTools.generateOperationReport(memoryId, range.startDate(), range.endDate(), range.label(), "weekly"); } if ("为什么利润下降".equals(normalized)) { DateRange range = monthRange(); return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20); } if ("哪个客户最赚钱".equals(normalized) || "最近哪个客户最赚钱".equals(normalized) || "本月哪个客户最赚钱".equals(normalized) || "近30天哪个客户最赚钱".equals(normalized) || "哪个客户利润最高".equals(normalized) || "哪个客户利润贡献最高".equals(normalized)) { DateRange range = extractDateRange(text); return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, 20); } return null; } private boolean containsAny(String text, String... keywords) { for (String keyword : keywords) { if (text.contains(keyword)) { return true; } } return false; } private Integer extractLimit(String text) { Matcher matcher = LIMIT_PATTERN.matcher(text); return matcher.find() ? Integer.parseInt(matcher.group(2)) : 10; } private DateRange extractDateRange(String text) { Matcher matcher = DATE_PATTERN.matcher(text); if (matcher.find()) { String first = matcher.group(1); String second = matcher.find() ? matcher.group(1) : first; return buildDateRange(first, second, first + "至" + second); } if (text.contains("本月")) { return monthRange(); } if (text.contains("上月")) { return lastMonthRange(); } if (text.contains("本年") || text.contains("今年")) { return yearRange(); } if (text.contains("本周")) { return weekRange(); } Matcher relativeDayMatcher = RELATIVE_DAY_PATTERN.matcher(text); if (relativeDayMatcher.find()) { int days = Integer.parseInt(relativeDayMatcher.group(2)); return recentDaysRange(days); } return new DateRange(null, null, "近30天"); } private DateRange buildDateRange(String start, String end, String label) { LocalDate startDate = parseDate(start); LocalDate endDate = parseDate(end); if (startDate == null || endDate == null) { return new DateRange(null, null, "近30天"); } if (startDate.isAfter(endDate)) { LocalDate temp = startDate; startDate = endDate; endDate = temp; } return new DateRange(formatDate(startDate), formatDate(endDate), label); } private DateRange recentDaysRange(int days) { LocalDate end = LocalDate.now(); int safeDays = Math.max(days, 1); LocalDate start = end.minusDays(safeDays - 1L); return new DateRange(formatDate(start), formatDate(end), "近" + safeDays + "天"); } private DateRange monthRange() { LocalDate today = LocalDate.now(); return new DateRange(formatDate(today.withDayOfMonth(1)), formatDate(today), "本月"); } private DateRange weekRange() { LocalDate today = LocalDate.now(); LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L); return new DateRange(formatDate(start), formatDate(today), "本周"); } private DateRange lastMonthRange() { YearMonth lastMonth = YearMonth.now().minusMonths(1); return new DateRange(formatDate(lastMonth.atDay(1)), formatDate(lastMonth.atEndOfMonth()), "上月"); } private DateRange yearRange() { LocalDate today = LocalDate.now(); return new DateRange(formatDate(today.withDayOfYear(1)), formatDate(today), "今年"); } private LocalDate parseDate(String text) { try { return LocalDate.parse(text, DATE_FMT); } catch (Exception ignored) { return null; } } private String formatDate(LocalDate date) { return date == null ? null : date.format(DATE_FMT); } 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(" ", "") .trim(); } private String extractKeyword(String text) { String cleaned = text .replace("查询", "") .replace("查看", "") .replace("看下", "") .replace("看看", "") .replace("帮我", "") .replace("请", "") .replace("一下", "") .replace("为什么", "") .replace("哪个客户最赚钱", "") .replace("最近哪个客户最赚钱", "") .replace("本月哪个客户最赚钱", "") .replace("近30天哪个客户最赚钱", "") .replace("最赚钱客户", "") .replace("客户最赚钱", "") .replace("哪个客户利润最高", "") .replace("利润最高客户", "") .replace("哪个客户利润贡献最高", "") .replace("利润贡献最高客户", "") .replace("本月", "") .replace("本周", "") .replace("本年", "") .replace("今年", "") .replace("上月", "") .replace("近30天", "") .replace("近7天", "") .replace("近90天", "") .replace("前10条", "") .replace("最近10条", "") .replace("前20条", "") .replace("最近20条", "") .replace("订单利润分析", "") .replace("利润分析", "") .replace("库存资金分析", "") .replace("现金流预测", "") .replace("经营驾驶舱", "") .replace("日报", "") .replace("周报", "") .replace("异常预警", "") .replace("条", "") .trim(); return cleaned.length() >= 2 ? cleaned : null; } private record DateRange(String startDate, String endDate, String label) { } }