From 1c518e10a50050d383e714b581c94dea58ec4d67 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期五, 22 五月 2026 18:01:34 +0800
Subject: [PATCH] fix(approve): 修复审批实例查询条件及流程控制问题

---
 src/main/java/com/ruoyi/ai/service/PurchaseAiService.java |  162 +++++++++++++++++++++++++++++++++++-------------------
 1 files changed, 105 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 85e174f..cbc936c 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,16 +18,20 @@
 import com.ruoyi.basic.service.StorageBlobService;
 import com.ruoyi.common.utils.StringUtils;
 import com.ruoyi.framework.security.LoginUser;
-import com.ruoyi.framework.web.domain.R;
+import com.ruoyi.framework.web.domain.AjaxResult;
 import com.ruoyi.purchase.dto.PurchaseLedgerDto;
 import com.ruoyi.purchase.dto.PurchaseReturnOrderDto;
-import com.ruoyi.purchase.pojo.PaymentRegistration;
-import com.ruoyi.purchase.service.IPaymentRegistrationService;
 import com.ruoyi.purchase.service.IPurchaseLedgerService;
 import com.ruoyi.purchase.service.PurchaseReturnOrdersService;
 import com.ruoyi.sales.pojo.SalesLedgerProduct;
 import dev.langchain4j.data.image.Image;
-import dev.langchain4j.data.message.*;
+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.model.chat.StreamingChatLanguageModel;
 import dev.langchain4j.model.chat.response.ChatResponse;
 import dev.langchain4j.model.chat.response.StreamingChatResponseHandler;
@@ -41,12 +46,21 @@
 import java.io.InputStream;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.nio.file.Files;
+import java.util.Base64;
 import java.time.LocalDate;
 import java.time.ZoneId;
 import java.time.format.DateTimeFormatter;
 import java.time.format.DateTimeParseException;
-import java.util.*;
+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;
 
 @Service
 public class PurchaseAiService {
@@ -66,7 +80,6 @@
     private final AiFileTextExtractor aiFileTextExtractor;
     private final ObjectMapper objectMapper;
     private final IPurchaseLedgerService purchaseLedgerService;
-    private final IPaymentRegistrationService paymentRegistrationService;
     private final PurchaseReturnOrdersService purchaseReturnOrdersService;
     private final StorageBlobService storageBlobService;
     private final SupplierManageMapper supplierManageMapper;
@@ -80,7 +93,6 @@
                                 AiFileTextExtractor aiFileTextExtractor,
                                  ObjectMapper objectMapper,
                                  IPurchaseLedgerService purchaseLedgerService,
-                                 IPaymentRegistrationService paymentRegistrationService,
                                  PurchaseReturnOrdersService purchaseReturnOrdersService,
                                  StorageBlobService storageBlobService,
                                  SupplierManageMapper supplierManageMapper,
@@ -93,7 +105,6 @@
         this.aiFileTextExtractor = aiFileTextExtractor;
         this.objectMapper = objectMapper;
         this.purchaseLedgerService = purchaseLedgerService;
-        this.paymentRegistrationService = paymentRegistrationService;
         this.purchaseReturnOrdersService = purchaseReturnOrdersService;
         this.storageBlobService = storageBlobService;
         this.supplierManageMapper = supplierManageMapper;
@@ -122,6 +133,16 @@
             );
             aiChatSessionService.refreshSessionStats(memoryId, loginUser);
             return Flux.just(directResponse);
+        }
+
+        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())
@@ -195,24 +216,23 @@
                 .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser));
     }
 
