diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java index b516c12a..ff4ab0f2 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/FormalSettlementServiceImpl.java @@ -106,6 +106,7 @@ import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; @@ -129,6 +130,9 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl分批与事务说明: + * ① 结算明细按 {@link #AR_RESULT_DETAIL_QUERY_BATCH} 分批 in 查询后内存分组,消除逐单查询 N+1; + * ② 回写聚合后经 {@code updateBatchById} 批量落库——本方法为 this 自调用,MP 自带的 + * {@code @Transactional} 不生效,但其 executeBatch 会独立开启 BATCH SqlSession 并在结束时统一提交、 + * 异常整体回滚,等效单次原子写;③ 整轮任务不包外层事务(远程星瀚查询不占库连接,勾稽按防重键 + * 幂等可重跑),private + lambda 自调用上补加 {@code @Transactional} 会静默失效,禁止添加。 * * @return 执行摘要 */ private String executeArResultSync() { + long startMillis = System.currentTimeMillis(); // 待勾稽应收结算单:审批通过 + 结算类型应收 + 金蝶状态为 已同步/生成失败(已同步一并轮询为补齐单号,生成失败纳入勾稽纠偏: // 金蝶侧已生成说明上次推送实际成功,查到即回填纠正为已同步,避免人工重新推送产生重复单据) List settlements = list(Wrappers.lambdaQuery() @@ -697,23 +708,36 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(item.getKingdeeSyncStatus())).count(); + log.info("金蝶应收单勾稽开始:待勾稽应收结算单{}笔(已同步{}笔,生成失败{}笔)", + settlements.size(), syncedCount, settlements.size() - syncedCount); + // 结算明细分批一次查全(消除逐单查询 N+1),按结算单 id 分组 + List settlementIds = settlements.stream().map(FormalSettlement::getId).toList(); + Map> detailsBySettlement = new HashMap<>(); + kingdeeApBillSyncSupport.batchForeach(settlementIds, AR_RESULT_DETAIL_QUERY_BATCH, batch -> + detailsBySettlement.putAll(detailMapper.selectList(Wrappers.lambdaQuery() + .in(FormalSettlementDetail::getFormalSettlementId, batch) + .eq(FormalSettlementDetail::getIsDeleted, 0) + .orderByAsc(FormalSettlementDetail::getLineNo)).stream() + .collect(Collectors.groupingBy(FormalSettlementDetail::getFormalSettlementId)))); // 组装防重键(与 A302-2 保存传参一致),同一防重键对应一张财务应收单 Map settlementBySrcCode = new LinkedHashMap<>(); for (FormalSettlement settlement : settlements) { - List details = detailMapper.selectList(Wrappers.lambdaQuery() - .eq(FormalSettlementDetail::getFormalSettlementId, settlement.getId()) - .eq(FormalSettlementDetail::getIsDeleted, 0) - .orderByAsc(FormalSettlementDetail::getLineNo)); - String srcCode = kingdeeArBillSyncSupport.arBillSrcCode(settlement, details); + String srcCode = kingdeeArBillSyncSupport.arBillSrcCode(settlement, + detailsBySettlement.getOrDefault(settlement.getId(), List.of())); if (Func.isNotBlank(srcCode)) { settlementBySrcCode.putIfAbsent(srcCode, settlement); } } // A302-4 批量查询:查到 → 回填金蝶单据编号(缺失时)、生成失败纠偏为已同步;查不到 → 状态保持不变 Map existBillNos = kingdeeArBillSyncSupport.queryArBillExistNos(new ArrayList<>(settlementBySrcCode.keySet())); - int updated = 0; + // 勾稽回写聚合后经 updateBatchById 批量更新,避免逐单 update + // (this 自调用下 MP 事务注解不生效,executeBatch 自开 BATCH SqlSession 统一提交、异常整体回滚,等效原子写) + List toUpdateSettlements = new ArrayList<>(); int corrected = 0; for (Map.Entry entry : settlementBySrcCode.entrySet()) { String billNo = existBillNos.get(entry.getKey()); @@ -733,11 +757,20 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl