refactor: 抽取正式结算单收款汇总批量装载公共逻辑统一读写口径

This commit is contained in:
刘泉佳
2026-11-05 15:09:25 +08:00
parent 1b811181a8
commit fd82d9604f
@@ -233,8 +233,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
/**
* 列表页批量 Entity → VO:收款汇总各数据源与预结算单号按本页一次批量查询后内存分发,
* 计算口径与单笔版 toVO 完全一致(复用 calculatePaymentSummary 纯计算重载),
* 固定约 6 次查询替代逐行 toVO 的 4~5 次/行(100 行约 500 次 SQL)。
* 计算口径与单笔版 toVO 完全一致,且与 refreshPaymentSummaries 写回路径共用批量装载与计算(loadPaymentSummaryBatchData)。
*
* @param entities 本页结算单
* @return 本页 VO 列表
@@ -243,9 +242,33 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
if (Func.isEmpty(entities)) {
return new ArrayList<>();
}
List<Long> ids = entities.stream().map(FormalSettlement::getId).toList();
boolean hasPayable = entities.stream().anyMatch(item -> "payable".equals(item.getSettlementType()));
boolean hasReceivable = entities.stream().anyMatch(item -> "receivable".equals(item.getSettlementType()));
PaymentSummaryBatchData batch = this.loadPaymentSummaryBatchData(
entities.stream().map(FormalSettlement::getId).toList());
List<FormalSettlementVO> voList = new ArrayList<>(entities.size());
for (FormalSettlement entity : entities) {
FormalSettlementVO vo = FormalSettlementWrapper.build().entityVO(entity);
vo.setInvoiceStatus(this.invoiceStatus(entity.getInvoiceAmount(), entity.getSettlementAmount()));
PaymentSummary summary = this.paymentSummaryFor(entity, batch);
vo.setAppliedPaymentAmount(summary.appliedAmount());
vo.setPaidAmount(summary.paidAmount());
vo.setRemainingPayableAmount(this.money(entity.getSettlementAmount()).subtract(summary.paidAmount())
.max(BigDecimal.ZERO));
vo.setPreSettlementNos(batch.sourcesBySettlement().getOrDefault(entity.getId(), List.of()).stream()
.map(FormalSettlementSource::getPreSettlementNo).collect(Collectors.joining(",")));
voList.add(vo);
}
return voList;
}
/**
* 批量收款汇总数据装载(列表页 toVOList 读路径与 refreshPaymentSummaries 写回路径共用):
* 固定 6 次查询取回本批结算单全部汇总数据源并按结算单id分组,替代两处各自维护的重复查询块。
*/
private PaymentSummaryBatchData loadPaymentSummaryBatchData(Collection<Long> settlementIds) {
List<Long> ids = settlementIds.stream().filter(Objects::nonNull).distinct().toList();
if (ids.isEmpty()) {
return new PaymentSummaryBatchData(Map.of(), Map.of(), Map.of(), Map.of(), Map.of(), Map.of());
}
// 结算-付款申请关系(全量)
Map<Long, List<PaymentApplicationSettlement>> relationsBySettlement = paymentApplicationSettlementMapper.selectList(
Wrappers.<PaymentApplicationSettlement>lambdaQuery()
@@ -275,53 +298,51 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
Wrappers.<FormalSettlementSource>lambdaQuery()
.in(FormalSettlementSource::getFormalSettlementId, ids)).stream()
.collect(Collectors.groupingBy(FormalSettlementSource::getFormalSettlementId));
// 收款认领分摊聚合(仅应收结算单需要)
Map<Long, BigDecimal> receiptClaimPaidBySettlement = hasReceivable
? receiptClaimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
// 收款认领分摊聚合(仅应收结算单消费)
Map<Long, BigDecimal> receiptClaimPaidBySettlement = receiptClaimSettlementMapper.selectList(
Wrappers.<ReceiptClaimSettlement>lambdaQuery()
.in(ReceiptClaimSettlement::getFormalSettlementId, ids)
.eq(ReceiptClaimSettlement::getStatus, 1)
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
.collect(Collectors.groupingBy(ReceiptClaimSettlement::getFormalSettlementId,
Collectors.reducing(BigDecimal.ZERO,
item -> this.money(item.getAllocatedReceiptAmount()), BigDecimal::add)))
: Map.of();
// 项目预付核销合计(仅应付结算单需要,一次 group-by 避免逐单查询)
Map<Long, BigDecimal> writeOffAmountsBySettlement = hasPayable
? this.projectAdvanceWriteOffService.sumBySettlementIds(
TransportBusiConstant.SETTLEMENT_BILL_FORMAL,
entities.stream().filter(item -> "payable".equals(item.getSettlementType()))
.map(FormalSettlement::getId).toList())
: Map.of();
item -> this.money(item.getAllocatedReceiptAmount()), BigDecimal::add)));
// 项目预付核销合计(仅应付结算单消费,一次 group-by 避免逐单查询)
Map<Long, BigDecimal> writeOffAmountsBySettlement = this.projectAdvanceWriteOffService.sumBySettlementIds(
TransportBusiConstant.SETTLEMENT_BILL_FORMAL, ids);
return new PaymentSummaryBatchData(relationsBySettlement, applicationsBySettlement, relationStatuses,
sourcesBySettlement, receiptClaimPaidBySettlement, writeOffAmountsBySettlement);
}
List<FormalSettlementVO> voList = new ArrayList<>(entities.size());
for (FormalSettlement entity : entities) {
FormalSettlementVO vo = FormalSettlementWrapper.build().entityVO(entity);
vo.setInvoiceStatus(this.invoiceStatus(entity.getInvoiceAmount(), entity.getSettlementAmount()));
List<PaymentApplicationSettlement> relations = relationsBySettlement
.getOrDefault(entity.getId(), List.of());
// 直挂付款申请需排除该结算单关系表已引用的申请(与单笔版 notIn 语义一致)
/**
* 单张结算单收款汇总纯计算:从批量装载数据取数组装
* (直挂付款申请排除该结算单关系表已引用的申请,与单笔版 notIn 语义一致)。
*/
private PaymentSummary paymentSummaryFor(FormalSettlement settlement, PaymentSummaryBatchData batch) {
List<PaymentApplicationSettlement> relations = batch.relationsBySettlement()
.getOrDefault(settlement.getId(), List.of());
Set<Long> ownRelationApplicationIds = relations.stream()
.map(PaymentApplicationSettlement::getPaymentApplicationId)
.filter(Objects::nonNull).collect(Collectors.toSet());
List<PaymentApplication> directApplications = applicationsBySettlement
.getOrDefault(entity.getId(), List.of()).stream()
List<PaymentApplication> directApplications = batch.applicationsBySettlement()
.getOrDefault(settlement.getId(), List.of()).stream()
.filter(item -> !ownRelationApplicationIds.contains(item.getId()))
.toList();
PaymentSummary summary = this.calculatePaymentSummary(entity, new PaymentSummaryData(
relations, directApplications, relationStatuses,
sourcesBySettlement.getOrDefault(entity.getId(), List.of()).stream()
return this.calculatePaymentSummary(settlement, new PaymentSummaryData(
relations, directApplications, batch.relationStatuses(),
batch.sourcesBySettlement().getOrDefault(settlement.getId(), List.of()).stream()
.filter(item -> Objects.equals(item.getIsDeleted(), 0)).toList(),
receiptClaimPaidBySettlement.getOrDefault(entity.getId(), BigDecimal.ZERO),
writeOffAmountsBySettlement));
vo.setAppliedPaymentAmount(summary.appliedAmount());
vo.setPaidAmount(summary.paidAmount());
vo.setRemainingPayableAmount(this.money(entity.getSettlementAmount()).subtract(summary.paidAmount())
.max(BigDecimal.ZERO));
vo.setPreSettlementNos(sourcesBySettlement.getOrDefault(entity.getId(), List.of()).stream()
.map(FormalSettlementSource::getPreSettlementNo).collect(Collectors.joining(",")));
voList.add(vo);
batch.receiptClaimPaidBySettlement().getOrDefault(settlement.getId(), BigDecimal.ZERO),
batch.writeOffAmountsBySettlement()));
}
return voList;
/**
* 批量收款汇总装载数据(结算单id → 各数据源分组)
*/
private record PaymentSummaryBatchData(Map<Long, List<PaymentApplicationSettlement>> relationsBySettlement,
Map<Long, List<PaymentApplication>> applicationsBySettlement, Map<Long, String> relationStatuses,
Map<Long, List<FormalSettlementSource>> sourcesBySettlement,
Map<Long, BigDecimal> receiptClaimPaidBySettlement, Map<Long, BigDecimal> writeOffAmountsBySettlement) {
}
/**
@@ -2191,72 +2212,9 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
return;
}
List<Long> settledIds = settlements.stream().map(FormalSettlement::getId).toList();
boolean hasPayable = settlements.stream().anyMatch(item -> "payable".equals(item.getSettlementType()));
boolean hasReceivable = settlements.stream().anyMatch(item -> "receivable".equals(item.getSettlementType()));
// 结算-付款申请关系(全量)
Map<Long, List<PaymentApplicationSettlement>> relationsBySettlement = paymentApplicationSettlementMapper.selectList(
Wrappers.<PaymentApplicationSettlement>lambdaQuery()
.in(PaymentApplicationSettlement::getFormalSettlementId, settledIds)
.eq(PaymentApplicationSettlement::getIsDeleted, 0)).stream()
.collect(Collectors.groupingBy(PaymentApplicationSettlement::getFormalSettlementId));
// 付款申请(按结算单直挂,payment_type=结算)
Map<Long, List<PaymentApplication>> applicationsBySettlement = paymentApplicationMapper.selectList(
Wrappers.<PaymentApplication>lambdaQuery()
.in(PaymentApplication::getSettlementId, settledIds)
.eq(PaymentApplication::getPaymentType, TransportBusiConstant.PAYMENT_TYPE_SETTLEMENT)
.eq(PaymentApplication::getIsDeleted, 0)).stream()
.collect(Collectors.groupingBy(PaymentApplication::getSettlementId));
// 关系表引用的付款申请状态(全量一次)
Set<Long> relationApplicationIds = relationsBySettlement.values().stream()
.flatMap(List::stream).map(PaymentApplicationSettlement::getPaymentApplicationId)
.filter(Objects::nonNull).collect(Collectors.toSet());
Map<Long, String> relationStatuses = relationApplicationIds.isEmpty() ? Map.of()
: paymentApplicationMapper.selectList(Wrappers.<PaymentApplication>lambdaQuery()
.select(PaymentApplication::getId, PaymentApplication::getApprovalStatus)
.in(PaymentApplication::getId, relationApplicationIds)
.eq(PaymentApplication::getIsDeleted, 0)).stream()
.collect(Collectors.toMap(PaymentApplication::getId, PaymentApplication::getApprovalStatus,
(first, second) -> first));
// 预收来源(仅应付结算单需要)
Map<Long, List<FormalSettlementSource>> sourcesBySettlement = hasPayable
? sourceMapper.selectList(Wrappers.<FormalSettlementSource>lambdaQuery()
.in(FormalSettlementSource::getFormalSettlementId, settledIds)
.eq(FormalSettlementSource::getIsDeleted, 0)).stream()
.collect(Collectors.groupingBy(FormalSettlementSource::getFormalSettlementId))
: Map.of();
// 收款认领分摊聚合(仅应收结算单需要)
Map<Long, BigDecimal> receiptClaimPaidBySettlement = hasReceivable
? receiptClaimSettlementMapper.selectList(Wrappers.<ReceiptClaimSettlement>lambdaQuery()
.in(ReceiptClaimSettlement::getFormalSettlementId, settledIds)
.eq(ReceiptClaimSettlement::getStatus, 1)
.eq(ReceiptClaimSettlement::getIsDeleted, 0)).stream()
.collect(Collectors.groupingBy(ReceiptClaimSettlement::getFormalSettlementId,
Collectors.reducing(BigDecimal.ZERO,
item -> money(item.getAllocatedReceiptAmount()), BigDecimal::add)))
: Map.of();
// 项目预付核销合计(仅应付结算单需要,一次 group-by 避免逐单查询)
Map<Long, BigDecimal> writeOffAmountsBySettlement = hasPayable
? this.projectAdvanceWriteOffService.sumBySettlementIds(
TransportBusiConstant.SETTLEMENT_BILL_FORMAL, settledIds)
: Map.of();
PaymentSummaryBatchData batch = this.loadPaymentSummaryBatchData(settledIds);
for (FormalSettlement settlement : settlements) {
List<PaymentApplicationSettlement> relations = relationsBySettlement
.getOrDefault(settlement.getId(), List.of());
// 直挂付款申请需排除该结算单关系表已引用的申请(与单笔版 notIn 语义一致)
Set<Long> ownRelationApplicationIds = relations.stream()
.map(PaymentApplicationSettlement::getPaymentApplicationId)
.filter(Objects::nonNull).collect(Collectors.toSet());
List<PaymentApplication> directApplications = applicationsBySettlement
.getOrDefault(settlement.getId(), List.of()).stream()
.filter(item -> !ownRelationApplicationIds.contains(item.getId()))
.toList();
PaymentSummary summary = calculatePaymentSummary(settlement, new PaymentSummaryData(
relations, directApplications, relationStatuses,
sourcesBySettlement.getOrDefault(settlement.getId(), List.of()),
receiptClaimPaidBySettlement.getOrDefault(settlement.getId(), BigDecimal.ZERO),
writeOffAmountsBySettlement));
PaymentSummary summary = this.paymentSummaryFor(settlement, batch);
settlement.setAppliedPaymentAmount(summary.appliedAmount());
settlement.setPaidAmount(summary.paidAmount());
settlement.setRemainingPayableAmount(money(settlement.getSettlementAmount())