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 static final Pattern FUTURE_MONTH_PATTERN = Pattern.compile("(?:未来|后续|接下来)\\s*(\\d{1,2})\\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, extractForecastMonths(text));
|
}
|
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) || "生成周报".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, null);
|
}
|
if ("近30天哪个客户利润贡献最高".equals(normalized)) {
|
DateRange range = recentDaysRange(30);
|
return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
|
}
|
if ("查看本月经营驾驶舱".equals(normalized) || "查看经营驾驶舱".equals(normalized)) {
|
DateRange range = monthRange();
|
return financialAgentTools.getBusinessCockpit(memoryId, range.startDate(), range.endDate(), range.label());
|
}
|
if ("查询近30天亏损订单".equals(normalized)) {
|
DateRange range = recentDaysRange(30);
|
return financialAgentTools.analyzeOrderProfit(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
|
}
|
if ("分析近30天库存资金占用".equals(normalized)) {
|
DateRange range = recentDaysRange(30);
|
return financialAgentTools.analyzeInventoryCapital(memoryId, range.startDate(), range.endDate(), range.label(), null, null);
|
}
|
if ("预测未来3个月现金流".equals(normalized)) {
|
return financialAgentTools.forecastCashFlow(memoryId, null, null, null, 3);
|
}
|
if ("哪个工序成本最高".equals(normalized)) {
|
return financialAgentTools.calculateIntelligentCost(memoryId, null, null, null, null, null);
|
}
|
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(1)) : null;
|
}
|
|
private Integer extractForecastMonths(String text) {
|
Matcher matcher = FUTURE_MONTH_PATTERN.matcher(text);
|
return matcher.find() ? Integer.parseInt(matcher.group(1)) : null;
|
}
|
|
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(1));
|
return recentDaysRange(days);
|
}
|
return new DateRange(null, null, null);
|
}
|
|
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, null);
|
}
|
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(":", "")
|
.replace(":", "")
|
.replace(";", "")
|
.replace(";", "")
|
.replace(" ", "")
|
.trim();
|
}
|
|
private String extractKeyword(String text) {
|
String cleaned = text
|
.replaceAll("\\d{4}-\\d{2}-\\d{2}", "")
|
.replaceAll("(?:近|最近)\\s*\\d{1,3}\\s*天", "")
|
.replaceAll("(?:前|最近|展示|返回)?\\s*\\d{1,2}\\s*(?:条|个|名)", "")
|
.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) {
|
}
|
}
|