10 小时以前 7bf754a7835d06f26240c4ca15bc5f83650de377
src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
@@ -14,9 +14,11 @@
import com.ruoyi.sales.mapper.SalesLedgerMapper;
import com.ruoyi.sales.mapper.SalesQuotationMapper;
import com.ruoyi.sales.mapper.ShippingInfoMapper;
import com.ruoyi.sales.mapper.StockOutRecordSalesLedgerMapper;
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.pojo.ShippingInfo;
import com.ruoyi.sales.pojo.StockOutRecordSalesLedger;
import com.ruoyi.stock.mapper.StockOutRecordMapper;
import com.ruoyi.stock.pojo.StockOutRecord;
import dev.langchain4j.agent.tool.P;
@@ -53,6 +55,7 @@
    private final ShippingInfoMapper shippingInfoMapper;
    private final AccountSalesCollectionMapper accountSalesCollectionMapper;
    private final StockOutRecordMapper stockOutRecordMapper;
    private final StockOutRecordSalesLedgerMapper stockOutRecordSalesLedgerMapper;
    private final AiSessionUserContext aiSessionUserContext;
    public SalesAgentTools(CustomerMapper customerMapper,
@@ -61,6 +64,7 @@
                           ShippingInfoMapper shippingInfoMapper,
                           AccountSalesCollectionMapper accountSalesCollectionMapper,
                           StockOutRecordMapper stockOutRecordMapper,
                           StockOutRecordSalesLedgerMapper stockOutRecordSalesLedgerMapper,
                           AiSessionUserContext aiSessionUserContext) {
        this.customerMapper = customerMapper;
        this.salesLedgerMapper = salesLedgerMapper;
@@ -68,6 +72,7 @@
        this.shippingInfoMapper = shippingInfoMapper;
        this.accountSalesCollectionMapper = accountSalesCollectionMapper;
        this.stockOutRecordMapper = stockOutRecordMapper;
        this.stockOutRecordSalesLedgerMapper = stockOutRecordSalesLedgerMapper;
        this.aiSessionUserContext = aiSessionUserContext;
    }
@@ -121,60 +126,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 +349,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 +864,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;
    }
@@ -1116,9 +1009,46 @@
                result.put(entry.getKey(), ledgerIds);
            }
        }
        mergeOilOutBindingLedgerIds(collections, result);
        return result;
    }
    /**
     * 油品出库的收款单不存出库单 id,存的是绑定行 id(一个出库单可能按客户拆给多个台账)。
     * 绑定行上直接带 sales_ledger_id,不需要再经 shipping_info 中转。
     */
    private void mergeOilOutBindingLedgerIds(List<AccountSalesCollection> collections, Map<Integer, Set<Long>> result) {
        Map<Integer, List<Long>> bindingIdsByCollection = new HashMap<>();
        Set<Long> allBindingIds = new HashSet<>();
        for (AccountSalesCollection collection : collections) {
            if (collection.getId() == null) {
                continue;
            }
            List<Long> bindingIds = parseLongIds(collection.getStockOutBindingIds());
            if (bindingIds.isEmpty()) {
                continue;
            }
            bindingIdsByCollection.put(collection.getId(), bindingIds);
            allBindingIds.addAll(bindingIds);
        }
        if (allBindingIds.isEmpty()) {
            return;
        }
        Map<Long, Long> ledgerIdByBindingId = defaultList(stockOutRecordSalesLedgerMapper.selectByIds(allBindingIds)).stream()
                .filter(item -> item.getId() != null && item.getSalesLedgerId() != null)
                .collect(Collectors.toMap(StockOutRecordSalesLedger::getId, StockOutRecordSalesLedger::getSalesLedgerId, (a, b) -> a));
        for (Map.Entry<Integer, List<Long>> entry : bindingIdsByCollection.entrySet()) {
            Set<Long> ledgerIds = result.computeIfAbsent(entry.getKey(), key -> new LinkedHashSet<>());
            for (Long bindingId : entry.getValue()) {
                Long ledgerId = ledgerIdByBindingId.get(bindingId);
                if (ledgerId != null) {
                    ledgerIds.add(ledgerId);
                }
            }
        }
    }
    private boolean isSalesOutboundRecord(StockOutRecord stockOutRecord) {
        if (stockOutRecord == null || !StringUtils.hasText(stockOutRecord.getRecordType())) {
            return false;