From 0d7d874912d0147376826b55667a1deb6547ed91 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 21 五月 2026 15:25:27 +0800
Subject: [PATCH] Merge branch 'dev_New_pro' into dev_宁夏_英泽防锈
---
src/main/java/com/ruoyi/ai/service/PurchaseAiService.java | 156 +++++++++++++++++++++++++++++++++-------------------
1 files changed, 99 insertions(+), 57 deletions(-)
diff --git a/src/main/java/com/ruoyi/ai/service/PurchaseAiService.java b/src/main/java/com/ruoyi/ai/service/PurchaseAiService.java
index 0f24d64..a4a0dc7 100644
--- a/src/main/java/com/ruoyi/ai/service/PurchaseAiService.java
+++ b/src/main/java/com/ruoyi/ai/service/PurchaseAiService.java
@@ -1,5 +1,6 @@
package com.ruoyi.ai.service;
+import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
@@ -17,7 +18,7 @@
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.LoginUser;
-import com.ruoyi.framework.web.domain.AjaxResult;
+import com.ruoyi.framework.web.domain.R;
import com.ruoyi.purchase.dto.PurchaseLedgerDto;
import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
import com.ruoyi.purchase.pojo.PaymentRegistration;
@@ -26,13 +27,7 @@
import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
import dev.langchain4j.data.image.Image;
-import dev.langchain4j.data.message.AiMessage;
-import dev.langchain4j.data.message.ChatMessage;
-import dev.langchain4j.data.message.Content;
-import dev.langchain4j.data.message.ImageContent;
-import dev.langchain4j.data.message.SystemMessage;
-import dev.langchain4j.data.message.TextContent;
-import dev.langchain4j.data.message.UserMessage;
+import dev.langchain4j.data.message.*;
import dev.langchain4j.model.chat.StreamingChatLanguageModel;
import dev.langchain4j.model.chat.response.ChatResponse;
import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
@@ -47,22 +42,12 @@
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.RoundingMode;
-import java.util.Base64;
-import java.util.Arrays;
+import java.nio.file.Files;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.UUID;
-import java.nio.file.Files;
+import java.util.*;
@Service
public class PurchaseAiService {
@@ -71,6 +56,8 @@
private static final int MAX_FILE_COUNT = 10;
private static final int MAX_SINGLE_FILE_TEXT_LENGTH = 8000;
private static final int MAX_TOTAL_FILE_TEXT_LENGTH = 30000;
+ private static final DateTimeFormatter CURRENT_DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ private static final ZoneId CHINA_ZONE_ID = ZoneId.of("Asia/Shanghai");
private final PurchaseAgent purchaseAgent;
private final PurchaseIntentExecutor purchaseIntentExecutor;
@@ -138,7 +125,17 @@
return Flux.just(directResponse);
}
- return purchaseAgent.chat(memoryId, userMessage)
+ if (isPurchaseBusinessIntent(userMessage)) {
+ String noGuessResponse = buildNoGuessResponse();
+ mongoChatMemoryStore.appendMessages(
+ memoryId,
+ List.of(UserMessage.from(userMessage), AiMessage.from(noGuessResponse))
+ );
+ aiChatSessionService.refreshSessionStats(memoryId, loginUser);
+ return Flux.just(noGuessResponse);
+ }
+
+ return purchaseAgent.chat(memoryId, userMessage, currentDateForPrompt())
.doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser))
.doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser));
}
@@ -200,21 +197,21 @@
.doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser));
}
- return Flux.defer(() -> purchaseAgent.chat(finalMemoryId, userPrompt))
+ return Flux.defer(() -> purchaseAgent.chat(finalMemoryId, userPrompt, currentDateForPrompt()))
.onErrorResume(NoSuchElementException.class, ex -> {
mongoChatMemoryStore.deleteMessages(finalMemoryId);
- return purchaseAgent.chat(finalMemoryId, userPrompt);
+ return purchaseAgent.chat(finalMemoryId, userPrompt, currentDateForPrompt());
})
.doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser))
.doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser));
}
- public AjaxResult confirmAnalyzeResult(PurchaseAiConfirmRequest request) {
+ public R confirmAnalyzeResult(PurchaseAiConfirmRequest request) {
if (request == null || !StringUtils.hasText(request.getBusinessType())) {
- return AjaxResult.error("businessType涓嶈兘涓虹┖");
+ return R.fail("businessType涓嶈兘涓虹┖");
}
if (request.getPayload() == null || request.getPayload().isEmpty()) {
- return AjaxResult.error("payload涓嶈兘涓虹┖");
+ return R.fail("payload涓嶈兘涓虹┖");
}
try {
@@ -223,10 +220,10 @@
case "purchase_ledger" -> processPurchaseLedger(request.getPayload());
case "payment_registration" -> processPaymentRegistration(request.getPayload());
case "purchase_return_order" -> processPurchaseReturnOrder(request.getPayload());
- default -> AjaxResult.error("鏆備笉鏀寔璇ヤ笟鍔$被鍨�: " + businessType);
+ default -> R.fail("鏆備笉鏀寔璇ヤ笟鍔$被鍨�: " + businessType);
};
} catch (Exception ex) {
- return AjaxResult.error(toCustomerMessage(ex));
+ return R.fail(toCustomerMessage(ex));
}
}
@@ -471,6 +468,51 @@
};
}
+ private String currentDateForPrompt() {
+ return LocalDate.now(CHINA_ZONE_ID).format(CURRENT_DATE_FMT);
+ }
+
+ private boolean isPurchaseBusinessIntent(String message) {
+ if (!StringUtils.hasText(message)) {
+ return false;
+ }
+ String text = message.trim();
+ boolean hasDomainWord = containsAny(text,
+ "閲囪喘", "閲囪喘鍙拌处", "閲囪喘鍗�", "閲囪喘璁㈠崟", "渚涘簲鍟�", "鐗╂枡", "鍏ュ簱", "鍒拌揣", "寰呬粯娆�",
+ "浠樻", "閫�璐�", "閫�鏂�", "鍙戠エ", "鍚堝悓");
+ boolean hasIntentWord = containsAny(text,
+ "鏌ヨ", "鏌ョ湅", "缁熻", "鍒嗘瀽", "鎺掕", "鎺掑悕", "鍒楀嚭", "鏈夊摢浜�", "鎯呭喌", "鏄庣粏", "璇︽儏", "鎶ヨ〃");
+ return hasDomainWord && hasIntentWord;
+ }
+
+ private boolean containsAny(String text, String... keywords) {
+ for (String keyword : keywords) {
+ if (text.contains(keyword)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private String buildNoGuessResponse() {
+ Map<String, Object> result = new LinkedHashMap<>();
+ result.put("success", false);
+ result.put("type", "purchase_intent_not_recognized");
+ result.put("description", "鏈瘑鍒埌鍙墽琛岀殑閲囪喘鏌ヨ鏉′欢銆備负淇濊瘉缁撴灉鍑嗙‘锛屽綋鍓嶄笉浼氭帹娴嬫垨缂栭�犳暟鎹紝璇疯ˉ鍏呮槑纭椂闂磋寖鍥淬�佷緵搴斿晢銆侀噰璐悎鍚屽彿鎴栫墿鏂欏悗鍐嶆煡璇€��");
+ result.put("summary", Map.of());
+ result.put("data", Map.of(
+ "quickPrompts", List.of(
+ "鏈湀閲囪喘閲戦鎺掑悕鍓嶅崄鐨勭墿鏂欐湁鍝簺锛�",
+ "鍝簺閲囪喘璁㈠崟杩樻湭鍏ュ簱锛�",
+ "鏈�杩�7澶╀緵搴斿晢鍒拌揣寮傚父鏈夊摢浜涳紵",
+ "甯垜缁熻寰呬粯娆鹃噰璐崟锛�",
+ "鍒楀嚭鏈湀閲囪喘閫�璐ф儏鍐�"
+ )
+ ));
+ result.put("charts", Map.of());
+ return JSON.toJSONString(result);
+ }
+
private String buildPurchaseFileAnalyzePrompt(String message, String fileContent) {
return """
浣犳槸閲囪喘涓氬姟鏂囦欢鍒嗘瀽鍔╂墜銆傝涓ユ牸鏍规嵁鐢ㄦ埛涓婁紶鐨勫涓枃浠跺拰鐢ㄦ埛瑕佹眰鎻愬彇閲囪喘涓氬姟鏁版嵁銆�
@@ -481,7 +523,7 @@
杈撳嚭瑕佹眰:
1. 鍙緭鍑哄悎娉� JSON锛屼笉瑕� Markdown锛屼笉瑕侀澶栬В閲娿��
2. JSON 椤跺眰瀛楁鍥哄畾涓�:
- - success: boolean
+ - ok: boolean
- businessType: purchase_ledger | payment_registration | purchase_return_order | unknown
- action: confirm_required
- description: 涓枃璇存槑
@@ -517,33 +559,33 @@
""".formatted(message, fileContent);
}
- private AjaxResult processPurchaseLedger(Map<String, Object> payload) throws Exception {
+ private R processPurchaseLedger(Map<String, Object> payload) throws Exception {
if (payload.containsKey("purchaseLedgers")) {
return processPurchaseLedgerBatch(payload);
}
Map<String, Object> normalizedPayload = normalizePurchaseLedgerMap(payload);
PurchaseLedgerDto dto = objectMapper.convertValue(normalizedPayload, PurchaseLedgerDto.class);
- AjaxResult ledgerResult = validatePurchaseLedger(dto, 0);
+ R ledgerResult = validatePurchaseLedger(dto, 0);
if (ledgerResult != null) {
return ledgerResult;
}
- AjaxResult supplierResult = fillSupplierIdByName(dto);
+ R supplierResult = fillSupplierIdByName(dto);
if (supplierResult != null) {
return supplierResult;
}
- AjaxResult productResult = validatePurchaseProducts(dto.getProductData(), 0);
+ R productResult = validatePurchaseProducts(dto.getProductData(), 0);
if (productResult != null) {
return productResult;
}
int result = purchaseLedgerService.addOrEditPurchase(dto);
- return AjaxResult.success("閲囪喘鍙拌处宸插鐞�", result);
+ return R.ok( result,"閲囪喘鍙拌处宸插鐞�");
}
- private AjaxResult processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception {
+ private R processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception {
List<Map<String, Object>> purchaseLedgers = toMapList(payload.get("purchaseLedgers"));
if (purchaseLedgers.isEmpty()) {
- return AjaxResult.error("purchaseLedgers涓嶈兘涓虹┖");
+ return R.fail("purchaseLedgers涓嶈兘涓虹┖");
}
List<Map<String, Object>> topLevelProductData = toMapList(payload.get("productData"));
@@ -551,11 +593,11 @@
for (int i = 0; i < purchaseLedgers.size(); i++) {
Map<String, Object> ledgerMap = normalizePurchaseLedgerMap(purchaseLedgers.get(i));
PurchaseLedgerDto dto = objectMapper.convertValue(ledgerMap, PurchaseLedgerDto.class);
- AjaxResult ledgerResult = validatePurchaseLedger(dto, i);
+ R ledgerResult = validatePurchaseLedger(dto, i);
if (ledgerResult != null) {
return ledgerResult;
}
- AjaxResult supplierResult = fillSupplierIdByName(dto);
+ R supplierResult = fillSupplierIdByName(dto);
if (supplierResult != null) {
return supplierResult;
}
@@ -565,7 +607,7 @@
products = matchProductsForLedger(ledgerMap, dto, topLevelProductData, purchaseLedgers.size() == 1);
dto.setProductData(products);
}
- AjaxResult productResult = validatePurchaseProducts(products, i);
+ R productResult = validatePurchaseProducts(products, i);
if (productResult != null) {
return productResult;
}
@@ -580,7 +622,7 @@
item.put("result", result);
results.add(item);
}
- return AjaxResult.success("閲囪喘鍙拌处宸叉壒閲忓鐞�", results);
+ return R.ok( results,"閲囪喘鍙拌处宸叉壒閲忓鐞�");
}
private List<SalesLedgerProduct> matchProductsForLedger(Map<String, Object> ledgerMap,
@@ -786,7 +828,7 @@
}
}
- private AjaxResult validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) {
+ private R validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) {
if (products == null || products.isEmpty()) {
return null;
}
@@ -794,34 +836,34 @@
SalesLedgerProduct product = products.get(i);
String prefix = "绗�" + (ledgerIndex + 1) + "涓噰璐彴璐︾殑绗�" + (i + 1) + "鏉′骇鍝�";
if (!StringUtils.hasText(product.getProductCategory())) {
- return AjaxResult.error(prefix + "缂哄皯浜у搧鍚嶇О锛岃琛ュ厖鍚庡啀纭");
+ return R.fail(prefix + "缂哄皯浜у搧鍚嶇О锛岃琛ュ厖鍚庡啀纭");
}
if (!StringUtils.hasText(product.getSpecificationModel())) {
- return AjaxResult.error(prefix + "缂哄皯瑙勬牸鍨嬪彿锛岃琛ュ厖鍚庡啀纭");
+ return R.fail(prefix + "缂哄皯瑙勬牸鍨嬪彿锛岃琛ュ厖鍚庡啀纭");
}
if (!StringUtils.hasText(product.getUnit())) {
- return AjaxResult.error(prefix + "缂哄皯鍗曚綅锛岃琛ュ厖鍚庡啀纭");
+ return R.fail(prefix + "缂哄皯鍗曚綅锛岃琛ュ厖鍚庡啀纭");
}
if (product.getQuantity() == null) {
- return AjaxResult.error(prefix + "缂哄皯鏁伴噺");
+ return R.fail(prefix + "缂哄皯鏁伴噺");
}
if (product.getTaxInclusiveUnitPrice() == null) {
- return AjaxResult.error(prefix + "缂哄皯鍚◣鍗曚环锛岃琛ュ厖鍚庡啀纭");
+ return R.fail(prefix + "缂哄皯鍚◣鍗曚环锛岃琛ュ厖鍚庡啀纭");
}
if (product.getTaxInclusiveTotalPrice() == null) {
- return AjaxResult.error(prefix + "缂哄皯鍚◣鎬讳环锛岃琛ュ厖鍚庡啀纭");
+ return R.fail(prefix + "缂哄皯鍚◣鎬讳环锛岃琛ュ厖鍚庡啀纭");
}
}
return null;
}
- private AjaxResult validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) {
+ private R validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) {
String prefix = "绗�" + (ledgerIndex + 1) + "涓噰璐彴璐�";
if (!StringUtils.hasText(dto.getPurchaseContractNumber())) {
- return AjaxResult.error(prefix + "缂哄皯閲囪喘鍚堝悓鍙凤紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
+ return R.fail(prefix + "缂哄皯閲囪喘鍚堝悓鍙凤紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
}
if (dto.getSupplierId() == null && !StringUtils.hasText(dto.getSupplierName())) {
- return AjaxResult.error(prefix + "缂哄皯渚涘簲鍟嗗悕绉帮紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
+ return R.fail(prefix + "缂哄皯渚涘簲鍟嗗悕绉帮紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
}
return null;
}
@@ -992,25 +1034,25 @@
return "澶勭悊澶辫触锛�" + message;
}
- private AjaxResult fillSupplierIdByName(PurchaseLedgerDto dto) {
+ private R fillSupplierIdByName(PurchaseLedgerDto dto) {
if (dto.getSupplierId() != null) {
return null;
}
if (!StringUtils.hasText(dto.getSupplierName())) {
- return AjaxResult.error("渚涘簲鍟咺D涓嶈兘涓虹┖锛涙湭璇嗗埆鍒颁緵搴斿晢鍚嶇О锛屾棤娉曡嚜鍔ㄥ尮閰嶄緵搴斿晢ID");
+ return R.fail("渚涘簲鍟咺D涓嶈兘涓虹┖锛涙湭璇嗗埆鍒颁緵搴斿晢鍚嶇О锛屾棤娉曡嚜鍔ㄥ尮閰嶄緵搴斿晢ID");
}
SupplierManage supplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>()
.eq(SupplierManage::getSupplierName, dto.getSupplierName().trim())
.last("limit 1"));
if (supplier == null) {
- return AjaxResult.error("鏈壘鍒颁緵搴斿晢锛�" + dto.getSupplierName() + "锛岃鍏堢淮鎶や緵搴斿晢鎴栨墜鍔ㄩ�夋嫨渚涘簲鍟咺D");
+ return R.fail("鏈壘鍒颁緵搴斿晢锛�" + dto.getSupplierName() + "锛岃鍏堢淮鎶や緵搴斿晢鎴栨墜鍔ㄩ�夋嫨渚涘簲鍟咺D");
}
dto.setSupplierId(supplier.getId());
return null;
}
- private AjaxResult processPaymentRegistration(Map<String, Object> payload) {
+ private R processPaymentRegistration(Map<String, Object> payload) {
Object recordsValue = payload.get("records");
List<PaymentRegistration> records;
if (recordsValue == null) {
@@ -1020,12 +1062,12 @@
});
}
int result = paymentRegistrationService.insertPaymentRegistration(records);
- return AjaxResult.success("浠樻鐧昏宸插鐞�", result);
+ return R.ok( result,"浠樻鐧昏宸插鐞�");
}
- private AjaxResult processPurchaseReturnOrder(Map<String, Object> payload) {
+ private R processPurchaseReturnOrder(Map<String, Object> payload) {
PurchaseReturnOrderDto dto = objectMapper.convertValue(payload, PurchaseReturnOrderDto.class);
Boolean result = purchaseReturnOrdersService.add(dto);
- return AjaxResult.success("閲囪喘閫�璐у崟宸插鐞�", result);
+ return R.ok( result,"閲囪喘閫�璐у崟宸插鐞�");
}
}
--
Gitblit v1.9.3