perf: 收款流水同步改为批量查询与批量保存(N+1→批量)
This commit is contained in:
+70
-25
@@ -346,53 +346,98 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
|
|||||||
// 同步口径:A301-2 按参与认领用户(当前登录用户真实姓名)拉取全量认领公告,
|
// 同步口径:A301-2 按参与认领用户(当前登录用户真实姓名)拉取全量认领公告,
|
||||||
// 以金蝶明细流水号为幂等键 upsert 收款流水表;本地已认领金额/状态(claimedAmount/claimStatus)
|
// 以金蝶明细流水号为幂等键 upsert 收款流水表;本地已认领金额/状态(claimedAmount/claimStatus)
|
||||||
// 由认领、作废驱动,不被公告顺带覆盖,公告侧状态单独存 kingdee_claim_status/unclaimed_amount。
|
// 由认领、作废驱动,不被公告顺带覆盖,公告侧状态单独存 kingdee_claim_status/unclaimed_amount。
|
||||||
Set<String> serialNumbers = new HashSet<>();
|
// 第一遍:收集有效的 detailSerialNo,跳过无效数据
|
||||||
int syncedCount = 0;
|
Map<String, BfiClaimAnnounceVO> validAnnounces = new LinkedHashMap<>();
|
||||||
int createdCount = 0;
|
|
||||||
int updatedCount = 0;
|
|
||||||
for (BfiClaimAnnounceVO announce : announces) {
|
for (BfiClaimAnnounceVO announce : announces) {
|
||||||
// 明细流水号为幂等键,缺省依次回退金蝶交易明细编号、银行流水号
|
|
||||||
String detailSerialNo = firstNonBlank(announce.getDetailid(),
|
String detailSerialNo = firstNonBlank(announce.getDetailid(),
|
||||||
announce.getTradedetailno(), announce.getBankdetailno());
|
announce.getTradedetailno(), announce.getBankdetailno());
|
||||||
if (Func.isEmpty(detailSerialNo)) {
|
if (Func.isEmpty(detailSerialNo)) {
|
||||||
log.warn("收款流水同步跳过:认领公告[{}]缺少明细流水号", announce.getBillno());
|
log.warn("收款流水同步跳过:认领公告[{}]缺少明细流水号", announce.getBillno());
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!serialNumbers.add(detailSerialNo.trim())) {
|
detailSerialNo = detailSerialNo.trim();
|
||||||
throw new ServiceException("明细流水号" + detailSerialNo.trim() + "重复");
|
if (validAnnounces.containsKey(detailSerialNo)) {
|
||||||
|
throw new ServiceException("明细流水号" + detailSerialNo + "重复");
|
||||||
}
|
}
|
||||||
// 行级锁防并发:同一流水并发手动同步时避免重复落库或覆盖已认领状态
|
BigDecimal receiptAmount = money(announce.getReamount());
|
||||||
KingdeeReceiptFlow entity = baseMapper.selectOne(Wrappers.<KingdeeReceiptFlow>lambdaQuery()
|
if (receiptAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
.eq(KingdeeReceiptFlow::getDetailSerialNo, detailSerialNo.trim())
|
log.warn("收款流水同步跳过:认领公告[{}]收款金额为空或小于0", announce.getBillno());
|
||||||
.last("FOR UPDATE"));
|
continue;
|
||||||
boolean created = entity == null;
|
}
|
||||||
|
validAnnounces.put(detailSerialNo, announce);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (validAnnounces.isEmpty()) {
|
||||||
|
record(null, null, "sync", "手动同步流水", null, null, BigDecimal.ZERO,
|
||||||
|
"金蝶认领公告无有效数据");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量查询已存在的记录(FOR UPDATE 防并发)
|
||||||
|
List<KingdeeReceiptFlow> existingEntities = baseMapper.selectList(
|
||||||
|
Wrappers.<KingdeeReceiptFlow>lambdaQuery()
|
||||||
|
.in(KingdeeReceiptFlow::getDetailSerialNo, validAnnounces.keySet())
|
||||||
|
.last("FOR UPDATE")
|
||||||
|
);
|
||||||
|
Map<String, KingdeeReceiptFlow> existingMap = existingEntities.stream()
|
||||||
|
.collect(Collectors.toMap(KingdeeReceiptFlow::getDetailSerialNo, e -> e));
|
||||||
|
|
||||||
|
// 第二遍:构建新增和更新列表
|
||||||
|
List<KingdeeReceiptFlow> toInsert = new ArrayList<>();
|
||||||
|
List<KingdeeReceiptFlow> toUpdate = new ArrayList<>();
|
||||||
|
int createdCount = 0;
|
||||||
|
int updatedCount = 0;
|
||||||
|
|
||||||
|
for (Map.Entry<String, BfiClaimAnnounceVO> entry : validAnnounces.entrySet()) {
|
||||||
|
String detailSerialNo = entry.getKey();
|
||||||
|
BfiClaimAnnounceVO announce = entry.getValue();
|
||||||
|
BigDecimal receiptAmount = money(announce.getReamount());
|
||||||
|
|
||||||
|
KingdeeReceiptFlow existing = existingMap.get(detailSerialNo);
|
||||||
|
boolean created = (existing == null);
|
||||||
|
|
||||||
|
KingdeeReceiptFlow entity;
|
||||||
if (created) {
|
if (created) {
|
||||||
entity = new KingdeeReceiptFlow();
|
entity = new KingdeeReceiptFlow();
|
||||||
entity.setClaimedAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
entity.setClaimedAmount(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP));
|
||||||
entity.setClaimStatus(UNCLAIMED);
|
entity.setClaimStatus(UNCLAIMED);
|
||||||
|
} else {
|
||||||
|
entity = existing;
|
||||||
}
|
}
|
||||||
BigDecimal receiptAmount = money(announce.getReamount());
|
|
||||||
if (receiptAmount.compareTo(BigDecimal.ZERO) <= 0) {
|
copyAnnounceRow(entity, announce, detailSerialNo, receiptAmount);
|
||||||
// 公告金额异常时跳过单条并告警,不阻断整批同步、不破坏已有流水
|
|
||||||
log.warn("收款流水同步跳过:认领公告[{}]收款金额为空或小于0", announce.getBillno());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
copyAnnounceRow(entity, announce, detailSerialNo.trim(), receiptAmount);
|
|
||||||
entity.setClaimStatus(amountClaimStatus(money(entity.getClaimedAmount()), receiptAmount));
|
entity.setClaimStatus(amountClaimStatus(money(entity.getClaimedAmount()), receiptAmount));
|
||||||
|
|
||||||
if (created) {
|
if (created) {
|
||||||
baseMapper.insert(entity);
|
toInsert.add(entity);
|
||||||
createdCount++;
|
createdCount++;
|
||||||
} else {
|
} else {
|
||||||
baseMapper.updateById(entity);
|
toUpdate.add(entity);
|
||||||
updatedCount++;
|
updatedCount++;
|
||||||
}
|
}
|
||||||
record(entity.getId(), null, "sync", created ? "新增金蝶收款流水" : "更新金蝶收款流水",
|
|
||||||
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, detailSerialNo.trim());
|
|
||||||
syncedCount++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 批量插入和更新
|
||||||
|
if (!toInsert.isEmpty()) {
|
||||||
|
saveBatch(toInsert);
|
||||||
|
}
|
||||||
|
if (!toUpdate.isEmpty()) {
|
||||||
|
updateBatchById(toUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 记录操作日志
|
||||||
|
for (KingdeeReceiptFlow entity : toInsert) {
|
||||||
|
record(entity.getId(), null, "sync", "新增金蝶收款流水",
|
||||||
|
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo());
|
||||||
|
}
|
||||||
|
for (KingdeeReceiptFlow entity : toUpdate) {
|
||||||
|
record(entity.getId(), null, "sync", "更新金蝶收款流水",
|
||||||
|
entity.getClaimStatus(), entity.getClaimStatus(), BigDecimal.ZERO, entity.getDetailSerialNo());
|
||||||
|
}
|
||||||
|
|
||||||
log.info("金蝶收款流水同步完成 claimusers:{},拉取 {} 条,新增 {} 条,更新 {} 条",
|
log.info("金蝶收款流水同步完成 claimusers:{},拉取 {} 条,新增 {} 条,更新 {} 条",
|
||||||
claimusers, announces.size(), createdCount, updatedCount);
|
claimusers, announces.size(), createdCount, updatedCount);
|
||||||
return syncedCount;
|
return createdCount + updatedCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> candidateRow(FormalSettlement settlement) {
|
private Map<String, Object> candidateRow(FormalSettlement settlement) {
|
||||||
|
|||||||
Reference in New Issue
Block a user