From 200997dac09a7e4d373a6f7709733d57567b38f8 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期四, 21 五月 2026 17:49:24 +0800
Subject: [PATCH] feat(ai): 更新采购和销售AI工具的数据查询功能
---
src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java | 243 +++++++++++++++++++--
src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java | 380 ++++++++++++++++++++++++++++++++--
2 files changed, 566 insertions(+), 57 deletions(-)
diff --git a/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java b/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
index 006438c..a294b68 100644
--- a/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/PurchaseAgentTools.java
@@ -2,6 +2,12 @@
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.ruoyi.account.mapper.purchase.AccountPaymentApplicationMapper;
+import com.ruoyi.account.mapper.purchase.AccountPurchaseInvoiceMapper;
+import com.ruoyi.account.mapper.purchase.AccountPurchasePaymentMapper;
+import com.ruoyi.account.pojo.purchase.AccountPaymentApplication;
+import com.ruoyi.account.pojo.purchase.AccountPurchaseInvoice;
+import com.ruoyi.account.pojo.purchase.AccountPurchasePayment;
import com.ruoyi.ai.context.AiSessionUserContext;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.framework.security.LoginUser;
@@ -13,8 +19,12 @@
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
import com.ruoyi.purchase.pojo.PurchaseLedger;
import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
+import com.ruoyi.quality.mapper.QualityInspectMapper;
+import com.ruoyi.quality.pojo.QualityInspect;
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
import com.ruoyi.sales.pojo.SalesLedgerProduct;
+import com.ruoyi.stock.mapper.StockInRecordMapper;
+import com.ruoyi.stock.pojo.StockInRecord;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolMemoryId;
@@ -41,6 +51,11 @@
private final SalesLedgerProductMapper salesLedgerProductMapper;
private final ProcurementRecordMapper procurementRecordMapper;
private final InboundManagementMapper inboundManagementMapper;
+ private final AccountPurchasePaymentMapper accountPurchasePaymentMapper;
+ private final AccountPaymentApplicationMapper accountPaymentApplicationMapper;
+ private final AccountPurchaseInvoiceMapper accountPurchaseInvoiceMapper;
+ private final StockInRecordMapper stockInRecordMapper;
+ private final QualityInspectMapper qualityInspectMapper;
private final AiSessionUserContext aiSessionUserContext;
public PurchaseAgentTools(PurchaseLedgerMapper purchaseLedgerMapper,
@@ -48,12 +63,22 @@
SalesLedgerProductMapper salesLedgerProductMapper,
ProcurementRecordMapper procurementRecordMapper,
InboundManagementMapper inboundManagementMapper,
+ AccountPurchasePaymentMapper accountPurchasePaymentMapper,
+ AccountPaymentApplicationMapper accountPaymentApplicationMapper,
+ AccountPurchaseInvoiceMapper accountPurchaseInvoiceMapper,
+ StockInRecordMapper stockInRecordMapper,
+ QualityInspectMapper qualityInspectMapper,
AiSessionUserContext aiSessionUserContext) {
this.purchaseLedgerMapper = purchaseLedgerMapper;
this.purchaseReturnOrdersMapper = purchaseReturnOrdersMapper;
this.salesLedgerProductMapper = salesLedgerProductMapper;
this.procurementRecordMapper = procurementRecordMapper;
this.inboundManagementMapper = inboundManagementMapper;
+ this.accountPurchasePaymentMapper = accountPurchasePaymentMapper;
+ this.accountPaymentApplicationMapper = accountPaymentApplicationMapper;
+ this.accountPurchaseInvoiceMapper = accountPurchaseInvoiceMapper;
+ this.stockInRecordMapper = stockInRecordMapper;
+ this.qualityInspectMapper = qualityInspectMapper;
this.aiSessionUserContext = aiSessionUserContext;
}
@@ -115,24 +140,22 @@
DateRange range = resolveDateRange(startDate, endDate, timeRange);
List<PurchaseLedger> ledgers = queryLedgers(loginUser, range);
-// List<PaymentRegistration> payments = queryPayments(loginUser, range);
-// List<InvoicePurchase> invoices = queryInvoices(loginUser, range);
+ List<AccountPurchasePayment> payments = queryPayments(loginUser, range);
+ List<AccountPurchaseInvoice> invoices = queryInvoices(loginUser, range);
List<PurchaseReturnOrders> returns = queryReturns(loginUser, range);
BigDecimal contractAmount = ledgers.stream()
.map(PurchaseLedger::getContractAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
- BigDecimal paymentAmount = BigDecimal.ZERO;
-// BigDecimal paymentAmount = payments.stream()
-// .map(PaymentRegistration::getCurrentPaymentAmount)
-// .filter(Objects::nonNull)
-// .reduce(BigDecimal.ZERO, BigDecimal::add);
- BigDecimal invoiceAmount = BigDecimal.ZERO;
-// BigDecimal invoiceAmount = invoices.stream()
-// .map(InvoicePurchase::getInvoiceAmount)
-// .filter(Objects::nonNull)
-// .reduce(BigDecimal.ZERO, BigDecimal::add);
+ BigDecimal paymentAmount = payments.stream()
+ .map(AccountPurchasePayment::getPaymentAmount)
+ .filter(Objects::nonNull)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+ BigDecimal invoiceAmount = invoices.stream()
+ .map(this::invoiceAmountOf)
+ .filter(Objects::nonNull)
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal returnAmount = returns.stream()
.map(PurchaseReturnOrders::getTotalAmount)
.filter(Objects::nonNull)
@@ -143,10 +166,8 @@
summary.put("startDate", range.start().toString());
summary.put("endDate", range.end().toString());
summary.put("ledgerCount", ledgers.size());
- summary.put("paymentCount", 0);
-// summary.put("paymentCount", payments.size());
-// summary.put("invoiceCount", invoices.size());
- summary.put("invoiceCount", 0);
+ summary.put("paymentCount", payments.size());
+ summary.put("invoiceCount", invoices.size());
summary.put("returnCount", returns.size());
summary.put("contractAmount", contractAmount);
summary.put("paymentAmount", paymentAmount);
@@ -268,9 +289,15 @@
@P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit) {
LoginUser loginUser = currentLoginUser(memoryId);
DateRange range = resolveDateRange(startDate, endDate, null);
- List<Map<String, Object>> items = queryLedgers(loginUser, range).stream()
+ List<PurchaseLedger> matchedLedgers = queryLedgers(loginUser, range).stream()
.filter(ledger -> matchLedgerKeyword(ledger, keyword))
- .map(ledger -> toPendingPaymentItem(loginUser, ledger))
+ .collect(Collectors.toList());
+ Map<Long, BigDecimal> paidAmountByLedgerId = sumPaymentAmountByLedgerId(loginUser, matchedLedgers.stream()
+ .map(PurchaseLedger::getId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()));
+ List<Map<String, Object>> items = matchedLedgers.stream()
+ .map(ledger -> toPendingPaymentItem(ledger, paidAmountByLedgerId.getOrDefault(ledger.getId(), BigDecimal.ZERO)))
.filter(Objects::nonNull)
.sorted(Comparator.comparing(item -> (BigDecimal) item.get("pendingAmount"), Comparator.reverseOrder()))
.limit(normalizeLimit(limit))
@@ -411,28 +438,58 @@
return map;
}
- private Map<String, Object> toPendingPaymentItem(LoginUser loginUser, PurchaseLedger ledger) {
+ private Map<String, Object> toPendingPaymentItem(PurchaseLedger ledger, BigDecimal paidAmount) {
BigDecimal contractAmount = defaultDecimal(ledger.getContractAmount());
- BigDecimal paidAmount = sumPaymentAmount(loginUser, ledger.getId());
- BigDecimal pendingAmount = contractAmount.subtract(paidAmount);
+ BigDecimal safePaidAmount = defaultDecimal(paidAmount);
+ BigDecimal pendingAmount = contractAmount.subtract(safePaidAmount);
if (pendingAmount.compareTo(BigDecimal.ZERO) <= 0) {
return null;
}
Map<String, Object> item = toLedgerItem(ledger);
- item.put("paidAmount", paidAmount);
+ item.put("paidAmount", safePaidAmount);
item.put("pendingAmount", pendingAmount);
return item;
}
- private BigDecimal sumPaymentAmount(LoginUser loginUser, Long purchaseLedgerId) {
-// LambdaQueryWrapper<PaymentRegistration> wrapper = new LambdaQueryWrapper<>();
-// applyTenantFilter(wrapper, loginUser.getTenantId(), PaymentRegistration::getTenantId);
-// wrapper.eq(PaymentRegistration::getPurchaseLedgerId, purchaseLedgerId);
-// return defaultList(paymentRegistrationMapper.selectList(wrapper)).stream()
-// .map(PaymentRegistration::getCurrentPaymentAmount)
-// .filter(Objects::nonNull)
-// .reduce(BigDecimal.ZERO, BigDecimal::add);
- return BigDecimal.ZERO;
+ private Map<Long, BigDecimal> sumPaymentAmountByLedgerId(LoginUser loginUser, List<Long> purchaseLedgerIds) {
+ if (purchaseLedgerIds == null || purchaseLedgerIds.isEmpty()) {
+ return Map.of();
+ }
+ List<AccountPurchasePayment> payments = queryPayments(loginUser);
+ if (payments.isEmpty()) {
+ return Map.of();
+ }
+
+ Map<Integer, AccountPaymentApplication> applicationById = queryPaymentApplications(payments);
+ if (applicationById.isEmpty()) {
+ return Map.of();
+ }
+
+ Map<Long, StockInRecord> stockInRecordById = queryStockInRecords(applicationById.values());
+ Map<Long, Long> purchaseLedgerIdByQualityInspectId = queryPurchaseLedgerIdByQualityInspectId(stockInRecordById.values());
+ Set<Long> targetLedgerIdSet = new HashSet<>(purchaseLedgerIds);
+ Map<Long, BigDecimal> result = new HashMap<>();
+
+ for (AccountPurchasePayment payment : payments) {
+ if (payment.getAccountPaymentApplicationId() == null) {
+ continue;
+ }
+ AccountPaymentApplication application = applicationById.get(payment.getAccountPaymentApplicationId());
+ if (application == null) {
+ continue;
+ }
+ Set<Long> ledgerIds = resolvePurchaseLedgerIds(application, stockInRecordById, purchaseLedgerIdByQualityInspectId);
+ if (ledgerIds.isEmpty()) {
+ continue;
+ }
+ BigDecimal amount = defaultDecimal(payment.getPaymentAmount());
+ for (Long ledgerId : ledgerIds) {
+ if (targetLedgerIdSet.contains(ledgerId)) {
+ result.merge(ledgerId, amount, BigDecimal::add);
+ }
+ }
+ }
+ return result;
}
private Map<String, Object> toReturnItem(PurchaseReturnOrders item) {
@@ -462,7 +519,129 @@
if (value instanceof Number number) {
return new BigDecimal(String.valueOf(number));
}
- return BigDecimal.ZERO;
+ try {
+ return new BigDecimal(String.valueOf(value));
+ } catch (Exception ignored) {
+ return BigDecimal.ZERO;
+ }
+ }
+
+ private BigDecimal invoiceAmountOf(AccountPurchaseInvoice invoice) {
+ if (invoice == null) {
+ return BigDecimal.ZERO;
+ }
+ BigDecimal amount = defaultDecimal(invoice.getTaxInclusivePrice());
+ if (amount.compareTo(BigDecimal.ZERO) > 0) {
+ return amount;
+ }
+ return defaultDecimal(invoice.getTaxExclusivelPrice()).add(defaultDecimal(invoice.getTaxPrice()));
+ }
+
+ private List<AccountPurchasePayment> queryPayments(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<AccountPurchasePayment> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountPurchasePayment::getDeptId);
+ wrapper.ge(AccountPurchasePayment::getPaymentDate, range.start())
+ .le(AccountPurchasePayment::getPaymentDate, range.end())
+ .orderByDesc(AccountPurchasePayment::getPaymentDate, AccountPurchasePayment::getId);
+ return defaultList(accountPurchasePaymentMapper.selectList(wrapper));
+ }
+
+ private List<AccountPurchasePayment> queryPayments(LoginUser loginUser) {
+ LambdaQueryWrapper<AccountPurchasePayment> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountPurchasePayment::getDeptId);
+ return defaultList(accountPurchasePaymentMapper.selectList(wrapper));
+ }
+
+ private List<AccountPurchaseInvoice> queryInvoices(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<AccountPurchaseInvoice> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountPurchaseInvoice::getDeptId);
+ wrapper.ge(AccountPurchaseInvoice::getIssueDate, range.start())
+ .le(AccountPurchaseInvoice::getIssueDate, range.end())
+ .orderByDesc(AccountPurchaseInvoice::getIssueDate, AccountPurchaseInvoice::getId);
+ return defaultList(accountPurchaseInvoiceMapper.selectList(wrapper));
+ }
+
+ private Map<Integer, AccountPaymentApplication> queryPaymentApplications(List<AccountPurchasePayment> payments) {
+ List<Integer> ids = payments.stream()
+ .map(AccountPurchasePayment::getAccountPaymentApplicationId)
+ .filter(Objects::nonNull)
+ .distinct()
+ .collect(Collectors.toList());
+ if (ids.isEmpty()) {
+ return Map.of();
+ }
+ return defaultList(accountPaymentApplicationMapper.selectBatchIds(ids)).stream()
+ .filter(item -> item.getId() != null)
+ .collect(Collectors.toMap(AccountPaymentApplication::getId, item -> item, (a, b) -> a));
+ }
+
+ private Map<Long, StockInRecord> queryStockInRecords(Collection<AccountPaymentApplication> applications) {
+ Set<Long> stockInRecordIds = new HashSet<>();
+ for (AccountPaymentApplication application : applications) {
+ stockInRecordIds.addAll(parseLongIds(application.getStockInRecordIds()));
+ }
+ if (stockInRecordIds.isEmpty()) {
+ return Map.of();
+ }
+ return defaultList(stockInRecordMapper.selectBatchIds(stockInRecordIds)).stream()
+ .filter(item -> item.getId() != null)
+ .collect(Collectors.toMap(StockInRecord::getId, item -> item, (a, b) -> a));
+ }
+
+ 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())))
+ .map(StockInRecord::getRecordId)
+ .collect(Collectors.toSet());
+ if (qualityInspectIds.isEmpty()) {
+ return Map.of();
+ }
+ return defaultList(qualityInspectMapper.selectBatchIds(qualityInspectIds)).stream()
+ .filter(item -> item.getId() != null && item.getPurchaseLedgerId() != null)
+ .collect(Collectors.toMap(QualityInspect::getId, QualityInspect::getPurchaseLedgerId, (a, b) -> a));
+ }
+
+ private Set<Long> resolvePurchaseLedgerIds(AccountPaymentApplication application,
+ Map<Long, StockInRecord> stockInRecordById,
+ Map<Long, Long> purchaseLedgerIdByQualityInspectId) {
+ Set<Long> result = new LinkedHashSet<>();
+ for (Long stockInRecordId : parseLongIds(application.getStockInRecordIds())) {
+ StockInRecord stockInRecord = stockInRecordById.get(stockInRecordId);
+ if (stockInRecord == null || stockInRecord.getRecordId() == null) {
+ continue;
+ }
+ if (stockInRecord.getApprovalStatus() != null && stockInRecord.getApprovalStatus() != 1) {
+ continue;
+ }
+ String recordType = safe(stockInRecord.getRecordType());
+ if ("7".equals(recordType)) {
+ result.add(stockInRecord.getRecordId());
+ } else if ("10".equals(recordType)) {
+ Long purchaseLedgerId = purchaseLedgerIdByQualityInspectId.get(stockInRecord.getRecordId());
+ if (purchaseLedgerId != null) {
+ result.add(purchaseLedgerId);
+ }
+ }
+ }
+ return result;
+ }
+
+ private List<Long> parseLongIds(String raw) {
+ if (!StringUtils.hasText(raw)) {
+ return List.of();
+ }
+ List<Long> result = new ArrayList<>();
+ for (String part : raw.split(",")) {
+ if (!StringUtils.hasText(part)) {
+ continue;
+ }
+ try {
+ result.add(Long.parseLong(part.trim()));
+ } catch (Exception ignored) {
+ }
+ }
+ return result;
}
diff --git a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
index f9b09de..8a63db7 100644
--- a/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
+++ b/src/main/java/com/ruoyi/ai/tools/SalesAgentTools.java
@@ -3,8 +3,8 @@
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
-import com.ruoyi.account.mapper.SalesReceiptReturnMapper;
-import com.ruoyi.account.pojo.SalesReceiptReturn;
+import com.ruoyi.account.mapper.sales.AccountSalesCollectionMapper;
+import com.ruoyi.account.pojo.sales.AccountSalesCollection;
import com.ruoyi.ai.context.AiSessionUserContext;
import com.ruoyi.basic.dto.CustomerDto;
import com.ruoyi.basic.mapper.CustomerMapper;
@@ -17,6 +17,8 @@
import com.ruoyi.sales.pojo.SalesLedger;
import com.ruoyi.sales.pojo.SalesQuotation;
import com.ruoyi.sales.pojo.ShippingInfo;
+import com.ruoyi.stock.mapper.StockOutRecordMapper;
+import com.ruoyi.stock.pojo.StockOutRecord;
import dev.langchain4j.agent.tool.P;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolMemoryId;
@@ -49,20 +51,23 @@
private final SalesLedgerMapper salesLedgerMapper;
private final SalesQuotationMapper salesQuotationMapper;
private final ShippingInfoMapper shippingInfoMapper;
- private final SalesReceiptReturnMapper salesReceiptReturnMapper;
+ private final AccountSalesCollectionMapper accountSalesCollectionMapper;
+ private final StockOutRecordMapper stockOutRecordMapper;
private final AiSessionUserContext aiSessionUserContext;
public SalesAgentTools(CustomerMapper customerMapper,
SalesLedgerMapper salesLedgerMapper,
SalesQuotationMapper salesQuotationMapper,
ShippingInfoMapper shippingInfoMapper,
- SalesReceiptReturnMapper salesReceiptReturnMapper,
+ AccountSalesCollectionMapper accountSalesCollectionMapper,
+ StockOutRecordMapper stockOutRecordMapper,
AiSessionUserContext aiSessionUserContext) {
this.customerMapper = customerMapper;
this.salesLedgerMapper = salesLedgerMapper;
this.salesQuotationMapper = salesQuotationMapper;
this.shippingInfoMapper = shippingInfoMapper;
- this.salesReceiptReturnMapper = salesReceiptReturnMapper;
+ this.accountSalesCollectionMapper = accountSalesCollectionMapper;
+ this.stockOutRecordMapper = stockOutRecordMapper;
this.aiSessionUserContext = aiSessionUserContext;
}
@@ -116,6 +121,60 @@
@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);
@@ -242,36 +301,35 @@
@P(value = "杩斿洖鏉℃暟锛岄粯璁�10锛屾渶澶�30", required = false) Integer limit) {
LoginUser loginUser = currentLoginUser(memoryId);
DateRange range = resolveDateRange(startDate, endDate, null);
- LambdaQueryWrapper<SalesReceiptReturn> wrapper = new LambdaQueryWrapper<>();
- applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), SalesReceiptReturn::getDeptId);
+ LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
if (StringUtils.hasText(keyword)) {
- wrapper.and(w -> w.like(SalesReceiptReturn::getRefundId, keyword)
- .or().like(SalesReceiptReturn::getTransactionNo, keyword)
- .or().like(SalesReceiptReturn::getPaymentAccountName, keyword));
+ wrapper.and(w -> w.like(AccountSalesCollection::getCollectionNumber, keyword)
+ .or().like(AccountSalesCollection::getCollectionMethod, keyword)
+ .or().like(AccountSalesCollection::getRemark, keyword));
}
- wrapper.ge(SalesReceiptReturn::getCreateTime, range.start().atStartOfDay())
- .le(SalesReceiptReturn::getCreateTime, range.end().atTime(23, 59, 59))
- .orderByDesc(SalesReceiptReturn::getCreateTime, SalesReceiptReturn::getId)
+ wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
+ .le(AccountSalesCollection::getCollectionDate, range.end())
+ .orderByDesc(AccountSalesCollection::getCollectionDate, AccountSalesCollection::getId)
.last("limit " + normalizeLimit(limit));
- List<SalesReceiptReturn> rows = defaultList(salesReceiptReturnMapper.selectList(wrapper));
+ List<AccountSalesCollection> rows = defaultList(accountSalesCollectionMapper.selectList(wrapper));
BigDecimal returnAmount = rows.stream()
- .map(SalesReceiptReturn::getActualAmount)
+ .map(AccountSalesCollection::getCollectionAmount)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
List<Map<String, Object>> items = rows.stream().map(item -> {
Map<String, Object> map = new LinkedHashMap<>();
map.put("id", item.getId());
- map.put("refundId", safe(item.getRefundId()));
- map.put("paymentAccount", safe(item.getPaymentAccount()));
- map.put("paymentAccountName", safe(item.getPaymentAccountName()));
- map.put("paymentMethod", item.getPaymentMethod());
- map.put("actualAmount", item.getActualAmount());
- map.put("fee", item.getFee());
- map.put("discountAmount", item.getDiscountAmount());
- map.put("transactionNo", safe(item.getTransactionNo()));
- map.put("createTime", formatDateTime(item.getCreateTime()));
+ map.put("refundId", safe(item.getCollectionNumber()));
+ map.put("collectionNumber", safe(item.getCollectionNumber()));
+ map.put("paymentMethod", safe(item.getCollectionMethod()));
+ map.put("actualAmount", item.getCollectionAmount());
+ map.put("collectionAmount", item.getCollectionAmount());
+ map.put("customerId", item.getCustomerId());
+ map.put("remark", safe(item.getRemark()));
+ map.put("createTime", formatDate(item.getCollectionDate()));
return map;
}).collect(Collectors.toList());
@@ -288,6 +346,61 @@
@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", "no_customer_interactions", 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());
+ 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);
@@ -862,9 +975,209 @@
}
private Map<Long, BigDecimal> sumReceiptAmounts(LoginUser loginUser, List<Long> ledgerIds) {
- Map<Long, BigDecimal> result = new HashMap<>();
+ if (ledgerIds == null || ledgerIds.isEmpty()) {
+ return Map.of();
+ }
+ List<SalesLedger> ledgers = defaultList(salesLedgerMapper.selectBatchIds(ledgerIds)).stream()
+ .filter(ledger -> tenantMatched(ledger.getTenantId(), loginUser.getTenantId()))
+ .collect(Collectors.toList());
+ if (ledgers.isEmpty()) {
+ return Map.of();
+ }
+ Set<Integer> customerIds = ledgers.stream()
+ .map(SalesLedger::getCustomerId)
+ .filter(Objects::nonNull)
+ .map(Long::intValue)
+ .collect(Collectors.toSet());
+ if (customerIds.isEmpty()) {
+ return Map.of();
+ }
+
+ LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
+ wrapper.in(AccountSalesCollection::getCustomerId, customerIds);
+ List<AccountSalesCollection> collections = defaultList(accountSalesCollectionMapper.selectList(wrapper));
+ if (collections.isEmpty()) {
+ return Map.of();
+ }
+
+ Map<Integer, Set<Long>> ledgerIdsByCollectionId = mapCollectionLedgerIds(loginUser, collections);
+ Map<Long, List<Long>> ledgerIdsByCustomerId = ledgers.stream()
+ .filter(item -> item.getId() != null && item.getCustomerId() != null)
+ .collect(Collectors.groupingBy(item -> item.getCustomerId().longValue(),
+ Collectors.mapping(SalesLedger::getId, Collectors.toList())));
+ Set<Long> targetLedgerIdSet = new HashSet<>(ledgerIds);
+
+ Map<Long, BigDecimal> result = new HashMap<>();
+ for (AccountSalesCollection collection : collections) {
+ BigDecimal amount = defaultDecimal(collection.getCollectionAmount());
+ if (amount.compareTo(BigDecimal.ZERO) == 0) {
+ continue;
+ }
+ Set<Long> relatedLedgerIds = ledgerIdsByCollectionId.getOrDefault(collection.getId(), Set.of());
+ if (!relatedLedgerIds.isEmpty()) {
+ for (Long ledgerId : relatedLedgerIds) {
+ if (targetLedgerIdSet.contains(ledgerId)) {
+ result.merge(ledgerId, amount, BigDecimal::add);
+ }
+ }
+ continue;
+ }
+ if (collection.getCustomerId() == null) {
+ continue;
+ }
+ List<Long> customerLedgerIds = ledgerIdsByCustomerId.get(collection.getCustomerId().longValue());
+ if (customerLedgerIds == null || customerLedgerIds.isEmpty()) {
+ continue;
+ }
+ for (Long ledgerId : customerLedgerIds) {
+ if (targetLedgerIdSet.contains(ledgerId)) {
+ result.merge(ledgerId, amount, BigDecimal::add);
+ }
+ }
+ }
return result;
+ }
+
+ private List<AccountSalesCollection> queryCollections(LoginUser loginUser, DateRange range) {
+ LambdaQueryWrapper<AccountSalesCollection> wrapper = new LambdaQueryWrapper<>();
+ applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), AccountSalesCollection::getDeptId);
+ if (range != null) {
+ wrapper.ge(AccountSalesCollection::getCollectionDate, range.start())
+ .le(AccountSalesCollection::getCollectionDate, range.end());
+ }
+ wrapper.orderByDesc(AccountSalesCollection::getCollectionDate, AccountSalesCollection::getId);
+ return defaultList(accountSalesCollectionMapper.selectList(wrapper));
+ }
+
+ private Map<Integer, Set<Long>> mapCollectionLedgerIds(LoginUser loginUser, List<AccountSalesCollection> collections) {
+ Map<Integer, Set<Long>> result = new HashMap<>();
+ if (collections == null || collections.isEmpty()) {
+ return result;
+ }
+
+ Map<Integer, List<Long>> stockOutRecordIdsByCollection = new HashMap<>();
+ Set<Long> allStockOutRecordIds = new HashSet<>();
+ for (AccountSalesCollection collection : collections) {
+ if (collection.getId() == null) {
+ continue;
+ }
+ List<Long> stockOutRecordIds = parseLongIds(collection.getStockOutRecordIds());
+ if (stockOutRecordIds.isEmpty()) {
+ continue;
+ }
+ stockOutRecordIdsByCollection.put(collection.getId(), stockOutRecordIds);
+ allStockOutRecordIds.addAll(stockOutRecordIds);
+ }
+ if (allStockOutRecordIds.isEmpty()) {
+ return result;
+ }
+
+ List<StockOutRecord> stockOutRecords = defaultList(stockOutRecordMapper.selectList(new LambdaQueryWrapper<StockOutRecord>()
+ .in(StockOutRecord::getId, allStockOutRecordIds)));
+ if (stockOutRecords.isEmpty()) {
+ return result;
+ }
+ Map<Long, StockOutRecord> stockOutRecordMap = stockOutRecords.stream()
+ .filter(item -> item.getId() != null)
+ .collect(Collectors.toMap(StockOutRecord::getId, item -> item, (a, b) -> a));
+
+ Set<Long> shippingIds = stockOutRecords.stream()
+ .filter(this::isSalesOutboundRecord)
+ .map(StockOutRecord::getRecordId)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toSet());
+ if (shippingIds.isEmpty()) {
+ return result;
+ }
+
+ LambdaQueryWrapper<ShippingInfo> shippingWrapper = new LambdaQueryWrapper<>();
+ applyTenantFilter(shippingWrapper, loginUser.getTenantId(), ShippingInfo::getTenantId);
+ applyDeptFilter(shippingWrapper, loginUser.getCurrentDeptId(), ShippingInfo::getDeptId);
+ shippingWrapper.in(ShippingInfo::getId, shippingIds);
+ Map<Long, Long> ledgerIdByShippingId = defaultList(shippingInfoMapper.selectList(shippingWrapper)).stream()
+ .filter(item -> item.getId() != null && item.getSalesLedgerId() != null)
+ .collect(Collectors.toMap(ShippingInfo::getId, ShippingInfo::getSalesLedgerId, (a, b) -> a));
+
+ for (Map.Entry<Integer, List<Long>> entry : stockOutRecordIdsByCollection.entrySet()) {
+ Set<Long> ledgerIds = new LinkedHashSet<>();
+ for (Long stockOutRecordId : entry.getValue()) {
+ StockOutRecord stockOutRecord = stockOutRecordMap.get(stockOutRecordId);
+ if (!isSalesOutboundRecord(stockOutRecord)) {
+ continue;
+ }
+ Long ledgerId = ledgerIdByShippingId.get(stockOutRecord.getRecordId());
+ if (ledgerId != null) {
+ ledgerIds.add(ledgerId);
+ }
+ }
+ if (!ledgerIds.isEmpty()) {
+ result.put(entry.getKey(), ledgerIds);
+ }
+ }
+ return result;
+ }
+
+ private boolean isSalesOutboundRecord(StockOutRecord stockOutRecord) {
+ if (stockOutRecord == null || !StringUtils.hasText(stockOutRecord.getRecordType())) {
+ return false;
+ }
+ if (stockOutRecord.getApprovalStatus() != null && stockOutRecord.getApprovalStatus() != 1) {
+ return false;
+ }
+ return "13".equals(stockOutRecord.getRecordType().trim());
+ }
+
+ private List<Long> parseLongIds(String raw) {
+ if (!StringUtils.hasText(raw)) {
+ return List.of();
+ }
+ List<Long> result = new ArrayList<>();
+ for (String part : raw.split(",")) {
+ if (!StringUtils.hasText(part)) {
+ continue;
+ }
+ try {
+ result.add(Long.parseLong(part.trim()));
+ } catch (Exception ignored) {
+ }
+ }
+ return result;
+ }
+
+ private boolean matchInteractionKeyword(AccountSalesCollection collection, SalesLedger ledger, String keyword) {
+ if (!StringUtils.hasText(keyword)) {
+ return true;
+ }
+ String text = keyword.trim();
+ if (safe(collection.getCollectionNumber()).contains(text)
+ || safe(collection.getCollectionMethod()).contains(text)
+ || safe(collection.getRemark()).contains(text)) {
+ return true;
+ }
+ if (ledger == null) {
+ return false;
+ }
+ return safe(ledger.getSalesContractNo()).contains(text)
+ || safe(ledger.getCustomerName()).contains(text)
+ || safe(ledger.getProjectName()).contains(text);
+ }
+
+ private Map<String, Object> toInteractionItem(AccountSalesCollection collection, SalesLedger ledger) {
+ Map<String, Object> map = new LinkedHashMap<>();
+ map.put("id", collection.getId());
+ map.put("salesLedgerId", ledger == null ? null : ledger.getId());
+ 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(collection.getCollectionDate()));
+ map.put("receiptPaymentAmount", collection.getCollectionAmount());
+ map.put("receiptPaymentType", safe(collection.getCollectionMethod()));
+ map.put("collectionNumber", safe(collection.getCollectionNumber()));
+ map.put("registrant", collection.getCreateUser());
+ map.put("remark", safe(collection.getRemark()));
+ return map;
}
private boolean isLedgerFullyShipped(Long ledgerId, Map<Long, List<ShippingInfo>> shippingByLedgerId) {
@@ -1090,6 +1403,23 @@
return value == null ? BigDecimal.ZERO : value;
}
+ private BigDecimal asBigDecimal(Object value) {
+ if (value == null) {
+ return BigDecimal.ZERO;
+ }
+ if (value instanceof BigDecimal decimal) {
+ return decimal;
+ }
+ if (value instanceof Number number) {
+ return new BigDecimal(String.valueOf(number));
+ }
+ try {
+ return new BigDecimal(String.valueOf(value));
+ } catch (Exception ignored) {
+ return BigDecimal.ZERO;
+ }
+ }
+
private BigDecimal maxZero(BigDecimal value) {
return value == null || value.compareTo(BigDecimal.ZERO) < 0 ? BigDecimal.ZERO : value;
}
--
Gitblit v1.9.3