From 620bb4712a31791231c4381581f0f60088f079fe Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期三, 27 五月 2026 14:03:45 +0800
Subject: [PATCH] Merge branch 'refs/heads/dev_New_pro' into dev_宁夏_英泽防锈

---
 src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java |   53 ++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 48 insertions(+), 5 deletions(-)

diff --git a/src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java b/src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java
index 082b2cd..e907aff 100644
--- a/src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/FinancialAgentTools.java
@@ -88,7 +88,14 @@
     private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
     private static final int DEFAULT_LIMIT = 10;
     private static final int MAX_LIMIT = 50;
-    private static final BigDecimal DEFAULT_FALLBACK_MATERIAL_COST_RATE = new BigDecimal("0.65");
+
+    private static final BigDecimal DEFAULT_FALLBACK_MATERIAL_COST_RATE = new BigDecimal("0.60");
+    private static final BigDecimal DEFAULT_LABOR_COST_RATE = new BigDecimal("0.15");
+    private static final BigDecimal DEFAULT_OVERHEAD_COST_RATE = new BigDecimal("0.10");
+
+    private static final BigDecimal SME_RECEIVABLE_RISK_THRESHOLD = new BigDecimal("500000");
+    private static final BigDecimal SME_INVENTORY_RISK_THRESHOLD = new BigDecimal("1000000");
+    private static final BigDecimal SME_PROFIT_WARNING_RATE = new BigDecimal("0.08");
 
     private final SalesLedgerMapper salesLedgerMapper;
     private final SalesLedgerProductMapper salesLedgerProductMapper;
@@ -194,6 +201,10 @@
                                            @P(value = "鍏抽敭璇嶏紝鍙尮閰嶅悎鍚屽彿/瀹㈡埛/椤圭洰", required = false) String keyword,
                                            @P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�50", required = false) Integer limit) {
         LoginUser loginUser = currentLoginUser(memoryId);
+        if (loginUser == null) {
+            return jsonResponse(false, "financial_cost_accounting", "鐢ㄦ埛淇℃伅鑾峰彇澶辫触", Map.of(), Map.of(), Map.of());
+        }
+
         DateRange range = resolveDateRange(startDate, endDate, timeRange, "杩�30澶�");
         AnalysisBundle bundle = buildOrderProfitBundle(loginUser, range, keyword, limit);
 
@@ -248,12 +259,16 @@
                                      @P(value = "鍏抽敭璇嶏紝鍙尮閰嶅悎鍚屽彿/瀹㈡埛/椤圭洰", required = false) String keyword,
                                      @P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�50", required = false) Integer limit) {
         LoginUser loginUser = currentLoginUser(memoryId);
+        if (loginUser == null) {
+            return jsonResponse(false, "financial_order_profit_analysis", "鐢ㄦ埛淇℃伅鑾峰彇澶辫触", Map.of(), Map.of(), Map.of());
+        }
+
         DateRange range = resolveDateRange(startDate, endDate, timeRange, "杩�30澶�");
         AnalysisBundle bundle = buildOrderProfitBundle(loginUser, range, keyword, limit);
         List<OrderProfitMetric> metrics = bundle.orderMetrics();
 
         List<OrderProfitMetric> riskyOrders = metrics.stream()
-                .filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0 || item.profitRate().compareTo(new BigDecimal("0.08")) < 0)
+                .filter(item -> item.profit().compareTo(BigDecimal.ZERO) < 0 || item.profitRate().compareTo(SME_PROFIT_WARNING_RATE) < 0)
                 .sorted(Comparator.comparing(OrderProfitMetric::profitRate)
                         .thenComparing(OrderProfitMetric::profit))
                 .toList();
@@ -594,10 +609,10 @@
         if (lossCount > 0) {
             riskSuggestions.add(riskSuggestion("鍒╂鼎椋庨櫓", "楂�", "澶嶆牳浜忔崯璁㈠崟BOM鍜屽伐搴忓伐璧勫畾棰濓紝蹇呰鏃惰皟鏁存姤浠蜂笌浜や粯鑺傚銆�"));
         }
-        if (snapshot.receivableTotal().compareTo(new BigDecimal("1000000")) > 0) {
+        if (snapshot.receivableTotal().compareTo(SME_RECEIVABLE_RISK_THRESHOLD) > 0) {
             riskSuggestions.add(riskSuggestion("鍥炴椋庨櫓", "涓�", "瀵瑰簲鏀禩OP瀹㈡埛寤虹珛鍛ㄥ害鍥炴璁″垝锛屽苟璁剧疆棰勮闃堝�笺��"));
         }
-        if (inventoryValue.compareTo(new BigDecimal("3000000")) > 0) {
+        if (inventoryValue.compareTo(SME_INVENTORY_RISK_THRESHOLD) > 0) {
             riskSuggestions.add(riskSuggestion("搴撳瓨椋庨櫓", "涓�", "瀵归珮閲戦鍛嗘粸搴撳瓨鎵ц闄嶄环銆佹浛浠e拰鐢熶骇娑堣�楃瓥鐣ャ��"));
         }
 
@@ -1319,7 +1334,35 @@
                 return productAmount;
             }
         }
-        return revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
+
+        BigDecimal materialCost = revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
+        BigDecimal laborCost = revenue.multiply(DEFAULT_LABOR_COST_RATE);
+        BigDecimal overheadCost = revenue.multiply(DEFAULT_OVERHEAD_COST_RATE);
+
+        return materialCost.add(laborCost).add(overheadCost);
+    }
+
+    private BigDecimal estimateTotalCost(BigDecimal revenue, List<SalesLedgerProduct> products) {
+        if (revenue == null || revenue.compareTo(BigDecimal.ZERO) <= 0) {
+            return BigDecimal.ZERO;
+        }
+
+        BigDecimal materialCost = BigDecimal.ZERO;
+        if (products != null && !products.isEmpty()) {
+            materialCost = products.stream()
+                    .map(SalesLedgerProduct::getTaxExclusiveTotalPrice)
+                    .filter(Objects::nonNull)
+                    .reduce(BigDecimal.ZERO, BigDecimal::add);
+        }
+
+        if (materialCost.compareTo(BigDecimal.ZERO) <= 0) {
+            materialCost = revenue.multiply(DEFAULT_FALLBACK_MATERIAL_COST_RATE);
+        }
+
+        BigDecimal laborCost = revenue.multiply(DEFAULT_LABOR_COST_RATE);
+        BigDecimal overheadCost = revenue.multiply(DEFAULT_OVERHEAD_COST_RATE);
+
+        return materialCost.add(laborCost).add(overheadCost);
     }
 
     private Map<String, String> queryCustomerNameMap(Set<String> idSet) {

--
Gitblit v1.9.3