-    public R confirmAnalyzeResult(PurchaseAiConfirmRequest request) {
+    public AjaxResult confirmAnalyzeResult(PurchaseAiConfirmRequest request) {
         if (request == null || !StringUtils.hasText(request.getBusinessType())) {
-            return R.fail("businessType涓嶈兘涓虹┖");
+            return AjaxResult.error("businessType涓嶈兘涓虹┖");
         }
         if (request.getPayload() == null || request.getPayload().isEmpty()) {
-            return R.fail("payload涓嶈兘涓虹┖");
+            return AjaxResult.error("payload涓嶈兘涓虹┖");
         }
 
         try {
             String businessType = request.getBusinessType().trim();
             return switch (businessType) {
                 case "purchase_ledger" -> processPurchaseLedger(request.getPayload());
-                case "payment_registration" -> processPaymentRegistration(request.getPayload());
                 case "purchase_return_order" -> processPurchaseReturnOrder(request.getPayload());
-                default -> R.fail("鏆備笉鏀寔璇ヤ笟鍔$被鍨�: " + businessType);
+                default -> AjaxResult.error("鏆備笉鏀寔璇ヤ笟鍔$被鍨�: " + businessType);
             };
         } catch (Exception ex) {
-            return R.fail(toCustomerMessage(ex));
+            return AjaxResult.error(toCustomerMessage(ex));
         }
     }
 
@@ -461,6 +481,47 @@
         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 """
                 浣犳槸閲囪喘涓氬姟鏂囦欢鍒嗘瀽鍔╂墜銆傝涓ユ牸鏍规嵁鐢ㄦ埛涓婁紶鐨勫涓枃浠跺拰鐢ㄦ埛瑕佹眰鎻愬彇閲囪喘涓氬姟鏁版嵁銆�
@@ -471,8 +532,8 @@
                 杈撳嚭瑕佹眰:
                 1. 鍙緭鍑哄悎娉� JSON锛屼笉瑕� Markdown锛屼笉瑕侀澶栬В閲娿��
                 2. JSON 椤跺眰瀛楁鍥哄畾涓�:
-                   - ok: boolean
-                   - businessType: purchase_ledger | payment_registration | purchase_return_order | unknown
+                   - success: boolean
+                   - businessType: purchase_ledger  | purchase_return_order | unknown
                    - action: confirm_required
                    - description: 涓枃璇存槑
                    - confidence: 0鍒�1鐨勫皬鏁�
@@ -497,7 +558,7 @@
                      entryDateStart, entryDateEnd, id, purchaseContractNumber, supplierId, supplierName, isWhite, recorderId, recorderName, salesContractNo, salesContractNoId, projectName, entryDate, executionDate, remarks, attachmentMaterials, createdAt, updatedAt, salesLedgerId, hasChildren, Type, productData, tempFileIds, SalesLedgerFiles, phoneNumber, businessPersonId, productId, productModelId, invoiceNumber, invoiceAmount, ticketRegistrationId, contractAmount, receiptPaymentAmount, unReceiptPaymentAmount, type, paymentMethod, approvalStatus, templateName
                    - productData 姣忔潯浜у搧鍙娇鐢ㄨ繖浜� SalesLedgerProduct 瀛楁鍚�:
                      productCategory, specificationModel, unit, quantity, taxRate, taxInclusiveUnitPrice, taxInclusiveTotalPrice, taxExclusiveTotalPrice, invoiceType, productId, productModelId, isChecked, type
-                4. 濡傛灉鍙垽鏂负浠樻鐧昏锛宐usinessType 浣跨敤 payment_registration锛宲ayload.records 涓轰粯娆剧櫥璁版暟缁勶紝瀛楁灏介噺鍖呭惈 purchaseLedgerId銆乻alesLedgerProductId銆乧urrentPaymentAmount銆乸aymentMethod銆乸aymentDate銆�
+                4. 濡傛灉鍙垽鏂负浠樻鐧昏锛宐usinessType 浣跨敤 payload.records 涓轰粯娆剧櫥璁版暟缁勶紝瀛楁灏介噺鍖呭惈 purchaseLedgerId銆乻alesLedgerProductId銆乧urrentPaymentAmount銆乸aymentMethod銆乸aymentDate銆�
                 5. 濡傛灉鍙垽鏂负閲囪喘閫�璐э紝businessType 浣跨敤 purchase_return_order锛宲ayload 鎸� PurchaseReturnOrderDto 缁勭粐锛屾槑缁嗘斁 purchaseReturnOrderProductsDtos銆�
                 6. 缂哄皯涓氬姟澶勭悊蹇呴』瀛楁鏃讹紝涓嶈缂栭�� ID锛屾妸瀛楁鏀惧叆 missingFields锛屽苟浠嶈繑鍥炲彲纭鐨勮崏绋挎暟鎹��
                 7. 鎵�鏈変腑鏂囧唴瀹圭洿鎺ヤ繚鐣欙紝涓嶈杞箟鎴� Unicode銆�
@@ -507,33 +568,33 @@
                 """.formatted(message, fileContent);
     }
 
-    private R processPurchaseLedger(Map<String, Object> payload) throws Exception {
+    private AjaxResult 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);
-        R ledgerResult = validatePurchaseLedger(dto, 0);
+        AjaxResult ledgerResult = validatePurchaseLedger(dto, 0);
         if (ledgerResult != null) {
             return ledgerResult;
         }
