✨ A303-5开票结果回写支持Base64解码响应并补充关键日志

This commit is contained in:
刘泉佳
2026-09-22 14:07:25 +08:00
parent 430fc6b5a7
commit 1268561fd9
7 changed files with 454 additions and 28 deletions
@@ -44,6 +44,8 @@ import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplyQueryDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveDTO;
import org.springblade.thirdparty.bfi.pojo.dto.BfiInvoiceApplySaveEntryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyOpenApiBillVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyOpenApiInvoiceVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiInvoiceApplyVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiOpenApiResultVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
@@ -738,6 +740,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
if (Func.isEmpty(applications)) {
return "金蝶开票结果回写:无待回写开票申请";
}
log.info("金蝶开票结果回写开始:待回写开票申请{}笔", applications.size());
// 按 serialNo(金蝶开票申请单编号)分批(≤50/批)调 A303-5,销方税号与 A303-1 同源(开票单位→客商档案),按税号分组查询
Map<String, InvoiceApplication> applicationBySerialNo = new LinkedHashMap<>();
applications.forEach(application -> {
@@ -766,6 +769,7 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
}
serialNosBySellerTaxNo.computeIfAbsent(sellerTaxNo, key -> new ArrayList<>()).add(entry.getKey());
}
log.info("金蝶开票结果回写:按销方税号分组完成,共{}组,销方税号无法解析跳过{}笔", serialNosBySellerTaxNo.size(), unresolved);
int invoiceWritten = 0;
int failed = 0;
int invoicedCount = 0;
@@ -773,34 +777,28 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
List<String> groupSerialNos = group.getValue();
for (int from = 0; from < groupSerialNos.size(); from += INVOICE_RESULT_QUERY_BATCH) {
List<String> batch = groupSerialNos.subList(from, Math.min(from + INVOICE_RESULT_QUERY_BATCH, groupSerialNos.size()));
String batchSerialNos = String.join(",", batch);
log.info("金蝶开票结果回写批次查询:销方税号=[{}],单据编号=[{}],数量={}", group.getKey(), batchSerialNos, batch.size());
try {
BfiInvoiceApplyOpenApiQueryDTO param = new BfiInvoiceApplyOpenApiQueryDTO();
param.setSerialNo(String.join(",", batch));
param.setSerialNo(batchSerialNos);
param.setSellerTaxpayerId(group.getKey());
BfiOpenApiResultVO<List<Object>> result = bfiService.queryInvoiceApplyInvoice(param);
BfiOpenApiResultVO<List<BfiInvoiceApplyOpenApiBillVO>> result = bfiService.queryInvoiceApplyInvoice(param);
if (result == null || !result.isSuccess() || result.getData() == null) {
failed++;
log.warn("金蝶开票结果回写批次查询未成功:serialNo=[{}]", String.join(",", batch));
log.warn("金蝶开票结果回写批次查询未成功:serialNo=[{}]", batchSerialNos);
continue;
}
for (Object billItem : result.getData()) {
if (!(billItem instanceof Map)) {
continue;
}
Map<?, ?> billMap = (Map<?, ?>) billItem;
String billNo = firstText(billMap, "billNo", "billno");
log.info("金蝶开票结果回写批次查询成功:serialNo=[{}],返回单据数={}", batchSerialNos, result.getData().size());
for (BfiInvoiceApplyOpenApiBillVO bill : result.getData()) {
String billNo = bill.getBillNo();
InvoiceApplication application = applicationBySerialNo.get(billNo);
if (application == null || !(billMap.get("invoiceList") instanceof List)) {
if (application == null || bill.getInvoiceList() == null) {
continue;
}
int inserted = 0;
for (Object invoiceItem : (List<?>) billMap.get("invoiceList")) {
if (!(invoiceItem instanceof Map)) {
continue;
}
Map<?, ?> invoice = (Map<?, ?>) invoiceItem;
// 宽松提取关键字段(票易通原文,字段名联调校准后收敛),原文整体 JSON 留档
String invoiceNo = firstText(invoice, "invoiceNo", "invoice_no", "invoiceNumber");
for (BfiInvoiceApplyOpenApiInvoiceVO invoice : bill.getInvoiceList()) {
String invoiceNo = invoice.getInvoiceNumber();
if (Func.isBlank(invoiceNo)) {
continue;
}
@@ -809,17 +807,20 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
.eq(InvoiceApplicationInvoice::getInvoiceApplicationId, application.getId())
.eq(InvoiceApplicationInvoice::getInvoiceNo, invoiceNo));
if (exists != null && exists > 0) {
log.debug("金蝶开票结果回写:发票号码[{}]已存在,跳过", invoiceNo);
continue;
}
InvoiceApplicationInvoice record = new InvoiceApplicationInvoice();
record.setInvoiceApplicationId(application.getId());
record.setSerialNo(billNo);
record.setInvoiceNo(invoiceNo);
record.setInvoiceDate(parseInvoiceDate(firstText(invoice, "invoiceDate", "invoice_date", "issueDate", "billingDate")));
record.setInvoiceAmount(parseDecimal(firstText(invoice, "amountWithTax", "totalAmount", "invoiceAmount", "priceTaxTotal")));
record.setInvoiceDate(parseInvoiceDateTimestamp(invoice.getInvoiceDate()));
record.setInvoiceAmount(invoice.getIncludeTaxAmount());
record.setRawJson(JsonUtil.toJson(invoice));
invoiceApplicationInvoiceMapper.insert(record);
inserted++;
log.info("金蝶开票结果回写:写入发票,申请单号=[{}],单据编号=[{}],发票号码=[{}],含税金额=[{}]",
application.getApplicationNo(), billNo, invoiceNo, invoice.getIncludeTaxAmount());
}
if (inserted > 0) {
invoiceWritten += inserted;
@@ -827,14 +828,17 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
application.setKingdeeStatus(KingdeeBfiConstant.KINGDEE_STATUS_INVOICED);
updateById(application);
invoicedCount++;
log.info("金蝶开票结果回写:开票申请[{}]状态推进为invoiced,本次写入发票{}张", application.getApplicationNo(), inserted);
}
}
} catch (Exception exception) {
failed++;
log.error("金蝶开票结果回写批次异常:serialNo=[{}]", String.join(",", batch), exception);
log.error("金蝶开票结果回写批次异常:serialNo=[{}]", batchSerialNos, exception);
}
}
}
log.info("金蝶开票结果回写完成:待查开票申请{}笔(销方税号无法解析跳过{}笔),回写发票{}张(开票完成{}笔,失败批次{}个)",
applications.size(), unresolved, invoiceWritten, invoicedCount, failed);
return "金蝶开票结果回写完成:待查开票申请" + applications.size() + "笔(销方税号无法解析跳过" + unresolved
+ "笔),回写发票" + invoiceWritten + "张(开票完成" + invoicedCount + "笔,失败批次" + failed + "个)";
}
@@ -877,6 +881,19 @@ public class InvoiceApplicationServiceImpl extends BaseServiceImpl<InvoiceApplic
return null;
}
/** 解析时间戳格式的开票日期(毫秒时间戳),无法解析返回 null */
private LocalDateTime parseInvoiceDateTimestamp(Long timestamp) {
if (timestamp == null) {
return null;
}
try {
return LocalDateTime.ofInstant(java.time.Instant.ofEpochMilli(timestamp), java.time.ZoneId.systemDefault());
} catch (Exception exception) {
log.warn("金蝶开票结果开票日期时间戳解析失败:[{}]", timestamp);
return null;
}
}
/** 宽松解析金额文本,无法解析返回 null */
private BigDecimal parseDecimal(String text) {
if (Func.isBlank(text)) {