package com.ruoyi.ai.assistant;
|
|
import com.ruoyi.ai.tools.PurchaseAgentTools;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.StringUtils;
|
|
import java.time.LocalDate;
|
import java.time.ZoneId;
|
import java.time.format.DateTimeFormatter;
|
import java.util.regex.Matcher;
|
import java.util.regex.Pattern;
|
|
@Component
|
public class PurchaseIntentExecutor {
|
|
private static final Pattern ID_PATTERN = Pattern.compile("\\b\\d{1,12}\\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 RELATIVE_RANGE_PATTERN = Pattern.compile("(近|最近)\\s*(\\d{1,3})\\s*(天|周|个月|月|年)");
|
private static final Pattern HALF_RANGE_PATTERN = Pattern.compile("(最近|近)?半(个)?(月|年)");
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
private static final ZoneId CHINA_ZONE_ID = ZoneId.of("Asia/Shanghai");
|
|
private final PurchaseAgentTools purchaseAgentTools;
|
|
public PurchaseIntentExecutor(PurchaseAgentTools purchaseAgentTools) {
|
this.purchaseAgentTools = purchaseAgentTools;
|
}
|
|
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;
|
}
|
|
String keyword = extractKeyword(text);
|
Integer limit = extractLimit(text);
|
DateRange dateRange = extractDateRange(text);
|
String startDate = dateRange.startDate();
|
String endDate = dateRange.endDate();
|
|
if (containsAny(text, "排行", "排名", "前几", "前五", "前十")
|
&& containsAny(text, "物料", "产品", "原材料", "采购金额", "金额")) {
|
return purchaseAgentTools.rankPurchaseMaterials(memoryId, startDate, endDate, text, limit);
|
}
|
if (containsAny(text, "未入库", "待入库", "没有入库", "还未入库")) {
|
return purchaseAgentTools.listUnstockedPurchaseOrders(memoryId, startDate, endDate, keyword, limit);
|
}
|
if (containsAny(text, "到货异常", "到货有异常", "异常到货", "到货问题", "供应商到货异常")) {
|
return purchaseAgentTools.listArrivalExceptions(memoryId, startDate, endDate, text, limit);
|
}
|
if (containsAny(text, "待付款", "未付款", "未付清", "待支付", "应付")) {
|
return purchaseAgentTools.listPendingPaymentOrders(memoryId, startDate, endDate, keyword, limit);
|
}
|
if (containsAny(text, "退货", "退料", "拒收")) {
|
return purchaseAgentTools.listPurchaseReturns(memoryId, startDate, endDate, keyword, limit);
|
}
|
if (isStatsIntent(text)) {
|
return purchaseAgentTools.getPurchaseStats(memoryId, startDate, endDate, text);
|
}
|
|
Long ledgerId = extractId(text);
|
if (containsAny(text, "详情", "明细") && ledgerId != null) {
|
return purchaseAgentTools.getPurchaseLedgerDetail(memoryId, ledgerId);
|
}
|
if (containsAny(text, "台账", "采购单", "采购订单", "订单", "合同", "列表", "查询")) {
|
return purchaseAgentTools.listPurchaseLedgers(memoryId, keyword, startDate, endDate, limit);
|
}
|
return null;
|
}
|
|
private String tryExecuteQuickPrompt(String memoryId, String text) {
|
String normalized = normalizeForMatch(text);
|
if ("本月采购金额排名前十的物料有哪些".equals(normalized)) {
|
return purchaseAgentTools.rankPurchaseMaterials(memoryId, null, null, "本月", 10);
|
}
|
if ("哪些采购订单还未入库".equals(normalized)) {
|
return purchaseAgentTools.listUnstockedPurchaseOrders(memoryId, null, null, null, 10);
|
}
|
if ("最近7天供应商到货异常有哪些".equals(normalized)) {
|
return purchaseAgentTools.listArrivalExceptions(memoryId, null, null, "最近7天", 10);
|
}
|
if ("帮我统计待付款采购单".equals(normalized)) {
|
return purchaseAgentTools.listPendingPaymentOrders(memoryId, null, null, null, 10);
|
}
|
if ("列出本月采购退货情况".equals(normalized)) {
|
return purchaseAgentTools.listPurchaseReturns(memoryId, null, null, null, 10);
|
}
|
return null;
|
}
|
|
private boolean isStatsIntent(String text) {
|
if (containsAny(text, "统计", "分析", "报表", "汇总", "趋势", "数据看板", "情况", "有多少")) {
|
return true;
|
}
|
boolean queryWord = containsAny(text, "查询", "查看", "看下", "看看", "获取");
|
boolean dataWord = containsAny(text, "数据", "金额", "数量", "合同额", "付款额", "发票额");
|
boolean timeWord = containsAny(text, "今天", "昨天", "本周", "上周", "本月", "上月", "今年", "去年", "近半年", "最近半个月", "半个月")
|
|| DATE_PATTERN.matcher(text).find()
|
|| RELATIVE_RANGE_PATTERN.matcher(text).find()
|
|| HALF_RANGE_PATTERN.matcher(text).find();
|
return queryWord && dataWord && timeWord;
|
}
|
|
private boolean containsAny(String text, String... keywords) {
|
for (String keyword : keywords) {
|
if (text.contains(keyword)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
private Long extractId(String text) {
|
Matcher matcher = ID_PATTERN.matcher(text);
|
if (!matcher.find()) {
|
return null;
|
}
|
return Long.parseLong(matcher.group());
|
}
|
|
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);
|
}
|
|
LocalDate today = LocalDate.now(CHINA_ZONE_ID);
|
if (text.contains("今天")) {
|
return new DateRange(formatDate(today), formatDate(today));
|
}
|
if (text.contains("昨天")) {
|
LocalDate yesterday = today.minusDays(1);
|
return new DateRange(formatDate(yesterday), formatDate(yesterday));
|
}
|
if (text.contains("本周")) {
|
LocalDate start = today.minusDays(today.getDayOfWeek().getValue() - 1L);
|
return new DateRange(formatDate(start), formatDate(today));
|
}
|
if (text.contains("上周")) {
|
LocalDate thisWeekStart = today.minusDays(today.getDayOfWeek().getValue() - 1L);
|
LocalDate start = thisWeekStart.minusWeeks(1);
|
LocalDate end = start.plusDays(6);
|
return new DateRange(formatDate(start), formatDate(end));
|
}
|
if (text.contains("本月")) {
|
return new DateRange(formatDate(today.withDayOfMonth(1)), formatDate(today));
|
}
|
if (text.contains("上月")) {
|
LocalDate start = today.minusMonths(1).withDayOfMonth(1);
|
return new DateRange(formatDate(start), formatDate(start.withDayOfMonth(start.lengthOfMonth())));
|
}
|
if (text.contains("今年") || text.contains("本年")) {
|
return new DateRange(formatDate(today.withDayOfYear(1)), formatDate(today));
|
}
|
if (text.contains("去年")) {
|
LocalDate start = today.minusYears(1).withDayOfYear(1);
|
LocalDate end = start.withDayOfYear(start.lengthOfYear());
|
return new DateRange(formatDate(start), formatDate(end));
|
}
|
if (containsAny(text, "近半年", "最近半年")) {
|
return new DateRange(formatDate(today.minusMonths(6).plusDays(1)), formatDate(today));
|
}
|
if (containsAny(text, "近半个月", "最近半个月", "半个月")) {
|
return new DateRange(formatDate(today.minusDays(14)), formatDate(today));
|
}
|
|
Matcher relativeMatcher = RELATIVE_RANGE_PATTERN.matcher(text);
|
if (relativeMatcher.find()) {
|
int amount = Integer.parseInt(relativeMatcher.group(2));
|
String unit = relativeMatcher.group(3);
|
LocalDate start = switch (unit) {
|
case "天" -> today.minusDays(Math.max(amount - 1L, 0));
|
case "周" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1);
|
case "个月", "月" -> today.minusMonths(Math.max(amount, 1)).plusDays(1);
|
case "年" -> today.minusYears(Math.max(amount, 1)).plusDays(1);
|
default -> today.minusDays(29);
|
};
|
return new DateRange(formatDate(start), formatDate(today));
|
}
|
|
return new DateRange(null, null);
|
}
|
|
private DateRange buildDateRange(String start, String end) {
|
LocalDate startDate = parseDate(start);
|
LocalDate endDate = parseDate(end);
|
if (startDate == null || endDate == null) {
|
return new DateRange(null, null);
|
}
|
if (startDate.isAfter(endDate)) {
|
LocalDate temp = startDate;
|
startDate = endDate;
|
endDate = temp;
|
}
|
return new DateRange(formatDate(startDate), formatDate(endDate));
|
}
|
|
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("列表", "")
|
.replace("哪些", "")
|
.replace("列出", "")
|
.replace("帮我", "")
|
.replace("统计", "")
|
.replace("分析", "")
|
.replace("本月", "")
|
.replace("上月", "")
|
.replace("本年", "")
|
.replace("今年", "")
|
.replace("去年", "")
|
.replace("本周", "")
|
.replace("上周", "")
|
.replace("今天", "")
|
.replace("昨天", "")
|
.replace("近30天", "")
|
.replace("近7天", "")
|
.replace("近15天", "")
|
.replace("近60天", "")
|
.replace("最近30天", "")
|
.replace("最近7天", "")
|
.replace("最近15天", "")
|
.replace("最近60天", "")
|
.replace("最近10条", "")
|
.replace("前10条", "")
|
.replace("前20条", "")
|
.replace("最近20条", "")
|
.trim();
|
return cleaned.length() >= 2 ? cleaned : null;
|
}
|
|
private record DateRange(String startDate, String endDate) {
|
}
|
}
|