refactor: 收款流水同步改为主单粒度一单一流水不再展开流水分录

This commit is contained in:
刘泉佳
2026-10-29 14:44:41 +08:00
parent b72f05cc22
commit 87288ae10d
3 changed files with 20 additions and 51 deletions
@@ -113,7 +113,7 @@ public class KingdeeReceiptFlow extends TenantEntity {
*/
private LocalDateTime transactionTime;
/**
* 明细流水号(A301-1 流水分录 bei_detailid,兜底 bei_bankdetailno,幂等键)
* 明细流水号(收款单号 billno,幂等键;C104-2 按此定位合规侧收款流水)
*/
private String detailSerialNo;
/**
@@ -40,7 +40,6 @@ import org.springblade.system.cache.UserCache;
import org.springblade.system.pojo.entity.Dept;
import org.springblade.thirdparty.bfi.pojo.dto.BfiRecBillQueryDTO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiRecBillFlowVO;
import org.springblade.thirdparty.bfi.pojo.vo.BfiRecBillVO;
import org.springblade.thirdparty.bfi.service.IBfiService;
import org.springblade.thirdparty.bfi.util.BfiUtil;
@@ -483,7 +482,24 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
}
pullCount += rows.size();
for (BfiRecBillVO recBill : rows) {
skippedCount += expandRecBillRows(recBill, pending, batchSerials);
// 主单粒度:一张收款单一条流水,不展开流水分录;明细流水号取收款单号(幂等键,C104-2 按此定位合规流水)
String detailSerialNo = trimToEmpty(recBill.getBillno());
if (Func.isEmpty(detailSerialNo)) {
log.warn("收款流水同步跳过:收款单缺少单号");
skippedCount++;
continue;
}
if (!batchSerials.add(detailSerialNo)) {
throw new ServiceException("收款单号" + detailSerialNo + "重复");
}
// 收款金额取主单入账金额 inneraccountamount(金蝶口径,非流水分录 bei_creditamount)
BigDecimal receiptAmount = money(recBill.getInneraccountAmount());
if (receiptAmount.compareTo(BigDecimal.ZERO) <= 0) {
log.warn("收款流水同步跳过:收款单[{}]收款金额(inneraccountamount)为空或小于0", recBill.getBillno());
skippedCount++;
continue;
}
pending.add(new BfiRecBillRow(recBill, detailSerialNo, receiptAmount));
if (pending.size() >= SYNC_BATCH_SIZE) {
batchNo++;
totalCount += syncBatch(batchNo, pending);
@@ -515,44 +531,6 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
return totalCount;
}
/**
* 单张收款单展开为流水行并做批内校验(金额非法跳过,批内流水号重复拦截)。
* 明细流水号优先级:bei_detailid > bei_bankdetailno > 收款单号 billno 兜底
* (无流水分录的单如手工收款单也入库,但认领后 C104-2 将定位不到合规流水,C107-1 落失败标记)。
*
* @return 跳过条数
*/
private int expandRecBillRows(BfiRecBillVO recBill, List<BfiRecBillRow> pending, Set<String> batchSerials) {
int skipped = 0;
List<BfiRecBillFlowVO> flowEntries = recBill.getInfoentry();
if (Func.isEmpty(flowEntries)) {
flowEntries = List.of(new BfiRecBillFlowVO());
}
for (BfiRecBillFlowVO flowEntry : flowEntries) {
// 明细流水号优先级:bei_detailid > bei_bankdetailno > 收款单号兜底
String detailSerialNo = firstNonBlank(flowEntry.getBeiDetailid(), flowEntry.getBeiBankdetailno(),
recBill.getBillno());
if (Func.isEmpty(detailSerialNo)) {
log.warn("收款流水同步跳过:收款单[{}]缺少明细流水号", recBill.getBillno());
skipped++;
continue;
}
detailSerialNo = detailSerialNo.trim();
if (!batchSerials.add(detailSerialNo)) {
throw new ServiceException("明细流水号" + detailSerialNo + "重复");
}
// 收款金额取主单入账金额 inneraccountamount(金蝶口径,非流水分录 bei_creditamount)
BigDecimal receiptAmount = money(recBill.getInneraccountAmount());
if (receiptAmount.compareTo(BigDecimal.ZERO) <= 0) {
log.warn("收款流水同步跳过:收款单[{}]收款金额(inneraccountamount)为空或小于0", recBill.getBillno());
skipped++;
continue;
}
pending.add(new BfiRecBillRow(recBill, detailSerialNo, receiptAmount));
}
return skipped;
}
/**
* 单批入库(独立事务):批内 FOR UPDATE 防并发,按明细流水号 upsert,操作日志批量落库
*
@@ -877,15 +855,6 @@ public class ReceiptFlowServiceImpl extends BaseServiceImpl<KingdeeReceiptFlowMa
return KingdeeBfiConstant.KINGDEE_STATUS_FAILED;
}
private String firstNonBlank(String... values) {
for (String value : values) {
if (Func.isNotEmpty(value)) {
return value.trim();
}
}
return null;
}
private LocalDateTime parseDateTime(String value) {
if (Func.isEmpty(value)) {
return null;
@@ -73,7 +73,7 @@ A301-1 收款单查询(业务日期窗口取 `thirdParty.bfi.recBillSync.days`
| --- | --- | --- |
| receipt_notice_no / kingdee_bill_no | `billno` | 金蝶收款单号(两处同值) |
| kingdee_bill_status | `billstatus` 映射 | A/B已提交→pushed、C已审核→approved、D已收款→received、其余(含G退单)→failed |
| detail_serial_no | `infoentry[].bei_detailid`,兜底 `bei_bankdetailno` | **幂等键**;每条流水分录展开为一行流水 |
| detail_serial_no | 主单 `billno` | 明细流水号=收款单号,**幂等键**;主单粒度,一张收款单一条流水,不展开流水分录 |
| receipt_amount | 主单 `inneraccountamount` | 收款金额(入账金额口径,非流水分录 bei_creditamount) |
| unclaimed_amount | `unmatchamountrec` | 未匹配收款金额,**认领上限口径** |
| payer_name / counterparty_name | `payername` | 付款人名称 |