perf: 金蝶应收单勾稽明细查询与回写改分批执行并补充日志说明
This commit is contained in:
+44
-11
@@ -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<FormalSettlemen
|
||||
/** 金蝶应收单勾稽 single-flight 锁键前缀(多副本定时任务与手动触发共用) */
|
||||
private static final String AR_RESULT_SYNC_LOCK_PREFIX = "transport:formal-settlement:kingdee-ar-result-sync:";
|
||||
|
||||
/** 金蝶应收单勾稽结算明细分批查询单次条数上限(in 条件分批,避免单次过滤条件过长) */
|
||||
private static final int AR_RESULT_DETAIL_QUERY_BATCH = 500;
|
||||
|
||||
private final FormalSettlementSourceMapper sourceMapper;
|
||||
private final FormalSettlementSummaryFeeMapper summaryFeeMapper;
|
||||
private final FormalSettlementPaymentMapper paymentMapper;
|
||||
@@ -161,7 +165,7 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
private final KingdeeSyncLockSupport kingdeeSyncLockSupport;
|
||||
|
||||
/**
|
||||
* 金蝶应收单勾稽 single-flight 锁租约秒数,须大于最坏一轮耗时(批量远程查询 + 逐单纠偏回写),
|
||||
* 金蝶应收单勾稽 single-flight 锁租约秒数,须大于最坏一轮耗时(批量远程查询 + 分批纠偏回写),
|
||||
* 否则租约到期后另一副本会并发起跑;进程异常退出时锁在租约到期后自动释放
|
||||
*/
|
||||
@Value("${thirdParty.bfi.arResult.lockLeaseSeconds:1800}")
|
||||
@@ -683,11 +687,18 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
|
||||
/**
|
||||
* 金蝶应收单勾稽主体:A302-4 批量查询财务应收单回填单据编号、生成失败纠偏为已同步,
|
||||
* 由 {@link #syncKingdeeArResult()} 在分布式锁保护下调用
|
||||
* 由 {@link #syncKingdeeArResult()} 在分布式锁保护下调用。
|
||||
* <p>分批与事务说明:
|
||||
* ① 结算明细按 {@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<FormalSettlement> settlements = list(Wrappers.<FormalSettlement>lambdaQuery()
|
||||
@@ -697,23 +708,36 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
KingdeeBfiConstant.KINGDEE_STATUS_FAILED)
|
||||
.orderByAsc(FormalSettlement::getId));
|
||||
if (Func.isEmpty(settlements)) {
|
||||
log.info("金蝶应收单勾稽:无待勾稽应收结算单");
|
||||
return "金蝶应收单勾稽:无待勾稽应收结算单";
|
||||
}
|
||||
long syncedCount = settlements.stream()
|
||||
.filter(item -> KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(item.getKingdeeSyncStatus())).count();
|
||||
log.info("金蝶应收单勾稽开始:待勾稽应收结算单{}笔(已同步{}笔,生成失败{}笔)",
|
||||
settlements.size(), syncedCount, settlements.size() - syncedCount);
|
||||
// 结算明细分批一次查全(消除逐单查询 N+1),按结算单 id 分组
|
||||
List<Long> settlementIds = settlements.stream().map(FormalSettlement::getId).toList();
|
||||
Map<Long, List<FormalSettlementDetail>> detailsBySettlement = new HashMap<>();
|
||||
kingdeeApBillSyncSupport.batchForeach(settlementIds, AR_RESULT_DETAIL_QUERY_BATCH, batch ->
|
||||
detailsBySettlement.putAll(detailMapper.selectList(Wrappers.<FormalSettlementDetail>lambdaQuery()
|
||||
.in(FormalSettlementDetail::getFormalSettlementId, batch)
|
||||
.eq(FormalSettlementDetail::getIsDeleted, 0)
|
||||
.orderByAsc(FormalSettlementDetail::getLineNo)).stream()
|
||||
.collect(Collectors.groupingBy(FormalSettlementDetail::getFormalSettlementId))));
|
||||
// 组装防重键(与 A302-2 保存传参一致),同一防重键对应一张财务应收单
|
||||
Map<String, FormalSettlement> settlementBySrcCode = new LinkedHashMap<>();
|
||||
for (FormalSettlement settlement : settlements) {
|
||||
List<FormalSettlementDetail> details = detailMapper.selectList(Wrappers.<FormalSettlementDetail>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<String, String> existBillNos = kingdeeArBillSyncSupport.queryArBillExistNos(new ArrayList<>(settlementBySrcCode.keySet()));
|
||||
int updated = 0;
|
||||
// 勾稽回写聚合后经 updateBatchById 批量更新,避免逐单 update
|
||||
// (this 自调用下 MP 事务注解不生效,executeBatch 自开 BATCH SqlSession 统一提交、异常整体回滚,等效原子写)
|
||||
List<FormalSettlement> toUpdateSettlements = new ArrayList<>();
|
||||
int corrected = 0;
|
||||
for (Map.Entry<String, FormalSettlement> entry : settlementBySrcCode.entrySet()) {
|
||||
String billNo = existBillNos.get(entry.getKey());
|
||||
@@ -733,11 +757,20 @@ public class FormalSettlementServiceImpl extends BaseServiceImpl<FormalSettlemen
|
||||
}
|
||||
settlement.setKingdeeSyncStatus(KingdeeBfiConstant.KINGDEE_STATUS_SYNCED);
|
||||
settlement.setSyncedTime(LocalDateTime.now());
|
||||
updateById(settlement);
|
||||
updated++;
|
||||
toUpdateSettlements.add(settlement);
|
||||
// 生成失败纠偏与缺失单号补齐逐笔留痕,便于对账核查
|
||||
log.info("正式结算单[{}]金蝶应收单勾稽回写:{},billNo={}",
|
||||
settlement.getFormalSettlementNo(), failed ? "生成失败纠偏为已同步" : "补齐单据编号",
|
||||
settlement.getKingdeeBillNo());
|
||||
}
|
||||
return "金蝶应收单勾稽完成:待查应收结算单" + settlementBySrcCode.size() + "笔,回写单据信息" + updated
|
||||
if (Func.isNotEmpty(toUpdateSettlements)) {
|
||||
updateBatchById(toUpdateSettlements);
|
||||
log.info("金蝶应收单勾稽批量回写完成:本批更新{}笔", toUpdateSettlements.size());
|
||||
}
|
||||
String summary = "金蝶应收单勾稽完成:待查应收结算单" + settlementBySrcCode.size() + "笔,回写单据信息" + toUpdateSettlements.size()
|
||||
+ "笔(生成失败纠偏" + corrected + "笔)";
|
||||
log.info("{},耗时{}ms", summary, System.currentTimeMillis() - startMillis);
|
||||
return summary;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user