-        R supplierResult = fillSupplierIdByName(dto);
+        AjaxResult supplierResult = fillSupplierIdByName(dto);
         if (supplierResult != null) {
             return supplierResult;
         }
-        R productResult = validatePurchaseProducts(dto.getProductData(), 0);
+        AjaxResult productResult = validatePurchaseProducts(dto.getProductData(), 0);
         if (productResult != null) {
             return productResult;
         }
         int result = purchaseLedgerService.addOrEditPurchase(dto);
-        return R.ok( result,"閲囪喘鍙拌处宸插鐞�");
+        return AjaxResult.success("閲囪喘鍙拌处宸插鐞�", result);
     }
 
-    private R processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception {
+    private AjaxResult processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception {
         List<Map<String, Object>> purchaseLedgers = toMapList(payload.get("purchaseLedgers"));
         if (purchaseLedgers.isEmpty()) {
-            return R.fail("purchaseLedgers涓嶈兘涓虹┖");
+            return AjaxResult.error("purchaseLedgers涓嶈兘涓虹┖");
         }
 
         List<Map<String, Object>> topLevelProductData = toMapList(payload.get("productData"));
@@ -541,11 +602,11 @@
         for (int i = 0; i < purchaseLedgers.size(); i++) {
             Map<String, Object> ledgerMap = normalizePurchaseLedgerMap(purchaseLedgers.get(i));
             PurchaseLedgerDto dto = objectMapper.convertValue(ledgerMap, PurchaseLedgerDto.class);
-            R ledgerResult = validatePurchaseLedger(dto, i);
+            AjaxResult ledgerResult = validatePurchaseLedger(dto, i);
             if (ledgerResult != null) {
                 return ledgerResult;
             }
-            R supplierResult = fillSupplierIdByName(dto);
+            AjaxResult supplierResult = fillSupplierIdByName(dto);
             if (supplierResult != null) {
                 return supplierResult;
             }
@@ -555,7 +616,7 @@
                 products = matchProductsForLedger(ledgerMap, dto, topLevelProductData, purchaseLedgers.size() == 1);
                 dto.setProductData(products);
             }
-            R productResult = validatePurchaseProducts(products, i);
+            AjaxResult productResult = validatePurchaseProducts(products, i);
             if (productResult != null) {
                 return productResult;
             }
@@ -570,7 +631,7 @@
             item.put("result", result);
             results.add(item);
         }
-        return R.ok( results,"閲囪喘鍙拌处宸叉壒閲忓鐞�");
+        return AjaxResult.success("閲囪喘鍙拌处宸叉壒閲忓鐞�", results);
     }
 
     private List<SalesLedgerProduct> matchProductsForLedger(Map<String, Object> ledgerMap,
@@ -776,7 +837,7 @@
         }
     }
 
-    private R validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) {
+    private AjaxResult validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) {
         if (products == null || products.isEmpty()) {
             return null;
         }
@@ -784,34 +845,34 @@
             SalesLedgerProduct product = products.get(i);
             String prefix = "绗�" + (ledgerIndex + 1) + "涓噰璐彴璐︾殑绗�" + (i + 1) + "鏉′骇鍝�";
             if (!StringUtils.hasText(product.getProductCategory())) {
-                return R.fail(prefix + "缂哄皯浜у搧鍚嶇О锛岃琛ュ厖鍚庡啀纭");
+                return AjaxResult.error(prefix + "缂哄皯浜у搧鍚嶇О锛岃琛ュ厖鍚庡啀纭");
             }
             if (!StringUtils.hasText(product.getSpecificationModel())) {
-                return R.fail(prefix + "缂哄皯瑙勬牸鍨嬪彿锛岃琛ュ厖鍚庡啀纭");
+                return AjaxResult.error(prefix + "缂哄皯瑙勬牸鍨嬪彿锛岃琛ュ厖鍚庡啀纭");
             }
             if (!StringUtils.hasText(product.getUnit())) {
-                return R.fail(prefix + "缂哄皯鍗曚綅锛岃琛ュ厖鍚庡啀纭");
+                return AjaxResult.error(prefix + "缂哄皯鍗曚綅锛岃琛ュ厖鍚庡啀纭");
             }
             if (product.getQuantity() == null) {
-                return R.fail(prefix + "缂哄皯鏁伴噺");
+                return AjaxResult.error(prefix + "缂哄皯鏁伴噺");
             }
             if (product.getTaxInclusiveUnitPrice() == null) {
-                return R.fail(prefix + "缂哄皯鍚◣鍗曚环锛岃琛ュ厖鍚庡啀纭");
+                return AjaxResult.error(prefix + "缂哄皯鍚◣鍗曚环锛岃琛ュ厖鍚庡啀纭");
             }
             if (product.getTaxInclusiveTotalPrice() == null) {
-                return R.fail(prefix + "缂哄皯鍚◣鎬讳环锛岃琛ュ厖鍚庡啀纭");
+                return AjaxResult.error(prefix + "缂哄皯鍚◣鎬讳环锛岃琛ュ厖鍚庡啀纭");
             }
         }
         return null;
     }
 
-    private R validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) {
+    private AjaxResult validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) {
         String prefix = "绗�" + (ledgerIndex + 1) + "涓噰璐彴璐�";
         if (!StringUtils.hasText(dto.getPurchaseContractNumber())) {
-            return R.fail(prefix + "缂哄皯閲囪喘鍚堝悓鍙凤紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
+            return AjaxResult.error(prefix + "缂哄皯閲囪喘鍚堝悓鍙凤紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
         }
         if (dto.getSupplierId() == null && !StringUtils.hasText(dto.getSupplierName())) {
-            return R.fail(prefix + "缂哄皯渚涘簲鍟嗗悕绉帮紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
+            return AjaxResult.error(prefix + "缂哄皯渚涘簲鍟嗗悕绉帮紝璇疯ˉ鍏呭悗鍐嶇‘璁�");
         }
         return null;
     }
@@ -982,40 +1043,27 @@
         return "澶勭悊澶辫触锛�" + message;
     }
 
-    private R fillSupplierIdByName(PurchaseLedgerDto dto) {
+    private AjaxResult fillSupplierIdByName(PurchaseLedgerDto dto) {
         if (dto.getSupplierId() != null) {
             return null;
         }
         if (!StringUtils.hasText(dto.getSupplierName())) {
-            return R.fail("渚涘簲鍟咺D涓嶈兘涓虹┖锛涙湭璇嗗埆鍒颁緵搴斿晢鍚嶇О锛屾棤娉曡嚜鍔ㄥ尮閰嶄緵搴斿晢ID");
+            return AjaxResult.error("渚涘簲鍟咺D涓嶈兘涓虹┖锛涙湭璇嗗埆鍒颁緵搴斿晢鍚嶇О锛屾棤娉曡嚜鍔ㄥ尮閰嶄緵搴斿晢ID");
         }
 
         SupplierManage supplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>()
                 .eq(SupplierManage::getSupplierName, dto.getSupplierName().trim())
                 .last("limit 1"));
         if (supplier == null) {
-            return R.fail("鏈壘鍒颁緵搴斿晢锛�" + dto.getSupplierName() + "锛岃鍏堢淮鎶や緵搴斿晢鎴栨墜鍔ㄩ�夋嫨渚涘簲鍟咺D");
+            return AjaxResult.error("鏈壘鍒颁緵搴斿晢锛�" + dto.getSupplierName() + "锛岃鍏堢淮鎶や緵搴斿晢鎴栨墜鍔ㄩ�夋嫨渚涘簲鍟咺D");
         }
         dto.setSupplierId(supplier.getId());
         return null;
     }
 
-    private R processPaymentRegistration(Map<String, Object> payload) {
-        Object recordsValue = payload.get("records");
-        List<PaymentRegistration> records;
-        if (recordsValue == null) {
-            records = Collections.singletonList(objectMapper.convertValue(payload, PaymentRegistration.class));
-        } else {
-            records = objectMapper.convertValue(recordsValue, new TypeReference<List<PaymentRegistration>>() {
-            });
-        }
-        int result = paymentRegistrationService.insertPaymentRegistration(records);
-        return R.ok( result,"浠樻鐧昏宸插鐞�");
-    }
-
-    private R processPurchaseReturnOrder(Map<String, Object> payload) {
+    private AjaxResult processPurchaseReturnOrder(Map<String, Object> payload) {
         PurchaseReturnOrderDto dto = objectMapper.convertValue(payload, PurchaseReturnOrderDto.class);
         Boolean result = purchaseReturnOrdersService.add(dto);
-        return R.ok( result,"閲囪喘閫�璐у崟宸插鐞�");
+        return AjaxResult.success("閲囪喘閫�璐у崟宸插鐞�", result);
     }
 }

--
Gitblit v1.9.3