From e93af9a26dfdedfa47c982151e8f8f0b3d65645b Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期五, 22 五月 2026 09:20:06 +0800
Subject: [PATCH] fix(ai): 解决记录类型字符串前后空格导致的数据映射问题
---
doc/20260522_财务升级AI模块前端变更联调文档.md | 150 ++++++++++++++++++++++++++++++
src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java | 4
src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java | 114 ----------------------
3 files changed, 153 insertions(+), 115 deletions(-)
diff --git "a/doc/20260522_\350\264\242\345\212\241\345\215\207\347\272\247AI\346\250\241\345\235\227\345\211\215\347\253\257\345\217\230\346\233\264\350\201\224\350\260\203\346\226\207\346\241\243.md" "b/doc/20260522_\350\264\242\345\212\241\345\215\207\347\272\247AI\346\250\241\345\235\227\345\211\215\347\253\257\345\217\230\346\233\264\350\201\224\350\260\203\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..e011374
--- /dev/null
+++ "b/doc/20260522_\350\264\242\345\212\241\345\215\207\347\272\247AI\346\250\241\345\235\227\345\211\215\347\253\257\345\217\230\346\233\264\350\201\224\350\260\203\346\226\207\346\241\243.md"
@@ -0,0 +1,150 @@
+# 璐㈠姟妯″潡鍗囩骇鍚� AI 妯″潡鍓嶇鍙樻洿鑱旇皟鏂囨。锛堥噰璐�/閿�鍞�/鐢熶骇/寰呭姙锛�
+
+鏇存柊鏃ユ湡锛�2026-05-22
+閫傜敤鑼冨洿锛歚/sales-ai`銆乣/purchase-ai`銆乣/manufacturing-ai`銆乣/xiaozhi`锛堝鎵瑰緟鍔烇級
+
+## 1. 鍙樻洿鎬昏
+
+| 妯″潡 | 瀵瑰鎺ュ彛 | 鏄惁闇�瑕佸墠绔敼閫� | 缁撹 |
+| --- | --- | --- | --- |
+| 閿�鍞� AI | `POST /sales-ai/chat` | 鏄� | 璐㈠姟鍙e緞鍒囨崲鍒版柊鏀舵妯″瀷锛岄儴鍒� `type` 鐨勫瓧娈佃涔夊彉鍖� |
+| 閲囪喘 AI | `POST /purchase-ai/chat` | 鏄� | 浠樻/鍙戠エ/寰呬粯娆捐绠楀垏鎹㈠埌鏂拌储鍔¢摼璺紝缁熻鍊间粠鍗犱綅鏀逛负鐪熷疄鍊� |
+| 鐢熶骇 AI | `POST /manufacturing-ai/chat` | 鍚� | 宸叉牳鏌ワ紝鏃犳棫璐㈠姟閫昏緫渚濊禆锛屾棤瀛楁鍙樻洿 |
+| 寰呭姙 AI | `POST /xiaozhi/chat` | 鍚� | 宸叉牳鏌ワ紝鏃犳棫璐㈠姟閫昏緫渚濊禆锛屾棤瀛楁鍙樻洿 |
+
+## 2. 閿�鍞� AI 鍙樻洿锛坄/sales-ai/chat`锛�
+
+### 2.1 `type = sales_return_list`锛堥攢鍞��娆�/鍥炴璁板綍锛�
+
+褰撳墠杩斿洖鏁版嵁鏉ユ簮缁熶竴涓烘柊璐㈠姟琛� `account_sales_collection`锛屼笉鍐嶈蛋鏃ф敹娆鹃��璐ч�昏緫銆�
+
+`data.items[]` 鍏抽敭瀛楁锛�
+
+| 瀛楁 | 绫诲瀷 | 璇存槑 |
+| --- | --- | --- |
+| id | number | 鏀舵璁板綍ID |
+| refundId | string | 鏄犲皠 `collectionNumber`锛屽墠绔彲缁х画浣滀负鈥滈��娆�/鍥炴鍗曞彿鈥濆睍绀� |
+| collectionNumber | string | 鏀舵鍗曞彿 |
+| paymentMethod | string | 鏀舵鏂瑰紡 |
+| actualAmount | number | 鏀舵閲戦锛堜笌 `collectionAmount` 鍚屽�硷級 |
+| collectionAmount | number | 鏀舵閲戦锛堟帹鑽愪富灞曠ず瀛楁锛� |
+| customerId | number | 瀹㈡埛ID |
+| remark | string | 澶囨敞 |
+| createTime | string | 鏀舵鏃ユ湡锛坹yyy-MM-dd锛� |
+
+`summary` 澧為噺鍏虫敞锛�
+- `returnAmount`锛氭椂闂磋寖鍥村唴閲戦姹囨�伙紙鎸� `collectionAmount` 缁熻锛�
+
+### 2.2 `type = sales_customer_interaction_list`锛堝鎴峰線鏉ワ級
+
+褰撳墠杩斿洖鍩轰簬鏂伴摼璺細
+`account_sales_collection.stock_out_record_ids -> stock_out_record(record_type=13) -> shipping_info -> sales_ledger`
+
+杩斿洖绾﹀畾锛�
+- 鏃犳暟鎹椂锛歚description = "no_customer_interactions"`
+- 鏈夋暟鎹椂锛歚description = "ok"`
+
+`summary` 鍏抽敭瀛楁锛�
+- `totalReceiptAmount`
+- `customerCount`
+
+`data.items[]` 鍏抽敭瀛楁锛�
+- `salesLedgerId`
+- `salesContractNo`
+- `customerName`
+- `projectName`
+- `receiptPaymentDate`
+- `receiptPaymentAmount`
+- `receiptPaymentType`
+- `collectionNumber`
+- `registrant`
+- `remark`
+
+### 2.3 `type = sales_ledger_list`锛堥攢鍞彴璐︼級
+
+瀛楁缁撴瀯涓嶅彉锛屼絾閲戦鍙e緞宸插垏鎹細
+- `receivedAmount` 鐢辨柊鏀舵妯″瀷姹囨�诲緱鍒帮紱
+- `pendingAmount = max(0, invoicedAmount - receivedAmount)`锛�
+- 鑻ユ敹娆捐褰曟湭鏄惧紡鍏宠仈鍙拌处锛屽垯鎸夊鎴风淮搴﹀厹搴曞綊闆嗐��
+
+鍓嶇鏀归�犲缓璁細
+- 涓嶆敼瀛楁鍚嶏紱
+- 閲嶇偣鍥炲綊鈥滃凡鏀堕噾棰�/寰呭洖娆鹃噾棰濃�濇槸鍚︿笌璐㈠姟鍙拌处涓�鑷淬��
+
+## 3. 閲囪喘 AI 鍙樻洿锛坄/purchase-ai/chat`锛�
+
+### 3.1 `type = purchase_stats`锛堥噰璐粺璁★級
+
+浠ヤ笅瀛楁宸蹭粠鍗犱綅鍊兼敼涓虹湡瀹炵粺璁″�硷細
+- `summary.paymentCount`
+- `summary.invoiceCount`
+- `summary.paymentAmount`
+- `summary.invoiceAmount`
+
+鍙戠エ閲戦鍙e緞锛�
+- 浼樺厛 `taxInclusivePrice`
+- 鑻ヤ负绌�/0锛屽垯浣跨敤 `taxExclusivelPrice + taxPrice`
+
+### 3.2 `type = purchase_pending_payment_list`锛堝緟浠樻閲囪喘鍗曪級
+
+鏍稿績璁$畻宸插垏鎹㈠埌鏂拌储鍔¢摼璺細
+
+`account_purchase_payment -> account_payment_application -> stock_in_record -> (purchase_ledger / quality_inspect) -> purchase_ledger_id`
+
+鏄犲皠瑙勫垯锛�
+1. `stock_in_record.record_type = 7`锛歚record_id` 鐩存帴瑙嗕负 `purchase_ledger_id`
+2. `stock_in_record.record_type = 10`锛氶�氳繃 `quality_inspect.id = record_id` 鍙� `quality_inspect.purchase_ledger_id`
+
+閲戦瀛楁鍙e緞锛�
+- `paidAmount`锛氭柊閾捐矾绱宸蹭粯娆鹃噾棰�
+- `pendingAmount = contractAmount - paidAmount`锛�<=0 鐨勮褰曚笉杩斿洖锛�
+
+`summary` 鍏抽敭瀛楁锛堝潎涓虹湡瀹炲�硷級锛�
+- `pendingOrderCount`
+- `totalContractAmount`
+- `totalPaidAmount`
+- `totalPendingAmount`
+
+### 3.3 鏁版嵁娓呮礂淇
+
+宸蹭慨澶� `record_type` 甯︾┖鏍煎鑷寸殑鏄犲皠涓㈠け闂锛堝悗绔粺涓� `trim()` 鍚庡啀鍒ゆ柇 `7/10`锛夈��
+
+## 4. 鐢熶骇 AI / 寰呭姙 AI 鏍告煡缁撹
+
+宸叉牳鏌ヤ互涓嬫ā鍧椾唬鐮侊紝鏈彂鐜版棫璐㈠姟閫昏緫鑰﹀悎鐐癸細
+- `ManufacturingAgentTools`锛堢敓浜э級
+- `ApproveTodoTools`锛堝緟鍔炲鎵癸級
+
+缁撹锛�
+- 瀵瑰 `type` 涓庡瓧娈电粨鏋勬棤鍙樻洿锛�
+- 鍓嶇鏃犻渶鍋氬吋瀹规敼閫狅紝浠呴渶鍋氫竴娆″洖褰掗獙璇併��
+
+## 5. 鍓嶇鑱旇皟瑕佺偣
+
+1. `/sales-ai/chat`銆乣/purchase-ai/chat` 缁х画鎸� SSE 鏂囨湰娴佹嫾鎺ュ悗鍋� JSON 瑙f瀽銆�
+2. 鎸� `type` 璺敱娓叉煋锛屼笉瑕佷粎渚濊禆 `description` 鏂囨銆�
+3. `sales_customer_interaction_list` 闇�鍏煎 `description` 鏋氫妇锛歚ok` / `no_customer_interactions`銆�
+4. `sales_return_list` 閲戦灞曠ず缁熶竴鐢� `collectionAmount`锛坄actualAmount` 淇濈暀鍏煎锛夈��
+5. `purchase_pending_payment_list` 鐨勬眹鎬诲崱鐗囪鐩存帴璇诲彇 `summary.totalPendingAmount` 绛夊瓧娈碉紝涓嶅啀鍓嶇浜屾浼扮畻銆�
+
+## 6. 鍥炲綊娓呭崟锛堝缓璁級
+
+### 閿�鍞�
+1. 鎻愰棶锛氣�滆繎30澶╁摢涓鍗曞洖娆炬渶灏戔��
+ - 鏍¢獙 `sales_ledger_list` 鐨� `receivedAmount/pendingAmount`銆�
+2. 鎻愰棶锛氣�滄煡璇㈡湰鏈堥攢鍞��娆锯��
+ - 鏍¢獙 `sales_return_list` 鐨� `collectionNumber/collectionAmount/returnAmount`銆�
+3. 鎻愰棶锛氣�滄煡璇㈡湰鏈堝鎴峰線鏉モ��
+ - 鏍¢獙 `sales_customer_interaction_list` 鐨� `totalReceiptAmount/customerCount`銆�
+
+### 閲囪喘
+1. 鎻愰棶锛氣�滅粺璁℃湰鏈堥噰璐暟鎹��
+ - 鏍¢獙 `purchase_stats` 鐨� `paymentCount/invoiceCount/paymentAmount/invoiceAmount` 闈炲浐瀹�0銆�
+2. 鎻愰棶锛氣�滃垪鍑哄緟浠樻閲囪喘鍗曗��
+ - 鏍¢獙 `purchase_pending_payment_list` 鐨� `paidAmount/pendingAmount` 涓庤储鍔″疄闄呬竴鑷淬��
+
+### 鐢熶骇/寰呭姙
+1. 鐢熶骇鎻愰棶锛氣�滄煡璇㈡湰鍛ㄨ澶囩淮淇褰曗��
+2. 寰呭姙鎻愰棶锛氣�滄煡璇㈡垜鐨勫緟瀹℃壒鍒楄〃鈥�
+ - 鏍¢獙杩斿洖缁撴瀯涓庡崌绾у墠涓�鑷达紙鏃犲瓧娈电牬鍧忥級銆�
+
diff --git a/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java b/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
index a294b68..bdd1f1f 100644
--- a/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
@@ -591,7 +591,7 @@
private Map<Long, Long> queryPurchaseLedgerIdByQualityInspectId(Collection<StockInRecord> stockInRecords) {
Set<Long> qualityInspectIds = stockInRecords.stream()
.filter(Objects::nonNull)
- .filter(item -> item.getRecordId() != null && "10".equals(safe(item.getRecordType())))
+ .filter(item -> item.getRecordId() != null && "10".equals(safe(item.getRecordType()).trim()))
.map(StockInRecord::getRecordId)
.collect(Collectors.toSet());
if (qualityInspectIds.isEmpty()) {
@@ -614,7 +614,7 @@
if (stockInRecord.getApprovalStatus() != null && stockInRecord.getApprovalStatus() != 1) {
continue;
}
- String recordType = safe(stockInRecord.getRecordType());
+ String recordType = safe(stockInRecord.getRecordType()).trim();
if ("7".equals(recordType)) {
result.add(stockInRecord.getRecordId());
} else if ("10".equals(recordType)) {
diff --git a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
index 8a63db7..0f7e586 100644
--- a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
@@ -121,60 +121,6 @@
@P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit) {
LoginUser loginUser = currentLoginUser(memoryId);
DateRange range = resolveDateRange(startDate, endDate, null);
- /*
- List<AccountSalesCollection> collections = queryCollections(loginUser, range);
- if (collections.isEmpty()) {
- return jsonResponse(true, "sales_customer_interaction_list", "閺堫亝鐓$拠銏犲煂鐎广垺鍩涘鈧弶銉唶瑜�?, rangeSummary(range, 0, keyword), Map.of("items", List.of()), Map.of());
- }
-
- Map<Integer, Set<Long>> ledgerIdsByCollectionId = mapCollectionLedgerIds(loginUser, collections);
- Set<Long> ledgerIds = ledgerIdsByCollectionId.values().stream()
- .flatMap(Collection::stream)
- .collect(Collectors.toSet());
- Map<Long, SalesLedger> ledgerMap = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
- .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
- .collect(Collectors.toMap(SalesLedger::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
-
- int finalLimit = normalizeLimit(limit);
- List<Map<String, Object>> items = new ArrayList<>();
- for (AccountSalesCollection collection : collections) {
- Set<Long> relatedLedgerIds = ledgerIdsByCollectionId.get(collection.getId());
- if (relatedLedgerIds == null || relatedLedgerIds.isEmpty()) {
- if (!matchInteractionKeyword(collection, null, keyword)) {
- continue;
- }
- items.add(toInteractionItem(collection, null));
- if (items.size() >= finalLimit) {
- break;
- }
- continue;
- }
- for (Long ledgerId : relatedLedgerIds) {
- SalesLedger ledger = ledgerMap.get(ledgerId);
- if (ledger == null || !matchInteractionKeyword(collection, ledger, keyword)) {
- continue;
- }
- items.add(toInteractionItem(collection, ledger));
- if (items.size() >= finalLimit) {
- break;
- }
- }
- if (items.size() >= finalLimit) {
- break;
- }
- }
-
- BigDecimal totalReceiptAmount = items.stream()
- .map(item -> asBigDecimal(item.get("receiptPaymentAmount")))
- .reduce(BigDecimal.ZERO, BigDecimal::add);
- Map<String, Object> summary = rangeSummary(range, items.size(), keyword);
- summary.put("totalReceiptAmount", totalReceiptAmount);
- summary.put("customerCount", items.stream()
- .map(item -> String.valueOf(item.get("customerName")))
- .filter(StringUtils::hasText)
- .distinct()
- .count());
- */
LambdaQueryWrapper<SalesQuotation> wrapper = new LambdaQueryWrapper<>();
applyTenantFilter(wrapper, loginUser.getTenantId(), SalesQuotation::getTenantId);
applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), SalesQuotation::getDeptId);
@@ -398,59 +344,7 @@
.filter(StringUtils::hasText)
.distinct()
.count());
- if (summary.size() >= 0) {
- return jsonResponse(true, "sales_customer_interaction_list", "ok", summary, Map.of("items", items), Map.of());
- }
-// LambdaQueryWrapper<ReceiptPayment> wrapper = new LambdaQueryWrapper<>();
-// applyTenantFilter(wrapper, loginUser.getTenantId(), ReceiptPayment::getTenantId);
-// applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), ReceiptPayment::getDeptId);
-// wrapper.ge(ReceiptPayment::getReceiptPaymentDate, range.start())
-// .le(ReceiptPayment::getReceiptPaymentDate, range.end())
-// .orderByDesc(ReceiptPayment::getReceiptPaymentDate, ReceiptPayment::getId);
-// List<ReceiptPayment> payments = defaultList(receiptPaymentMapper.selectList(wrapper));
-// if (payments.isEmpty()) {
-// return jsonResponse(true, "sales_customer_interaction_list", "鏈煡璇㈠埌瀹㈡埛寰�鏉ヨ褰�", rangeSummary(range, 0, keyword), Map.of("items", List.of()), Map.of());
-// }
-//
-// List<Long> ledgerIds = payments.stream()
-// .map(ReceiptPayment::getSalesLedgerId)
-// .filter(Objects::nonNull)
-// .distinct()
-// .collect(Collectors.toList());
-// Map<Long, SalesLedger> ledgerMap = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
-// .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
-// .collect(Collectors.toMap(SalesLedger::getId, item -> item, (a, b) -> a, LinkedHashMap::new));
-//
-// List<ReceiptPayment> filtered = payments.stream()
-// .filter(item -> matchInteractionKeyword(item, ledgerMap.get(item.getSalesLedgerId()), keyword))
-// .limit(normalizeLimit(limit))
-// .collect(Collectors.toList());
-//
-// BigDecimal totalReceiptAmount = filtered.stream()
-// .map(ReceiptPayment::getReceiptPaymentAmount)
-// .filter(Objects::nonNull)
-// .reduce(BigDecimal.ZERO, BigDecimal::add);
-
-// List<Map<String, Object>> items = filtered.stream().map(item -> {
-// SalesLedger ledger = ledgerMap.get(item.getSalesLedgerId());
-// Map<String, Object> map = new LinkedHashMap<>();
-// map.put("id", item.getId());
-// map.put("salesLedgerId", item.getSalesLedgerId());
-// map.put("salesContractNo", ledger == null ? "" : safe(ledger.getSalesContractNo()));
-// map.put("customerName", ledger == null ? "" : safe(ledger.getCustomerName()));
-// map.put("projectName", ledger == null ? "" : safe(ledger.getProjectName()));
-// map.put("receiptPaymentDate", formatDate(item.getReceiptPaymentDate()));
-// map.put("receiptPaymentAmount", item.getReceiptPaymentAmount());
-// map.put("receiptPaymentType", safe(item.getReceiptPaymentType()));
-// map.put("registrant", safe(item.getRegistrant()));
-// return map;
-// }).collect(Collectors.toList());
-
-// Map<String, Object> summary = rangeSummary(range, items.size(), keyword);
-// summary.put("totalReceiptAmount", totalReceiptAmount);
-// summary.put("customerCount", items.stream().map(item -> String.valueOf(item.get("customerName"))).filter(StringUtils::hasText).distinct().count());
-// return jsonResponse(true, "sales_customer_interaction_list", "宸茶繑鍥炲鎴峰線鏉ユ槑缁�", summary, Map.of("items", items), Map.of());
- return jsonResponse(true, "sales_customer_interaction_list", "宸茶繑鍥炲鎴峰線鏉ユ槑缁�", null, Map.of("items", List.of()), Map.of());
+ return jsonResponse(true, "sales_customer_interaction_list", "ok", summary, Map.of("items", items), Map.of());
}
@Tool(name = "鏌ヨ鍙戣揣鍙拌处", value = "鎸夊叧閿瘝鍜屾椂闂磋寖鍥存煡璇㈠彂璐у彴璐�")
@@ -965,12 +859,6 @@
return Map.of();
}
Map<Long, BigDecimal> result = new HashMap<>();
-// for (InvoiceLedgerDto item : defaultList(invoiceLedgerMapper.invoicedTotal(ledgerIds))) {
-// if (item.getSalesLedgerId() == null) {
-// continue;
-// }
-// result.merge(item.getSalesLedgerId().longValue(), defaultDecimal(item.getInvoiceTotal()), BigDecimal::add);
-// }
return result;
}
--
Gitblit v1.9.3