package com.ruoyi.ai.tools;
|
|
import com.alibaba.fastjson2.JSON;
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
import com.ruoyi.ai.context.AiSessionUserContext;
|
import com.ruoyi.common.utils.SecurityUtils;
|
import com.ruoyi.framework.security.LoginUser;
|
import com.ruoyi.purchase.mapper.InvoicePurchaseMapper;
|
import com.ruoyi.purchase.mapper.PaymentRegistrationMapper;
|
import com.ruoyi.purchase.mapper.PurchaseLedgerMapper;
|
import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper;
|
import com.ruoyi.purchase.pojo.InvoicePurchase;
|
import com.ruoyi.purchase.pojo.PaymentRegistration;
|
import com.ruoyi.purchase.pojo.PurchaseLedger;
|
import com.ruoyi.purchase.pojo.PurchaseReturnOrders;
|
import com.ruoyi.procurementrecord.mapper.InboundManagementMapper;
|
import com.ruoyi.procurementrecord.mapper.ProcurementRecordMapper;
|
import com.ruoyi.procurementrecord.pojo.InboundManagement;
|
import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage;
|
import com.ruoyi.sales.mapper.SalesLedgerProductMapper;
|
import com.ruoyi.sales.pojo.SalesLedgerProduct;
|
import dev.langchain4j.agent.tool.P;
|
import dev.langchain4j.agent.tool.Tool;
|
import dev.langchain4j.agent.tool.ToolMemoryId;
|
import org.springframework.stereotype.Component;
|
import org.springframework.util.StringUtils;
|
|
import java.math.BigDecimal;
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.ZoneId;
|
import java.time.format.DateTimeFormatter;
|
import java.util.Date;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Comparator;
|
import java.util.stream.Collectors;
|
|
@Component
|
public class PurchaseAgentTools {
|
|
private static final DateTimeFormatter DATE_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
private static final int DEFAULT_LIMIT = 10;
|
private static final int MAX_LIMIT = 30;
|
|
private final PurchaseLedgerMapper purchaseLedgerMapper;
|
private final PaymentRegistrationMapper paymentRegistrationMapper;
|
private final InvoicePurchaseMapper invoicePurchaseMapper;
|
private final PurchaseReturnOrdersMapper purchaseReturnOrdersMapper;
|
private final SalesLedgerProductMapper salesLedgerProductMapper;
|
private final ProcurementRecordMapper procurementRecordMapper;
|
private final InboundManagementMapper inboundManagementMapper;
|
private final AiSessionUserContext aiSessionUserContext;
|
|
public PurchaseAgentTools(PurchaseLedgerMapper purchaseLedgerMapper,
|
PaymentRegistrationMapper paymentRegistrationMapper,
|
InvoicePurchaseMapper invoicePurchaseMapper,
|
PurchaseReturnOrdersMapper purchaseReturnOrdersMapper,
|
SalesLedgerProductMapper salesLedgerProductMapper,
|
ProcurementRecordMapper procurementRecordMapper,
|
InboundManagementMapper inboundManagementMapper,
|
AiSessionUserContext aiSessionUserContext) {
|
this.purchaseLedgerMapper = purchaseLedgerMapper;
|
this.paymentRegistrationMapper = paymentRegistrationMapper;
|
this.invoicePurchaseMapper = invoicePurchaseMapper;
|
this.purchaseReturnOrdersMapper = purchaseReturnOrdersMapper;
|
this.salesLedgerProductMapper = salesLedgerProductMapper;
|
this.procurementRecordMapper = procurementRecordMapper;
|
this.inboundManagementMapper = inboundManagementMapper;
|
this.aiSessionUserContext = aiSessionUserContext;
|
}
|
|
@Tool(name = "查询采购台账列表", value = "按关键字和时间范围查询采购台账,支持返回最近N条")
|
public String listPurchaseLedgers(@ToolMemoryId String memoryId,
|
@P(value = "关键字,可匹配采购合同号/供应商/项目名", required = false) String keyword,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "返回条数,默认10,最大30", required = false) Integer limit) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
LocalDate start = parseLocalDate(startDate);
|
LocalDate end = parseLocalDate(endDate);
|
int finalLimit = normalizeLimit(limit);
|
|
LambdaQueryWrapper<PurchaseLedger> wrapper = new LambdaQueryWrapper<>();
|
applyTenantFilter(wrapper, loginUser.getTenantId(), PurchaseLedger::getTenantId);
|
if (StringUtils.hasText(keyword)) {
|
wrapper.and(w -> w.like(PurchaseLedger::getPurchaseContractNumber, keyword)
|
.or().like(PurchaseLedger::getSupplierName, keyword)
|
.or().like(PurchaseLedger::getProjectName, keyword));
|
}
|
if (start != null) {
|
wrapper.ge(PurchaseLedger::getEntryDate, toDate(start));
|
}
|
if (end != null) {
|
wrapper.lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(end));
|
}
|
wrapper.orderByDesc(PurchaseLedger::getEntryDate, PurchaseLedger::getId).last("limit " + finalLimit);
|
|
List<PurchaseLedger> rows = defaultList(purchaseLedgerMapper.selectList(wrapper));
|
List<Map<String, Object>> items = rows.stream().map(this::toLedgerItem).collect(Collectors.toList());
|
return jsonResponse(true, "purchase_ledger_list", "已返回采购台账列表",
|
Map.of("count", items.size(), "limit", finalLimit, "keyword", safe(keyword)),
|
Map.of("items", items), Map.of());
|
}
|
|
@Tool(name = "查询采购台账详情", value = "按采购台账ID查询详情")
|
public String getPurchaseLedgerDetail(@ToolMemoryId String memoryId, @P("采购台账ID") Long ledgerId) {
|
if (ledgerId == null) {
|
return jsonResponse(false, "purchase_ledger_detail", "采购台账ID不能为空", Map.of(), Map.of(), Map.of());
|
}
|
LoginUser loginUser = currentLoginUser(memoryId);
|
PurchaseLedger ledger = purchaseLedgerMapper.selectById(ledgerId);
|
if (ledger == null || !tenantMatched(ledger.getTenantId(), loginUser.getTenantId())) {
|
return jsonResponse(false, "purchase_ledger_detail", "未找到该采购台账或无权限访问", Map.of("ledgerId", ledgerId), Map.of(), Map.of());
|
}
|
return jsonResponse(true, "purchase_ledger_detail", "已返回采购台账详情",
|
Map.of("ledgerId", ledgerId),
|
Map.of("detail", toLedgerItem(ledger)),
|
Map.of());
|
}
|
|
@Tool(name = "统计采购数据", value = "统计时间范围内采购合同数、合同金额、付款金额、发票金额、退货金额")
|
public String getPurchaseStats(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "时间范围描述,例如今年、本月、近30天", required = false) String timeRange) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
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<PurchaseReturnOrders> returns = queryReturns(loginUser, range);
|
|
BigDecimal contractAmount = ledgers.stream()
|
.map(PurchaseLedger::getContractAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal paymentAmount = payments.stream()
|
.map(PaymentRegistration::getCurrentPaymentAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal invoiceAmount = invoices.stream()
|
.map(InvoicePurchase::getInvoiceAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
BigDecimal returnAmount = returns.stream()
|
.map(PurchaseReturnOrders::getTotalAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
Map<String, Object> summary = new LinkedHashMap<>();
|
summary.put("timeRange", range.label());
|
summary.put("startDate", range.start().toString());
|
summary.put("endDate", range.end().toString());
|
summary.put("ledgerCount", ledgers.size());
|
summary.put("paymentCount", payments.size());
|
summary.put("invoiceCount", invoices.size());
|
summary.put("returnCount", returns.size());
|
summary.put("contractAmount", contractAmount);
|
summary.put("paymentAmount", paymentAmount);
|
summary.put("invoiceAmount", invoiceAmount);
|
summary.put("returnAmount", returnAmount);
|
|
return jsonResponse(true, "purchase_stats", "已返回采购统计数据", summary, Map.of(), Map.of());
|
}
|
|
@Tool(name = "采购物料金额排行", value = "按时间范围统计采购物料金额排行,可回答本月采购金额排名靠前的物料。")
|
public String rankPurchaseMaterials(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "时间范围描述,例如本月、近7天、近30天", required = false) String timeRange,
|
@P(value = "返回条数,默认10,最大30", required = false) Integer limit) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
DateRange range = resolveDateRange(startDate, endDate, timeRange);
|
List<Long> ledgerIds = queryLedgers(loginUser, range).stream()
|
.map(PurchaseLedger::getId)
|
.filter(Objects::nonNull)
|
.collect(Collectors.toList());
|
if (ledgerIds.isEmpty()) {
|
return jsonResponse(true, "purchase_material_rank", "当前时间范围内没有采购物料数据。",
|
rangeSummary(range, 0), Map.of("items", List.of()), Map.of());
|
}
|
|
List<SalesLedgerProduct> products = defaultList(salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
|
.eq(SalesLedgerProduct::getType, 2)
|
.in(SalesLedgerProduct::getSalesLedgerId, ledgerIds)));
|
|
Map<String, MaterialRankItem> grouped = new LinkedHashMap<>();
|
for (SalesLedgerProduct product : products) {
|
String name = safe(product.getProductCategory());
|
String model = safe(product.getSpecificationModel());
|
String key = name + "|" + model;
|
MaterialRankItem item = grouped.computeIfAbsent(key, ignored -> new MaterialRankItem(name, model, safe(product.getUnit())));
|
item.quantity = item.quantity.add(defaultDecimal(product.getQuantity()));
|
item.amount = item.amount.add(defaultDecimal(product.getTaxInclusiveTotalPrice()));
|
}
|
|
List<Map<String, Object>> items = grouped.values().stream()
|
.sorted(Comparator.comparing((MaterialRankItem item) -> item.amount).reversed())
|
.limit(normalizeLimit(limit))
|
.map(MaterialRankItem::toMap)
|
.collect(Collectors.toList());
|
|
return jsonResponse(true, "purchase_material_rank", "已返回采购物料金额排行。",
|
rangeSummary(range, items.size()), Map.of("items", items), Map.of());
|
}
|
|
@Tool(name = "查询未入库采购订单", value = "查询采购订单下仍有待入库数量的物料明细。")
|
public String listUnstockedPurchaseOrders(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "关键字,可匹配采购合同号/供应商/物料", required = false) String keyword,
|
@P(value = "返回条数,默认10,最大30", required = false) Integer limit) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
DateRange range = resolveDateRange(startDate, endDate, null);
|
List<PurchaseLedger> ledgers = queryLedgers(loginUser, range).stream()
|
.filter(ledger -> matchLedgerKeyword(ledger, keyword))
|
.collect(Collectors.toList());
|
Map<Long, PurchaseLedger> ledgerMap = ledgers.stream()
|
.filter(ledger -> ledger.getId() != null)
|
.collect(Collectors.toMap(PurchaseLedger::getId, ledger -> ledger, (a, b) -> a, LinkedHashMap::new));
|
if (ledgerMap.isEmpty()) {
|
return jsonResponse(true, "purchase_unstocked_list", "未查询到符合条件的采购订单。",
|
rangeSummary(range, 0), Map.of("items", List.of()), Map.of());
|
}
|
|
List<SalesLedgerProduct> products = defaultList(salesLedgerProductMapper.selectList(new LambdaQueryWrapper<SalesLedgerProduct>()
|
.eq(SalesLedgerProduct::getType, 2)
|
.in(SalesLedgerProduct::getSalesLedgerId, ledgerMap.keySet())));
|
|
List<Map<String, Object>> items = products.stream()
|
.filter(product -> matchProductKeyword(product, keyword))
|
.map(product -> toUnstockedItem(product, ledgerMap.get(product.getSalesLedgerId())))
|
.filter(Objects::nonNull)
|
.limit(normalizeLimit(limit))
|
.collect(Collectors.toList());
|
|
return jsonResponse(true, "purchase_unstocked_list", "已返回未入库采购订单。",
|
rangeSummary(range, items.size()), Map.of("items", items), Map.of());
|
}
|
|
@Tool(name = "查询采购到货异常", value = "查询到货状态异常或备注包含异常信息的到货记录。")
|
public String listArrivalExceptions(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "时间范围描述,例如近7天、本月", required = false) String timeRange,
|
@P(value = "返回条数,默认10,最大30", required = false) Integer limit) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
DateRange range = resolveDateRange(startDate, endDate, timeRange);
|
LambdaQueryWrapper<InboundManagement> wrapper = new LambdaQueryWrapper<>();
|
applyTenantFilter(wrapper, loginUser.getTenantId(), InboundManagement::getTenantId);
|
wrapper.ge(InboundManagement::getArrivalTime, toDate(range.start()))
|
.lt(InboundManagement::getArrivalTime, toExclusiveEndDate(range.end()))
|
.and(w -> w.notLike(InboundManagement::getStatus, "正常")
|
.notLike(InboundManagement::getStatus, "完成")
|
.notLike(InboundManagement::getStatus, "已到货")
|
.or().like(InboundManagement::getStatus, "异常")
|
.or().like(InboundManagement::getRemark, "异常")
|
.or().like(InboundManagement::getRemark, "问题")
|
.or().like(InboundManagement::getRemark, "延迟")
|
.or().like(InboundManagement::getRemark, "短缺"));
|
wrapper.orderByDesc(InboundManagement::getArrivalTime).last("limit " + normalizeLimit(limit));
|
|
List<Map<String, Object>> items = defaultList(inboundManagementMapper.selectList(wrapper)).stream()
|
.map(this::toArrivalItem)
|
.collect(Collectors.toList());
|
return jsonResponse(true, "purchase_arrival_exception_list", "已返回采购到货异常记录。",
|
rangeSummary(range, items.size()), Map.of("items", items), Map.of());
|
}
|
|
@Tool(name = "查询待付款采购单", value = "查询合同金额大于已付款金额的采购单。")
|
public String listPendingPaymentOrders(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "关键字,可匹配采购合同号/供应商/项目名", required = false) String keyword,
|
@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()
|
.filter(ledger -> matchLedgerKeyword(ledger, keyword))
|
.map(ledger -> toPendingPaymentItem(loginUser, ledger))
|
.filter(Objects::nonNull)
|
.sorted(Comparator.comparing(item -> (BigDecimal) item.get("pendingAmount"), Comparator.reverseOrder()))
|
.limit(normalizeLimit(limit))
|
.collect(Collectors.toList());
|
return jsonResponse(true, "purchase_pending_payment_list", "已返回待付款采购单。",
|
rangeSummary(range, items.size()), Map.of("items", items), Map.of());
|
}
|
|
@Tool(name = "查询采购退货情况", value = "按时间范围查询采购退货单列表和退货金额。")
|
public String listPurchaseReturns(@ToolMemoryId String memoryId,
|
@P(value = "开始日期 yyyy-MM-dd", required = false) String startDate,
|
@P(value = "结束日期 yyyy-MM-dd", required = false) String endDate,
|
@P(value = "关键字,可匹配退货单号/备注", required = false) String keyword,
|
@P(value = "返回条数,默认10,最大30", required = false) Integer limit) {
|
LoginUser loginUser = currentLoginUser(memoryId);
|
DateRange range = resolveDateRange(startDate, endDate, null);
|
LambdaQueryWrapper<PurchaseReturnOrders> wrapper = new LambdaQueryWrapper<>();
|
applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), PurchaseReturnOrders::getDeptId);
|
wrapper.ge(PurchaseReturnOrders::getPreparedAt, range.start())
|
.le(PurchaseReturnOrders::getPreparedAt, range.end());
|
if (StringUtils.hasText(keyword)) {
|
wrapper.and(w -> w.like(PurchaseReturnOrders::getNo, keyword)
|
.or().like(PurchaseReturnOrders::getRemark, keyword)
|
.or().like(PurchaseReturnOrders::getReturnUserName, keyword));
|
}
|
wrapper.orderByDesc(PurchaseReturnOrders::getPreparedAt).last("limit " + normalizeLimit(limit));
|
|
List<PurchaseReturnOrders> returns = defaultList(purchaseReturnOrdersMapper.selectList(wrapper));
|
BigDecimal totalAmount = returns.stream()
|
.map(PurchaseReturnOrders::getTotalAmount)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
Map<String, Object> summary = rangeSummary(range, returns.size());
|
summary.put("returnAmount", totalAmount);
|
|
return jsonResponse(true, "purchase_return_list", "已返回采购退货情况。",
|
summary,
|
Map.of("items", returns.stream().map(this::toReturnItem).collect(Collectors.toList())),
|
Map.of());
|
}
|
|
private List<PurchaseLedger> queryLedgers(LoginUser loginUser, DateRange range) {
|
LambdaQueryWrapper<PurchaseLedger> wrapper = new LambdaQueryWrapper<>();
|
applyTenantFilter(wrapper, loginUser.getTenantId(), PurchaseLedger::getTenantId);
|
wrapper.ge(PurchaseLedger::getEntryDate, toDate(range.start()))
|
.lt(PurchaseLedger::getEntryDate, toExclusiveEndDate(range.end()));
|
return defaultList(purchaseLedgerMapper.selectList(wrapper));
|
}
|
|
private Map<String, Object> rangeSummary(DateRange range, int count) {
|
Map<String, Object> summary = new LinkedHashMap<>();
|
summary.put("timeRange", range.label());
|
summary.put("startDate", range.start().toString());
|
summary.put("endDate", range.end().toString());
|
summary.put("count", count);
|
return summary;
|
}
|
|
private boolean matchLedgerKeyword(PurchaseLedger ledger, String keyword) {
|
if (!StringUtils.hasText(keyword)) {
|
return true;
|
}
|
String text = keyword.trim();
|
return safe(ledger.getPurchaseContractNumber()).contains(text)
|
|| safe(ledger.getSupplierName()).contains(text)
|
|| safe(ledger.getProjectName()).contains(text);
|
}
|
|
private boolean matchProductKeyword(SalesLedgerProduct product, String keyword) {
|
if (!StringUtils.hasText(keyword)) {
|
return true;
|
}
|
String text = keyword.trim();
|
return safe(product.getProductCategory()).contains(text)
|
|| safe(product.getSpecificationModel()).contains(text);
|
}
|
|
private Map<String, Object> toUnstockedItem(SalesLedgerProduct product, PurchaseLedger ledger) {
|
if (product == null || ledger == null || product.getId() == null) {
|
return null;
|
}
|
BigDecimal orderedQuantity = defaultDecimal(product.getQuantity());
|
BigDecimal inboundQuantity = sumInboundQuantity(product.getId());
|
BigDecimal pendingQuantity = orderedQuantity.subtract(inboundQuantity);
|
if (pendingQuantity.compareTo(BigDecimal.ZERO) <= 0) {
|
return null;
|
}
|
Map<String, Object> item = new LinkedHashMap<>();
|
item.put("purchaseLedgerId", ledger.getId());
|
item.put("purchaseContractNumber", safe(ledger.getPurchaseContractNumber()));
|
item.put("supplierName", safe(ledger.getSupplierName()));
|
item.put("productCategory", safe(product.getProductCategory()));
|
item.put("specificationModel", safe(product.getSpecificationModel()));
|
item.put("unit", safe(product.getUnit()));
|
item.put("orderedQuantity", orderedQuantity);
|
item.put("inboundQuantity", inboundQuantity);
|
item.put("pendingInboundQuantity", pendingQuantity);
|
item.put("entryDate", formatDate(ledger.getEntryDate()));
|
return item;
|
}
|
|
private BigDecimal sumInboundQuantity(Long salesLedgerProductId) {
|
List<ProcurementRecordStorage> records = defaultList(procurementRecordMapper.selectList(new LambdaQueryWrapper<ProcurementRecordStorage>()
|
.eq(ProcurementRecordStorage::getType, 1)
|
.eq(ProcurementRecordStorage::getSalesLedgerProductId, salesLedgerProductId)));
|
return records.stream()
|
.map(ProcurementRecordStorage::getInboundNum)
|
.filter(Objects::nonNull)
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
}
|
|
private Map<String, Object> toArrivalItem(InboundManagement item) {
|
Map<String, Object> map = new LinkedHashMap<>();
|
map.put("id", item.getId());
|
map.put("orderNo", safe(item.getOrderNo()));
|
map.put("arrivalNo", safe(item.getArrivalNo()));
|
map.put("supplierName", safe(item.getSupplierName()));
|
map.put("status", safe(item.getStatus()));
|
map.put("arrivalTime", formatDate(item.getArrivalTime()));
|
map.put("arrivalQuantity", safe(item.getArrivalQuantity()));
|
map.put("remark", safe(item.getRemark()));
|
return map;
|
}
|
|
private Map<String, Object> toPendingPaymentItem(LoginUser loginUser, PurchaseLedger ledger) {
|
BigDecimal contractAmount = defaultDecimal(ledger.getContractAmount());
|
BigDecimal paidAmount = sumPaymentAmount(loginUser, ledger.getId());
|
BigDecimal pendingAmount = contractAmount.subtract(paidAmount);
|
if (pendingAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
return null;
|
}
|
Map<String, Object> item = toLedgerItem(ledger);
|
item.put("paidAmount", paidAmount);
|
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);
|
}
|
|
private Map<String, Object> toReturnItem(PurchaseReturnOrders item) {
|
Map<String, Object> map = new LinkedHashMap<>();
|
map.put("id", item.getId());
|
map.put("no", safe(item.getNo()));
|
map.put("returnType", item.getReturnType());
|
map.put("purchaseLedgerId", item.getPurchaseLedgerId());
|
map.put("preparedAt", item.getPreparedAt() == null ? "" : item.getPreparedAt().toString());
|
map.put("returnUserName", safe(item.getReturnUserName()));
|
map.put("totalAmount", item.getTotalAmount());
|
map.put("remark", safe(item.getRemark()));
|
return map;
|
}
|
|
private BigDecimal defaultDecimal(BigDecimal value) {
|
return value == null ? BigDecimal.ZERO : value;
|
}
|
|
private List<PaymentRegistration> queryPayments(LoginUser loginUser, DateRange range) {
|
LambdaQueryWrapper<PaymentRegistration> wrapper = new LambdaQueryWrapper<>();
|
applyTenantFilter(wrapper, loginUser.getTenantId(), PaymentRegistration::getTenantId);
|
wrapper.ge(PaymentRegistration::getPaymentDate, toDate(range.start()))
|
.lt(PaymentRegistration::getPaymentDate, toExclusiveEndDate(range.end()));
|
return defaultList(paymentRegistrationMapper.selectList(wrapper));
|
}
|
|
private List<InvoicePurchase> queryInvoices(LoginUser loginUser, DateRange range) {
|
LambdaQueryWrapper<InvoicePurchase> wrapper = new LambdaQueryWrapper<>();
|
applyTenantFilter(wrapper, loginUser.getTenantId(), InvoicePurchase::getTenantId);
|
wrapper.ge(InvoicePurchase::getIssueDate, range.start())
|
.le(InvoicePurchase::getIssueDate, range.end());
|
return defaultList(invoicePurchaseMapper.selectList(wrapper));
|
}
|
|
private List<PurchaseReturnOrders> queryReturns(LoginUser loginUser, DateRange range) {
|
LambdaQueryWrapper<PurchaseReturnOrders> wrapper = new LambdaQueryWrapper<>();
|
applyDeptFilter(wrapper, loginUser.getCurrentDeptId(), PurchaseReturnOrders::getDeptId);
|
wrapper.ge(PurchaseReturnOrders::getPreparedAt, range.start())
|
.le(PurchaseReturnOrders::getPreparedAt, range.end());
|
return defaultList(purchaseReturnOrdersMapper.selectList(wrapper));
|
}
|
|
private Map<String, Object> toLedgerItem(PurchaseLedger item) {
|
Map<String, Object> map = new LinkedHashMap<>();
|
map.put("id", item.getId());
|
map.put("purchaseContractNumber", safe(item.getPurchaseContractNumber()));
|
map.put("supplierName", safe(item.getSupplierName()));
|
map.put("projectName", safe(item.getProjectName()));
|
map.put("entryDate", formatDate(item.getEntryDate()));
|
map.put("contractAmount", item.getContractAmount());
|
map.put("approvalStatus", item.getApprovalStatus());
|
map.put("paymentMethod", safe(item.getPaymentMethod()));
|
return map;
|
}
|
|
private DateRange resolveDateRange(String startDate, String endDate, String timeRange) {
|
LocalDate today = LocalDate.now();
|
LocalDate start = parseLocalDate(startDate);
|
LocalDate end = parseLocalDate(endDate);
|
if (start != null || end != null) {
|
LocalDate s = start != null ? start : end;
|
LocalDate e = end != null ? end : start;
|
if (s.isAfter(e)) {
|
LocalDate temp = s;
|
s = e;
|
e = temp;
|
}
|
return new DateRange(s, e, s + "至" + e);
|
}
|
if (!StringUtils.hasText(timeRange)) {
|
return new DateRange(today.minusDays(29), today, "近30天");
|
}
|
String text = timeRange.trim();
|
if (text.contains("今年") || text.contains("本年")) {
|
return new DateRange(today.withDayOfYear(1), today, "今年");
|
}
|
if (text.contains("本月")) {
|
return new DateRange(today.withDayOfMonth(1), today, "本月");
|
}
|
if (text.contains("上月")) {
|
LocalDate first = today.minusMonths(1).withDayOfMonth(1);
|
LocalDate last = first.withDayOfMonth(first.lengthOfMonth());
|
return new DateRange(first, last, "上月");
|
}
|
if (text.contains("近半年") || text.contains("最近半年")) {
|
return new DateRange(today.minusMonths(6).plusDays(1), today, "近半年");
|
}
|
if (text.contains("近半个月") || text.contains("最近半个月") || text.contains("半个月")) {
|
return new DateRange(today.minusDays(14), today, "近半个月");
|
}
|
java.util.regex.Matcher relativeMatcher = java.util.regex.Pattern.compile("(近|最近)(\\d+)(天|周|个月|月|年)").matcher(text);
|
if (relativeMatcher.find()) {
|
int amount = Integer.parseInt(relativeMatcher.group(2));
|
String unit = relativeMatcher.group(3);
|
LocalDate relativeStart = switch (unit) {
|
case "天" -> today.minusDays(Math.max(amount - 1L, 0));
|
case "周" -> today.minusWeeks(Math.max(amount, 1)).plusDays(1);
|
case "个月", "月" -> today.minusMonths(Math.max(amount, 1)).plusDays(1);
|
case "年" -> today.minusYears(Math.max(amount, 1)).plusDays(1);
|
default -> today.minusDays(29);
|
};
|
return new DateRange(relativeStart, today, "近" + amount + unit);
|
}
|
return new DateRange(today.minusDays(29), today, "近30天");
|
}
|
|
private LocalDate parseLocalDate(String text) {
|
if (!StringUtils.hasText(text)) {
|
return null;
|
}
|
return LocalDate.parse(text.trim(), DATE_FMT);
|
}
|
|
private Date toDate(LocalDate localDate) {
|
return Date.from(localDate.atStartOfDay(ZoneId.systemDefault()).toInstant());
|
}
|
|
private Date toExclusiveEndDate(LocalDate localDate) {
|
return Date.from(localDate.plusDays(1).atStartOfDay(ZoneId.systemDefault()).toInstant());
|
}
|
|
private String formatDate(Date date) {
|
if (date == null) {
|
return "";
|
}
|
return DATE_FMT.format(date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate());
|
}
|
|
private boolean tenantMatched(Long dataTenantId, Long userTenantId) {
|
if (userTenantId == null) {
|
return true;
|
}
|
return Objects.equals(dataTenantId, userTenantId);
|
}
|
|
private <T> void applyTenantFilter(LambdaQueryWrapper<T> wrapper, Long tenantId, com.baomidou.mybatisplus.core.toolkit.support.SFunction<T, Long> field) {
|
if (tenantId != null) {
|
wrapper.eq(field, tenantId);
|
}
|
}
|
|
private <T> void applyDeptFilter(LambdaQueryWrapper<T> wrapper, Long deptId, com.baomidou.mybatisplus.core.toolkit.support.SFunction<T, Long> field) {
|
if (deptId != null) {
|
wrapper.eq(field, deptId);
|
}
|
}
|
|
private LoginUser currentLoginUser(String memoryId) {
|
LoginUser loginUser = aiSessionUserContext.get(memoryId);
|
if (loginUser != null) {
|
return loginUser;
|
}
|
return SecurityUtils.getLoginUser();
|
}
|
|
private int normalizeLimit(Integer limit) {
|
if (limit == null || limit <= 0) {
|
return DEFAULT_LIMIT;
|
}
|
return Math.min(limit, MAX_LIMIT);
|
}
|
|
private String safe(Object value) {
|
return value == null ? "" : String.valueOf(value).replace('\n', ' ').replace('\r', ' ');
|
}
|
|
private <T> List<T> defaultList(List<T> list) {
|
return list == null ? List.of() : list;
|
}
|
|
private String jsonResponse(boolean success,
|
String type,
|
String description,
|
Map<String, Object> summary,
|
Map<String, Object> data,
|
Map<String, Object> charts) {
|
Map<String, Object> result = new LinkedHashMap<>();
|
result.put("success", success);
|
result.put("type", type);
|
result.put("description", description);
|
result.put("summary", summary == null ? Map.of() : summary);
|
result.put("data", data == null ? Map.of() : data);
|
result.put("charts", charts == null ? Map.of() : charts);
|
return JSON.toJSONString(result);
|
}
|
|
private record DateRange(LocalDate start, LocalDate end, String label) {
|
}
|
|
private static class MaterialRankItem {
|
private final String productCategory;
|
private final String specificationModel;
|
private final String unit;
|
private BigDecimal quantity = BigDecimal.ZERO;
|
private BigDecimal amount = BigDecimal.ZERO;
|
|
private MaterialRankItem(String productCategory, String specificationModel, String unit) {
|
this.productCategory = productCategory;
|
this.specificationModel = specificationModel;
|
this.unit = unit;
|
}
|
|
private Map<String, Object> toMap() {
|
Map<String, Object> map = new LinkedHashMap<>();
|
map.put("productCategory", productCategory);
|
map.put("specificationModel", specificationModel);
|
map.put("unit", unit);
|
map.put("quantity", quantity);
|
map.put("amount", amount);
|
return map;
|
}
|
}
|
}
|