♻️ syncKingdeeInvoiceResult优化N+1问题,使用批量查询和批量插入/更新

This commit is contained in:
刘泉佳
2026-09-22 14:29:37 +08:00
parent 1268561fd9
commit 06e6c9543f
@@ -25,6 +25,7 @@
*/
package org.springblade.transport.service.impl;
import com.baomidou.mybatisplus.extension.toolkit.Db;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
@@ -790,6 +791,12 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
continue;
}
log.info("金蝶开票结果回写批次查询成功:serialNo=[{}],返回单据数={}", batchSerialNos, result.getData().size());
// 批量查询当前批次已存在的发票号码(避免 N+1)
Set<String> existingInvoiceNos = batchQueryExistingInvoiceNos(batch);
log.debug("金蝶开票结果回写:批次已存在发票号码数={}", existingInvoiceNos.size());
// 收集待插入的发票记录和待更新的开票申请
List<InvoiceApplicationInvoice> toInsertInvoices = new ArrayList<>();
List<InvoiceApplication> toUpdateApplications = new ArrayList<>();
for (BfiInvoiceApplyOpenApiBillVO bill : result.getData()) {
String billNo = bill.getBillNo();
InvoiceApplication application = applicationBySerialNo.get(billNo);
@@ -802,11 +809,8 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
if (Func.isBlank(invoiceNo)) {
continue;
}
Long exists = invoiceApplicationInvoiceMapper.selectCount(
Wrappers.<InvoiceApplicationInvoice>lambdaQuery()
.eq(InvoiceApplicationInvoice::getInvoiceApplicationId, application.getId())
.eq(InvoiceApplicationInvoice::getInvoiceNo, invoiceNo));
if (exists != null && exists > 0) {
// 使用批量查询结果判断是否已存在
if (existingInvoiceNos.contains(invoiceNo)) {
log.debug("金蝶开票结果回写:发票号码[{}]已存在,跳过", invoiceNo);
continue;
}
@@ -817,8 +821,10 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
record.setInvoiceDate(parseInvoiceDateTimestamp(invoice.getInvoiceDate()));
record.setInvoiceAmount(invoice.getIncludeTaxAmount());
record.setRawJson(JsonUtil.toJson(invoice));
invoiceApplicationInvoiceMapper.insert(record);
toInsertInvoices.add(record);
inserted++;
// 标记为已存在,避免同批次重复插入
existingInvoiceNos.add(invoiceNo);
log.info("金蝶开票结果回写:写入发票,申请单号=[{}],单据编号=[{}],发票号码=[{}],含税金额=[{}]",
application.getApplicationNo(), billNo, invoiceNo, invoice.getIncludeTaxAmount());
}
@@ -826,11 +832,21 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
invoiceWritten += inserted;
// 查到发票:开票申请单推进开票完成(终态)
application.setKingdeeStatus(KingdeeBfiConstant.KINGDEE_STATUS_INVOICED);
updateById(application);
toUpdateApplications.add(application);
invoicedCount++;
log.info("金蝶开票结果回写:开票申请[{}]状态推进为invoiced,本次写入发票{}张", application.getApplicationNo(), inserted);
}
}
// 批量插入发票记录
if (!toInsertInvoices.isEmpty()) {
Db.saveBatch(toInsertInvoices);
log.info("金蝶开票结果回写:批量插入发票记录{}条", toInsertInvoices.size());
}
// 批量更新开票申请状态
if (!toUpdateApplications.isEmpty()) {
updateBatchById(toUpdateApplications);
log.info("金蝶开票结果回写:批量更新开票申请状态{}笔", toUpdateApplications.size());
}
} catch (Exception exception) {
failed++;
log.error("金蝶开票结果回写批次异常:serialNo=[{}]", batchSerialNos, exception);
@@ -843,6 +859,23 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
+ "笔),回写发票" + invoiceWritten + "张(开票完成" + invoicedCount + "笔,失败批次" + failed + "个)";
}
/**
* 批量查询当前批次已存在的发票号码
*
* @param serialNos 单据编号列表
* @return 已存在的发票号码集合
*/
private Set<String> batchQueryExistingInvoiceNos(List<String> serialNos) {
List<InvoiceApplicationInvoice> existingList = invoiceApplicationInvoiceMapper.selectList(
Wrappers.<InvoiceApplicationInvoice>lambdaQuery()
.in(InvoiceApplicationInvoice::getSerialNo, serialNos)
.select(InvoiceApplicationInvoice::getInvoiceNo));
return existingList.stream()
.map(InvoiceApplicationInvoice::getInvoiceNo)
.filter(Func::isNotBlank)
.collect(Collectors.toSet());
}
/** 从票易通原文 Map 中按候选键顺序取首个非空文本值 */
private String firstText(Map<?, ?> map, String... keys) {
for (String key : keys) {