✨ 新增金蝶付款结果定时回写任务(A103-2/A103-4)并抽取BFI分页通用翻页工具
This commit is contained in:
+4
-2
@@ -110,9 +110,11 @@ public class PaymentApplication extends TenantEntity {
|
||||
private String currentNode;
|
||||
/** 当前处理人 */
|
||||
private String currentProcessor;
|
||||
/** 金蝶付款申请单id(A103-3 预付款保存 / A103-1 应付下推返回) */
|
||||
/** 金蝶单据号(A103-2 查询回填的星瀚单据编号 billno,如 FKSQ-xxxx) */
|
||||
private String kingdeeBillNo;
|
||||
/** 金蝶同步状态:unsynced 未生成 / synced 已生成 / failed 生成失败 */
|
||||
/** 金蝶单据id(A103-1/A103-3 推送返回的星瀚内部主键,附件上传等使用) */
|
||||
private String kingdeeBillId;
|
||||
/** 金蝶同步状态:unsynced 未生成 / synced 已生成 / paying 付款中 / paid 已付款 / closed 已关闭 / failed 生成失败 */
|
||||
private String kingdeeStatus;
|
||||
/** 附件信息JSON */
|
||||
private String attachmentsJson;
|
||||
|
||||
+2
@@ -27,12 +27,14 @@ package org.springblade.transport;
|
||||
|
||||
import org.springblade.core.cloud.client.BladeCloudApplication;
|
||||
import org.springblade.core.launch.BladeApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
/**
|
||||
* 运输服务启动器
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@EnableScheduling
|
||||
@BladeCloudApplication
|
||||
public class TransportApplication {
|
||||
|
||||
|
||||
+32
-2
@@ -66,13 +66,13 @@ public final class KingdeeBfiConstant {
|
||||
* A102-2 明细必填:物料编码。TMS 无物料档案,当前为占位空值,
|
||||
* 推送前会校验拦截。待确认:需金蝶侧提供运输服务类物料编码后补充。
|
||||
*/
|
||||
public static final String BFI_AP_BILL_MATERIAL = "";
|
||||
public static final String BFI_AP_BILL_MATERIAL = "GWZB01.003";
|
||||
|
||||
/**
|
||||
* A102-2 明细必填:计量单位编码。TMS 无对应档案,当前为占位空值,
|
||||
* 推送前会校验拦截。待确认:需金蝶侧确认编码(如"吨")后补充。
|
||||
*/
|
||||
public static final String BFI_AP_BILL_MEASURE_UNIT = "";
|
||||
public static final String BFI_AP_BILL_MEASURE_UNIT = "PCS";
|
||||
|
||||
/** A102-2 必填:外部单据类型(应付单来源标识)。待确认:取值需金蝶侧确认,确认后如有调整直接改此常量。 */
|
||||
public static final String BFI_AP_BILL_SRC_TYPE = "TMS财务应付单";
|
||||
@@ -105,6 +105,36 @@ public final class KingdeeBfiConstant {
|
||||
"其他付款申请", "ap_payapply_oth_BT_S"
|
||||
);
|
||||
|
||||
/** 金蝶同步状态:未生成(新建单据默认)。 */
|
||||
public static final String KINGDEE_STATUS_UNSYNCED = "unsynced";
|
||||
|
||||
/** 金蝶同步状态:已生成(推送成功,等待付款)。 */
|
||||
public static final String KINGDEE_STATUS_SYNCED = "synced";
|
||||
|
||||
/** 金蝶同步状态:付款中(A103-2 paystatus = Inpayment 部分付款)。 */
|
||||
public static final String KINGDEE_STATUS_PAYING = "paying";
|
||||
|
||||
/** 金蝶同步状态:已付款(A103-2 paystatus = Alreadypay,终态)。 */
|
||||
public static final String KINGDEE_STATUS_PAID = "paid";
|
||||
|
||||
/** 金蝶同步状态:已关闭(A103-2 billstatus = D,终态)。 */
|
||||
public static final String KINGDEE_STATUS_CLOSED = "closed";
|
||||
|
||||
/** 金蝶同步状态:生成失败(推送异常/金蝶返回失败,可重推)。 */
|
||||
public static final String KINGDEE_STATUS_FAILED = "failed";
|
||||
|
||||
/** A103-2 付款状态:已付款。 */
|
||||
public static final String BFI_PAY_STATUS_ALREADY_PAY = "Alreadypay";
|
||||
|
||||
/** A103-2 付款状态:部分付款。 */
|
||||
public static final String BFI_PAY_STATUS_IN_PAYMENT = "Inpayment";
|
||||
|
||||
/** A103-2 付款状态:未付款。 */
|
||||
public static final String BFI_PAY_STATUS_UNPAID = "Unpaid";
|
||||
|
||||
/** A103-2 单据状态:已关闭。 */
|
||||
public static final String BFI_BILL_STATUS_CLOSED = "D";
|
||||
|
||||
/**
|
||||
* 按中文名称从映射字典转换单据类型编码。
|
||||
*
|
||||
|
||||
+4
@@ -100,4 +100,8 @@ public class PaymentApplicationController extends BladeController {
|
||||
public R<List<String>> syncKingdeeBatch(@RequestBody List<Long> ids) {
|
||||
return R.data(paymentApplicationService.syncKingdeeBatch(ids));
|
||||
}
|
||||
@PostMapping("/sync-kingdee-result")
|
||||
@ApiOperationSupport(order = 12)
|
||||
@Operation(summary = "手动回写金蝶付款结果(A103-2/A103-4)")
|
||||
public R<String> syncKingdeeResult() { return R.data(paymentApplicationService.syncKingdeePaymentResult()); }
|
||||
}
|
||||
|
||||
+26
@@ -21,6 +21,8 @@ package org.springblade.transport.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.springblade.core.mp.base.BaseService;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPayApplyVO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPayBillVO;
|
||||
import org.springblade.transport.pojo.dto.PaymentApplicationSaveRequest;
|
||||
import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
@@ -42,4 +44,28 @@ public interface IPaymentApplicationService extends BaseService<PaymentApplicati
|
||||
void voidBill(PaymentApplicationStatusRequest request);
|
||||
String syncKingdee(Long id);
|
||||
List<String> syncKingdeeBatch(List<Long> ids);
|
||||
|
||||
/**
|
||||
* 定时回写金蝶付款结果(A103-2 付款申请单查询 / A103-4 付款处理查询)
|
||||
*
|
||||
* @return 执行摘要
|
||||
*/
|
||||
String syncKingdeePaymentResult();
|
||||
|
||||
/**
|
||||
* 回写单笔付款申请的金蝶付款申请单查询结果(A103-2),单据级事务
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
* @param payApply A103-2 查询返回的付款申请单
|
||||
* @return 是否付款完成(paystatus = Alreadypay)
|
||||
*/
|
||||
boolean applyPayApplyResult(PaymentApplication entity, BfiPayApplyVO payApply);
|
||||
|
||||
/**
|
||||
* 回填单笔付款申请的金蝶付款处理记录(A103-4),单据级事务
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
* @param payBill A103-4 查询返回的付款处理单
|
||||
*/
|
||||
void applyPayBillRecord(PaymentApplication entity, BfiPayBillVO payBill);
|
||||
}
|
||||
|
||||
+350
-15
@@ -30,6 +30,7 @@ import org.springblade.core.secure.utils.AuthUtil;
|
||||
import org.springblade.core.tool.jackson.JsonUtil;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.core.tool.utils.SpringUtil;
|
||||
import org.springblade.transport.constant.KingdeeBfiConstant;
|
||||
import org.springblade.system.cache.DictBizCache;
|
||||
import org.springblade.system.cache.SysCache;
|
||||
@@ -39,11 +40,17 @@ import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiApBillSaveEntryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyPushEntryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplyQueryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayApplySaveEntryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiPayBillQueryDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultItemVO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiBillSaveResultVO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPayApplyEntryVO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPayApplyVO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPayBillVO;
|
||||
import org.springblade.thirdparty.bfi.service.IBfiService;
|
||||
import org.springblade.thirdparty.bfi.util.BfiUtil;
|
||||
import org.springblade.transport.mapper.FormalSettlementMapper;
|
||||
import org.springblade.transport.mapper.FormalSettlementPaymentMapper;
|
||||
import org.springblade.transport.mapper.BillLedgerMapper;
|
||||
@@ -93,7 +100,10 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -108,6 +118,12 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
private static final String APPROVED = "approved";
|
||||
private static final String RETURNED = "returned";
|
||||
private static final String VOIDED = "voided";
|
||||
/** A103-2 按 awrp_src_code 批量查询单次条数上限 */
|
||||
private static final int PAY_RESULT_QUERY_BATCH = 50;
|
||||
/** A103-2 / A103-4 分页查询页大小 */
|
||||
private static final int PAY_RESULT_PAGE_SIZE = 200;
|
||||
/** 历史模拟付款记录单号前缀 */
|
||||
private static final String MOCK_PAYMENT_RECORD_PREFIX = "MOCK-PAY-";
|
||||
private final PaymentApplicationInvoiceMapper invoiceMapper;
|
||||
private final PaymentApplicationRecordMapper recordMapper;
|
||||
private final PaymentApplicationSettlementMapper settlementRelationMapper;
|
||||
@@ -212,7 +228,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
entity.setPaymentNo(nextNo());
|
||||
entity.setApprovalStatus(DRAFT);
|
||||
entity.setCurrentNode("草稿");
|
||||
entity.setKingdeeStatus("unsynced");
|
||||
entity.setKingdeeStatus(KingdeeBfiConstant.KINGDEE_STATUS_UNSYNCED);
|
||||
entity.setPaidAmount(BigDecimal.ZERO);
|
||||
entity.setInvoiceStatus("unmatched");
|
||||
}
|
||||
@@ -330,7 +346,13 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
public String syncKingdee(Long id) {
|
||||
PaymentApplication entity = lockedPayment(id);
|
||||
if (!APPROVED.equals(entity.getApprovalStatus())) throw new ServiceException("仅审批通过的付款申请允许生成金蝶单据");
|
||||
if ("synced".equals(entity.getKingdeeStatus())) return entity.getKingdeeBillNo();
|
||||
if (KingdeeBfiConstant.KINGDEE_STATUS_SYNCED.equals(entity.getKingdeeStatus())
|
||||
|| KingdeeBfiConstant.KINGDEE_STATUS_PAYING.equals(entity.getKingdeeStatus())
|
||||
|| KingdeeBfiConstant.KINGDEE_STATUS_PAID.equals(entity.getKingdeeStatus())
|
||||
|| KingdeeBfiConstant.KINGDEE_STATUS_CLOSED.equals(entity.getKingdeeStatus())) {
|
||||
// 存量数据金蝶单据id可能仍存于 kingdeeBillNo,兜底返回
|
||||
return Func.isNotEmpty(entity.getKingdeeBillId()) ? entity.getKingdeeBillId() : entity.getKingdeeBillNo();
|
||||
}
|
||||
String kingdeeBillId;
|
||||
try {
|
||||
if ("settlement_payment".equals(entity.getPaymentType())) {
|
||||
@@ -352,13 +374,17 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
markSyncFailed(entity);
|
||||
throw new ServiceException("推送金蝶付款申请失败:" + exception.getMessage());
|
||||
}
|
||||
entity.setKingdeeBillNo(kingdeeBillId);
|
||||
entity.setKingdeeStatus("synced");
|
||||
entity.setPaidAmount(money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP));
|
||||
mockPaymentRecords(entity);
|
||||
// 星瀚单据内部主键落 kingdeeBillId(附件上传等使用);
|
||||
// 金蝶单据编号(billno)与付款状态由定时回写任务经 A103-2/A103-4 查询回填
|
||||
entity.setKingdeeBillId(kingdeeBillId);
|
||||
entity.setKingdeeStatus(KingdeeBfiConstant.KINGDEE_STATUS_SYNCED);
|
||||
// 付款完成模拟逻辑已停用(mockPaymentRecords / 履约付款数据直接置 paid),
|
||||
// 真实付款结果改由定时回写任务驱动;联调环境需要模拟时恢复以下注释即可
|
||||
// entity.setPaidAmount(money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP));
|
||||
// mockPaymentRecords(entity);
|
||||
// syncPreSettlementAdvance(entity);
|
||||
// syncFormalSettlementPayments(entity);
|
||||
updateById(entity);
|
||||
syncPreSettlementAdvance(entity);
|
||||
syncFormalSettlementPayments(entity);
|
||||
return kingdeeBillId;
|
||||
}
|
||||
|
||||
@@ -379,6 +405,282 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String syncKingdeePaymentResult() {
|
||||
// ① 待回写付款申请:审批通过且金蝶状态为 已生成/付款中(终态已付款/已关闭不再轮询)
|
||||
List<PaymentApplication> applications = list(Wrappers.<PaymentApplication>lambdaQuery()
|
||||
.eq(PaymentApplication::getApprovalStatus, APPROVED)
|
||||
.in(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED, KingdeeBfiConstant.KINGDEE_STATUS_PAYING)
|
||||
.orderByAsc(PaymentApplication::getId));
|
||||
IPaymentApplicationService self = SpringUtil.getBean(IPaymentApplicationService.class);
|
||||
int updated = 0;
|
||||
int failed = 0;
|
||||
int paidCount = 0;
|
||||
Map<String, BfiPayApplyVO> payApplyMap = new HashMap<>();
|
||||
if (!applications.isEmpty()) {
|
||||
// ② A103-2 付款申请单查询:按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改的一条
|
||||
List<String> paymentNos = applications.stream()
|
||||
.map(PaymentApplication::getPaymentNo).filter(Func::isNotEmpty).distinct().toList();
|
||||
for (int from = 0; from < paymentNos.size(); from += PAY_RESULT_QUERY_BATCH) {
|
||||
List<String> batch = paymentNos.subList(from, Math.min(from + PAY_RESULT_QUERY_BATCH, paymentNos.size()));
|
||||
for (BfiPayApplyVO payApply : queryPayApplyPages(batch)) {
|
||||
if (Func.isEmpty(payApply.getAwrpSrcCode())) {
|
||||
continue;
|
||||
}
|
||||
BfiPayApplyVO existing = payApplyMap.get(payApply.getAwrpSrcCode());
|
||||
if (existing == null || strCompare(payApply.getModifytime(), existing.getModifytime()) > 0) {
|
||||
payApplyMap.put(payApply.getAwrpSrcCode(), payApply);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PaymentApplication application : applications) {
|
||||
BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo());
|
||||
if (payApply == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
if (self.applyPayApplyResult(application, payApply)) paidCount++;
|
||||
updated++;
|
||||
} catch (Exception exception) {
|
||||
failed++;
|
||||
log.error("付款申请[{}]回写金蝶付款申请单结果异常", application.getPaymentNo(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
// ③ A103-4 付款处理查询:付款中/已付款单据 + 已付款但尚无付款记录的单据(补偿),回填付款记录
|
||||
Map<Long, PaymentApplication> recordCandidates = new LinkedHashMap<>();
|
||||
for (PaymentApplication application : applications) {
|
||||
BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo());
|
||||
if (payApply != null && !KingdeeBfiConstant.BFI_PAY_STATUS_UNPAID.equals(payApply.getPaystatus())) {
|
||||
recordCandidates.put(application.getId(), application);
|
||||
}
|
||||
}
|
||||
list(Wrappers.<PaymentApplication>lambdaQuery()
|
||||
.eq(PaymentApplication::getApprovalStatus, APPROVED)
|
||||
.eq(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_PAID))
|
||||
.stream().filter(application -> realRecordCount(application.getId()) == 0)
|
||||
.forEach(application -> recordCandidates.putIfAbsent(application.getId(), application));
|
||||
int recordWritten = 0;
|
||||
int recordFailed = 0;
|
||||
if (!recordCandidates.isEmpty()) {
|
||||
Map<String, List<PaymentApplication>> orgGroups = new LinkedHashMap<>();
|
||||
for (PaymentApplication application : recordCandidates.values()) {
|
||||
BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo());
|
||||
String orgNumber = payApply == null ? null : payApply.getPayorgNumber();
|
||||
if (Func.isBlank(orgNumber)) {
|
||||
try {
|
||||
// A103-4 付款人(公司)编码必填:优先取 A103-2 返回付款组织编码,兜底按合同所属组织解析
|
||||
orgNumber = bfiOrgNumber(requireContract(application));
|
||||
} catch (Exception exception) {
|
||||
log.warn("付款申请[{}]付款处理查询跳过,付款组织编码获取失败:{}", application.getPaymentNo(), exception.getMessage());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
orgGroups.computeIfAbsent(orgNumber, key -> new ArrayList<>()).add(application);
|
||||
}
|
||||
// 以付款处理单源单id/源单编号建立索引,与本地金蝶单据id/单据编号勾稽
|
||||
Map<String, BfiPayBillVO> payBillBySourceId = new HashMap<>();
|
||||
Map<String, BfiPayBillVO> payBillBySourceNumber = new HashMap<>();
|
||||
for (String orgNumber : orgGroups.keySet()) {
|
||||
for (BfiPayBillVO payBill : queryPayBillPages(orgNumber)) {
|
||||
if (Func.isEmpty(payBill.getBillno())) continue;
|
||||
if (payBill.getSourcebillid() != null) {
|
||||
payBillBySourceId.put(String.valueOf(payBill.getSourcebillid()), payBill);
|
||||
}
|
||||
if (Func.isNotEmpty(payBill.getSourcebillnumber())) {
|
||||
payBillBySourceNumber.put(payBill.getSourcebillnumber(), payBill);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (PaymentApplication application : recordCandidates.values()) {
|
||||
BfiPayBillVO payBill = matchPayBill(application, payBillBySourceId, payBillBySourceNumber);
|
||||
if (payBill == null) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
self.applyPayBillRecord(application, payBill);
|
||||
recordWritten++;
|
||||
} catch (Exception exception) {
|
||||
recordFailed++;
|
||||
log.error("付款申请[{}]回填金蝶付款处理记录异常", application.getPaymentNo(), exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (applications.isEmpty() && recordCandidates.isEmpty()) {
|
||||
return "金蝶付款结果回写:无待回写付款申请";
|
||||
}
|
||||
return "金蝶付款结果回写完成:待查付款申请" + applications.size() + "笔,回写单据信息" + updated
|
||||
+ "笔(付款完成" + paidCount + "笔,失败" + failed + "笔),回填付款记录" + recordWritten + "条(失败" + recordFailed + "条)";
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public boolean applyPayApplyResult(PaymentApplication entity, BfiPayApplyVO payApply) {
|
||||
if (Func.isNotEmpty(payApply.getBillno())) {
|
||||
entity.setKingdeeBillNo(payApply.getBillno());
|
||||
}
|
||||
if (payApply.getId() != null) {
|
||||
entity.setKingdeeBillId(String.valueOf(payApply.getId()));
|
||||
}
|
||||
String kingdeeStatus = kingdeeResultStatus(payApply);
|
||||
entity.setKingdeeStatus(kingdeeStatus);
|
||||
// 已付金额:A103-2 分录 e_paidamt 汇总;分录缺失且已付款时兜底取申请金额
|
||||
if (Func.isNotEmpty(payApply.getEntry())) {
|
||||
BigDecimal paidAmount = payApply.getEntry().stream()
|
||||
.map(BfiPayApplyEntryVO::getEPaidamt).filter(Objects::nonNull)
|
||||
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
entity.setPaidAmount(paidAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
} else if (KingdeeBfiConstant.KINGDEE_STATUS_PAID.equals(kingdeeStatus)) {
|
||||
entity.setPaidAmount(money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
updateById(entity);
|
||||
if (KingdeeBfiConstant.KINGDEE_STATUS_PAID.equals(kingdeeStatus)) {
|
||||
// 付款完成:联动履约付款数据(预结算预付记录、正式结算付款记录)
|
||||
syncPreSettlementAdvance(entity);
|
||||
syncFormalSettlementPayments(entity);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void applyPayBillRecord(PaymentApplication entity, BfiPayBillVO payBill) {
|
||||
// 清理历史模拟付款记录,真实付款记录以金蝶付款处理单为准
|
||||
recordMapper.delete(Wrappers.<PaymentApplicationRecord>lambdaQuery()
|
||||
.eq(PaymentApplicationRecord::getPaymentApplicationId, entity.getId())
|
||||
.likeRight(PaymentApplicationRecord::getPaymentNo, MOCK_PAYMENT_RECORD_PREFIX));
|
||||
String billNo = payBill.getBillno();
|
||||
PaymentApplicationRecord record = recordMapper.selectList(Wrappers.<PaymentApplicationRecord>lambdaQuery()
|
||||
.eq(PaymentApplicationRecord::getPaymentApplicationId, entity.getId())
|
||||
.eq(Func.isNotEmpty(billNo), PaymentApplicationRecord::getPaymentNo, billNo)
|
||||
.last("limit 1")).stream().findFirst().orElse(null);
|
||||
boolean isNew = record == null;
|
||||
if (isNew) {
|
||||
record = new PaymentApplicationRecord();
|
||||
record.setPaymentApplicationId(entity.getId());
|
||||
record.setPaymentNo(billNo);
|
||||
}
|
||||
if (payBill.getActpayamt() != null) {
|
||||
record.setPaidAmount(payBill.getActpayamt().setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
record.setPaidDate(parseKingdeeDate(payBill.getPaydate()));
|
||||
record.setKingdeeBillNo(billNo);
|
||||
// 付款处理单查询结果整体留档(凭证号、银行流水、电子回单等信息),便于追溯
|
||||
record.setVoucherJson(JsonUtil.toJson(payBill));
|
||||
if (isNew) {
|
||||
recordMapper.insert(record);
|
||||
} else {
|
||||
recordMapper.updateById(record);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A103-2 返回状态映射本地金蝶同步状态:
|
||||
* paystatus=Alreadypay 已付款(终态)优先;billstatus=D 已关闭(终态)次之;
|
||||
* paystatus=Inpayment 付款中;其余(A暂存/B已提交/C已审核 + Unpaid)保持已生成继续轮询
|
||||
*
|
||||
* @param payApply A103-2 查询返回的付款申请单
|
||||
* @return 本地金蝶同步状态
|
||||
*/
|
||||
private String kingdeeResultStatus(BfiPayApplyVO payApply) {
|
||||
if (KingdeeBfiConstant.BFI_PAY_STATUS_ALREADY_PAY.equals(payApply.getPaystatus())) {
|
||||
return KingdeeBfiConstant.KINGDEE_STATUS_PAID;
|
||||
}
|
||||
if (KingdeeBfiConstant.BFI_BILL_STATUS_CLOSED.equals(payApply.getBillstatus())) {
|
||||
return KingdeeBfiConstant.KINGDEE_STATUS_CLOSED;
|
||||
}
|
||||
if (KingdeeBfiConstant.BFI_PAY_STATUS_IN_PAYMENT.equals(payApply.getPaystatus())) {
|
||||
return KingdeeBfiConstant.KINGDEE_STATUS_PAYING;
|
||||
}
|
||||
return KingdeeBfiConstant.KINGDEE_STATUS_SYNCED;
|
||||
}
|
||||
|
||||
/**
|
||||
* A103-2 分页查询:来源单据号批量 + 外部系统编号(防重键),自动翻页
|
||||
*
|
||||
* @param awrpSrcCodes 来源单据号(付款申请单号)批次
|
||||
* @return 付款申请单列表
|
||||
*/
|
||||
private List<BfiPayApplyVO> queryPayApplyPages(List<String> awrpSrcCodes) {
|
||||
BfiPayApplyQueryDTO param = new BfiPayApplyQueryDTO();
|
||||
param.setAwrpSrcCode(awrpSrcCodes);
|
||||
param.setAwrpSrcSys(bfiPayApplySrcSys);
|
||||
return BfiUtil.queryAllPages(pageNo -> {
|
||||
param.setPageNo(pageNo);
|
||||
param.setPageSize(PAY_RESULT_PAGE_SIZE);
|
||||
return bfiService.queryPayApply(param);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* A103-4 分页查询:按付款人(公司)编码 + 最近1个月业务日期窗口(接口限制不超过1个月),自动翻页
|
||||
*
|
||||
* @param orgNumber 付款人(公司)编码
|
||||
* @return 付款处理单列表
|
||||
*/
|
||||
private List<BfiPayBillVO> queryPayBillPages(String orgNumber) {
|
||||
LocalDate endDate = LocalDate.now();
|
||||
// 业务日期窗口控制在不超过1个月
|
||||
LocalDate beginDate = endDate.minusMonths(1).plusDays(1);
|
||||
BfiPayBillQueryDTO param = new BfiPayBillQueryDTO();
|
||||
param.setOrgNumber(List.of(orgNumber));
|
||||
param.setBeginBizdate(beginDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
param.setEndBizdate(endDate.format(DateTimeFormatter.ISO_LOCAL_DATE));
|
||||
return BfiUtil.queryAllPages(pageNo -> {
|
||||
param.setPageNo(pageNo);
|
||||
param.setPageSize(PAY_RESULT_PAGE_SIZE);
|
||||
return bfiService.queryPayBill(param);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 以金蝶付款处理单源单id(=付款申请单星瀚id)/源单编号(=付款申请单单据编号)勾稽匹配
|
||||
*
|
||||
* @param application 付款申请实体
|
||||
* @param payBillBySourceId 源单id索引
|
||||
* @param payBillBySourceNumber 源单编号索引
|
||||
* @return 匹配的付款处理单,未匹配返回 null
|
||||
*/
|
||||
private BfiPayBillVO matchPayBill(PaymentApplication application, Map<String, BfiPayBillVO> payBillBySourceId,
|
||||
Map<String, BfiPayBillVO> payBillBySourceNumber) {
|
||||
if (Func.isNotEmpty(application.getKingdeeBillId())
|
||||
&& payBillBySourceId.containsKey(application.getKingdeeBillId())) {
|
||||
return payBillBySourceId.get(application.getKingdeeBillId());
|
||||
}
|
||||
if (Func.isNotEmpty(application.getKingdeeBillNo())) {
|
||||
return payBillBySourceNumber.get(application.getKingdeeBillNo());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** 统计付款申请的真实付款记录数(剔除历史模拟记录,兼容手工记录付款单号为空的场景) */
|
||||
private long realRecordCount(Long applicationId) {
|
||||
return recordMapper.selectCount(Wrappers.<PaymentApplicationRecord>lambdaQuery()
|
||||
.eq(PaymentApplicationRecord::getPaymentApplicationId, applicationId)
|
||||
.and(wrapper -> wrapper.notLikeRight(PaymentApplicationRecord::getPaymentNo, MOCK_PAYMENT_RECORD_PREFIX)
|
||||
.or().isNull(PaymentApplicationRecord::getPaymentNo)));
|
||||
}
|
||||
|
||||
/** 解析金蝶返回日期(yyyy-MM-dd 或 yyyy-MM-dd HH:mm:ss),解析失败返回 null */
|
||||
private LocalDate parseKingdeeDate(String dateText) {
|
||||
if (Func.isBlank(dateText)) return null;
|
||||
try {
|
||||
return LocalDate.parse(dateText.trim().substring(0, 10));
|
||||
} catch (Exception exception) {
|
||||
log.warn("金蝶日期解析失败:{}", dateText);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/** 时间字符串比较(yyyy-MM-dd HH:mm:ss),空值视为最小 */
|
||||
private int strCompare(String left, String right) {
|
||||
if (Objects.equals(left, right)) return 0;
|
||||
if (left == null) return -1;
|
||||
if (right == null) return 1;
|
||||
return left.compareTo(right);
|
||||
}
|
||||
|
||||
/**
|
||||
* 推送金蝶付款申请单(A103-3 付款申请保存,预付款路径),返回金蝶单据id
|
||||
*
|
||||
@@ -478,40 +780,66 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
String billDateText = (entity.getApplyDate() == null ? LocalDate.now() : entity.getApplyDate()).format(DateTimeFormatter.BASIC_ISO_DATE);
|
||||
BigDecimal applyAmount = money(entity.getAppliedAmount()).setScale(2, RoundingMode.HALF_UP);
|
||||
BfiApBillSaveDTO param = new BfiApBillSaveDTO();
|
||||
// 【必填】单据类型.编码
|
||||
param.setBilltypeidNumber(billtypeNumber);
|
||||
// 【必填】结算组织.编码
|
||||
param.setOrgNumber(orgNumber);
|
||||
// 【必填】单据日期
|
||||
param.setBizdate(billDateText);
|
||||
// 【必填】往来类型(bd_supplier:供应商)
|
||||
param.setAsstacttype(KingdeeBfiConstant.BFI_SUPPLIER_ASSTACT_TYPE);
|
||||
// 【必填】往来户.编码
|
||||
param.setAsstactNumber(bfiSupplierCode(entity));
|
||||
// 【必填】付款方式(CASH:现购, CREDIT:赊购)
|
||||
param.setPurmode(KingdeeBfiConstant.BFI_AP_BILL_PUR_MODE);
|
||||
// 【必填】结算币别.货币代码
|
||||
param.setCurrencyNumber(currency);
|
||||
// 【必填】付款组织.编码
|
||||
param.setPayorgNumber(orgNumber);
|
||||
param.setPurorgNumber(orgNumber);
|
||||
// 采购组织.编码(非必传)
|
||||
// param.setPurorgNumber(orgNumber);
|
||||
// 【必填】款项性质.编码
|
||||
param.setPaypropertyNumber(KingdeeBfiConstant.BFI_AP_BILL_PAY_PROPERTY);
|
||||
// TODO 待确认:结算方式编码(settlementtype_number)与税率编码(taxrateid_number)需金蝶侧确认后补充,当前不传
|
||||
param.setIsincludetax(true);
|
||||
param.setIspricetotal(true);
|
||||
// 录入含税价(非必传)
|
||||
// param.setIsincludetax(true);
|
||||
// 录入总价,为 true 后可不录入单价(非必传)
|
||||
// param.setIspricetotal(true);
|
||||
// 汇率表.编码(非必传)
|
||||
param.setExratetableNumber(bfiPayApplyExratetable);
|
||||
// 汇率日期(非必传)
|
||||
param.setExratedate(billDateText);
|
||||
param.setAsstactname(entity.getPayeeName());
|
||||
// 往来单位名称(非必传)
|
||||
// param.setAsstactname(entity.getPayeeName());
|
||||
// 备注(非必传)
|
||||
param.setRemark(entity.getPaymentNo() + (Func.isEmpty(entity.getSettlementNo()) ? "" : " " + entity.getSettlementNo()));
|
||||
// 【必填】外部系统编号(候选键)
|
||||
param.setAwrpSrcSys(bfiPayApplySrcSys);
|
||||
// 【必填】外部系统来源单据号(候选键)
|
||||
param.setAwrpSrcCode(entity.getPaymentNo());
|
||||
// 来源单据(非必传,有上游合同时传 awrp_contract_info)
|
||||
param.setAwrpSrcBt(KingdeeBfiConstant.BFI_CONTRACT_SRC_BT);
|
||||
// 上游外部系统单据号/上游合同号(非必传)
|
||||
if (Func.isNotEmpty(entity.getContractNo())) {
|
||||
param.setAwrpSrcConNo(entity.getContractNo());
|
||||
}
|
||||
// 【必填】外部单据类型
|
||||
param.setAwrpSrcType(KingdeeBfiConstant.BFI_AP_BILL_SRC_TYPE);
|
||||
|
||||
BfiApBillSaveEntryDTO entry = new BfiApBillSaveEntryDTO();
|
||||
// 【必填】物料编码.编码
|
||||
entry.setMaterialNumber(KingdeeBfiConstant.BFI_AP_BILL_MATERIAL);
|
||||
// 【必填】计量单位.编码
|
||||
entry.setMeasureunitNumber(KingdeeBfiConstant.BFI_AP_BILL_MEASURE_UNIT);
|
||||
// 无吨位数据:数量固定 1,配合 ispricetotal=true 录入总价(含税应付金额)
|
||||
// 【必填】数量(无吨位数据:数量固定 1,配合 ispricetotal=true 录入总价)
|
||||
entry.setQuantity(BigDecimal.ONE);
|
||||
// 应付金额(含税)(非必传)
|
||||
entry.setEPricetaxtotal(applyAmount);
|
||||
// 【必填】项目.项目编码
|
||||
entry.setProjectNumber(bfiProjectNumber(entity));
|
||||
// 应付单分录唯一标识,A103-1 下推时以 awrp_finapbill_entryid 关联此行
|
||||
// 【必填】外部系统来源分录标识(候选键),A103-1 下推时以 awrp_finapbill_entryid 关联此行
|
||||
entry.setAwrpEntryCode(entity.getPaymentNo() + "-1");
|
||||
// 分录备注(非必传)
|
||||
entry.setERemark(entity.getRemark());
|
||||
param.setDetailentry(List.of(entry));
|
||||
|
||||
@@ -599,7 +927,7 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
}
|
||||
|
||||
private void markSyncFailed(PaymentApplication entity) {
|
||||
entity.setKingdeeStatus("failed");
|
||||
entity.setKingdeeStatus(KingdeeBfiConstant.KINGDEE_STATUS_FAILED);
|
||||
updateById(entity);
|
||||
}
|
||||
|
||||
@@ -652,6 +980,13 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl<PaymentApplic
|
||||
return project == null || Func.isEmpty(project.getProjectCode()) ? KingdeeBfiConstant.BFI_PROJECT_NUMBER_NONE : project.getProjectCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* 付款完成模拟(已停用):原在同步成功后模拟付款完成数据(paidAmount = 申请金额、MOCK-PAY- 前缀付款记录)。
|
||||
* 真实付款结果现由定时回写任务 syncKingdeePaymentResult 经 A103-2/A103-4 查询回填驱动,
|
||||
* 暂保留本方法以备联调环境无金蝶付款数据时模拟使用。
|
||||
*
|
||||
* @param entity 付款申请实体
|
||||
*/
|
||||
private void mockPaymentRecords(PaymentApplication entity) {
|
||||
recordMapper.delete(Wrappers.<PaymentApplicationRecord>lambdaQuery()
|
||||
.eq(PaymentApplicationRecord::getPaymentApplicationId, entity.getId()));
|
||||
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* BladeX Commercial License Agreement
|
||||
* Copyright (c) 2018-2099, https://bladex.cn. All rights reserved.
|
||||
* <p>
|
||||
* Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX.
|
||||
* <p>
|
||||
* 1. This software is for development use only under a valid license from BladeX.
|
||||
* <p>
|
||||
* 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited.
|
||||
* <p>
|
||||
* 3. Licensees may copyright their own code but cannot use segments from this software for such purposes.
|
||||
* Copyright of this software remains with BladeX.
|
||||
* <p>
|
||||
* Using this software signifies agreement to this License, and the software must not be used for illegal purposes.
|
||||
* <p>
|
||||
* THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development.
|
||||
* <p>
|
||||
* Author: Chill Zhuang (bladejava@qq.com)
|
||||
*/
|
||||
package org.springblade.transport.task;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springblade.transport.service.IPaymentApplicationService;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 金蝶付款结果定时回写任务
|
||||
* <p>
|
||||
* 定时查询金蝶(星瀚)付款结果并更新付款信息与履约付款数据:
|
||||
* A103-2 付款申请单查询回填金蝶单据编号与单据状态,A103-4 付款处理查询回填付款记录。
|
||||
* 业务逻辑收敛在 {@link IPaymentApplicationService#syncKingdeePaymentResult()},便于手动触发联调。
|
||||
* <p>
|
||||
* 调度配置(Nacos thirdParty.bfi.payResult.*):syncEnabled 开关、cron 表达式。
|
||||
*
|
||||
* @author Chill
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class PaymentKingdeeResultTask {
|
||||
|
||||
private final IPaymentApplicationService paymentApplicationService;
|
||||
|
||||
/** 付款结果回写开关,默认开启 */
|
||||
@Value("${thirdParty.bfi.payResult.syncEnabled:false}")
|
||||
private boolean syncEnabled;
|
||||
|
||||
/** 金蝶付款结果回写调度,默认每10分钟执行一次 */
|
||||
@Scheduled(cron = "${thirdParty.bfi.payResult.cron:0 */10 * * * ?}")
|
||||
public void syncKingdeePaymentResult() {
|
||||
if (!syncEnabled) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
String summary = paymentApplicationService.syncKingdeePaymentResult();
|
||||
log.info("定时回写金蝶付款结果:{}", summary);
|
||||
} catch (Exception exception) {
|
||||
log.error("定时回写金蝶付款结果异常", exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -22,6 +22,7 @@ package org.springblade.transport.wrapper;
|
||||
import org.springblade.core.mp.support.BaseEntityWrapper;
|
||||
import org.springblade.core.tool.utils.BeanUtil;
|
||||
import org.springblade.system.cache.UserCache;
|
||||
import org.springblade.transport.constant.KingdeeBfiConstant;
|
||||
import org.springblade.transport.pojo.entity.PaymentApplication;
|
||||
import org.springblade.transport.pojo.vo.PaymentApplicationVO;
|
||||
|
||||
@@ -50,8 +51,11 @@ public class PaymentApplicationWrapper extends BaseEntityWrapper<PaymentApplicat
|
||||
default -> entity.getApprovalStatus();
|
||||
});
|
||||
vo.setKingdeeStatusName(switch (entity.getKingdeeStatus() == null ? "" : entity.getKingdeeStatus()) {
|
||||
case "synced" -> "已生成";
|
||||
case "failed" -> "生成失败";
|
||||
case KingdeeBfiConstant.KINGDEE_STATUS_SYNCED -> "已生成";
|
||||
case KingdeeBfiConstant.KINGDEE_STATUS_PAYING -> "付款中";
|
||||
case KingdeeBfiConstant.KINGDEE_STATUS_PAID -> "已付款";
|
||||
case KingdeeBfiConstant.KINGDEE_STATUS_CLOSED -> "已关闭";
|
||||
case KingdeeBfiConstant.KINGDEE_STATUS_FAILED -> "生成失败";
|
||||
default -> "未生成";
|
||||
});
|
||||
return vo;
|
||||
|
||||
Vendored
+60
@@ -26,10 +26,15 @@
|
||||
package org.springblade.thirdparty.bfi.util;
|
||||
|
||||
import org.springblade.core.tool.utils.DateUtil;
|
||||
import org.springblade.core.tool.utils.Func;
|
||||
import org.springblade.thirdparty.bfi.pojo.dto.BfiBaseDTO;
|
||||
import org.springblade.thirdparty.bfi.pojo.vo.BfiPageVO;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.function.IntFunction;
|
||||
|
||||
/**
|
||||
* 业财融合(BFI)接口通用参数工具类
|
||||
@@ -46,6 +51,11 @@ public class BfiUtil {
|
||||
*/
|
||||
public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
/**
|
||||
* 分页查询默认最大页数保护,防止金蝶侧 lastPage 异常时死循环
|
||||
*/
|
||||
public static final int DEFAULT_MAX_PAGES = 50;
|
||||
|
||||
private BfiUtil() {
|
||||
}
|
||||
|
||||
@@ -79,4 +89,54 @@ public class BfiUtil {
|
||||
param.setTimestamp(timestamp());
|
||||
return param;
|
||||
}
|
||||
|
||||
/**
|
||||
* BFI 分页查询通用翻页,自动翻页汇总全量数据行(默认最大页数保护)
|
||||
* <p>
|
||||
* 适用所有返回 {@link BfiPageVO} 的查询接口
|
||||
* (A103-2 付款申请单查询、A103-4 付款处理查询、A102-4 财务应付单查询、A902-1 客户查询等):
|
||||
* <pre>{@code
|
||||
* List<BfiPayApplyVO> rows = BfiUtil.queryAllPages(pageNo -> {
|
||||
* param.setPageNo(pageNo);
|
||||
* param.setPageSize(pageSize);
|
||||
* return bfiService.queryPayApply(param);
|
||||
* });
|
||||
* }</pre>
|
||||
*
|
||||
* @param pageFetcher 页码 -> 分页结果,lambda 内负责设置页码(必要时含页大小)并调用接口
|
||||
* @param <T> 数据行类型
|
||||
* @return 全量数据行
|
||||
*/
|
||||
public static <T> List<T> queryAllPages(IntFunction<BfiPageVO<T>> pageFetcher) {
|
||||
return queryAllPages(pageFetcher, DEFAULT_MAX_PAGES);
|
||||
}
|
||||
|
||||
/**
|
||||
* BFI 分页查询通用翻页,自动翻页汇总全量数据行
|
||||
* <p>
|
||||
* 终止条件:返回页为空、数据行为空、lastPage=true、数据行不满一页(按返回 pageSize 判断)、超出最大页数保护
|
||||
*
|
||||
* @param pageFetcher 页码 -> 分页结果,lambda 内负责设置页码(必要时含页大小)并调用接口
|
||||
* @param maxPages 最大页数保护
|
||||
* @param <T> 数据行类型
|
||||
* @return 全量数据行
|
||||
*/
|
||||
public static <T> List<T> queryAllPages(IntFunction<BfiPageVO<T>> pageFetcher, int maxPages) {
|
||||
List<T> rows = new ArrayList<>();
|
||||
for (int pageNo = 1; pageNo <= maxPages; pageNo++) {
|
||||
BfiPageVO<T> page = pageFetcher.apply(pageNo);
|
||||
if (page == null || Func.isEmpty(page.getRows())) {
|
||||
break;
|
||||
}
|
||||
rows.addAll(page.getRows());
|
||||
if (Boolean.TRUE.equals(page.getLastPage())) {
|
||||
break;
|
||||
}
|
||||
// 末页数据不满一页时无需继续翻页(pageSize 取返回值,返回为空时依赖 lastPage 与最大页数保护)
|
||||
if (page.getPageSize() != null && page.getRows().size() < page.getPageSize()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
# BFI 分页查询通用翻页工具(BfiUtil.queryAllPages)技术文档
|
||||
|
||||
> 适用系统:TMS(BladeX 微服务平台,模块:`blade-third-party-api/blade-bfi-api`)
|
||||
> 组件类型:通用工具类(非业务流程),服务业财融合全部 `BfiPageVO` 分页查询接口
|
||||
> 关联文档:`2026-09-17-付款管理金蝶付款结果定时回写(A103-2付款申请单查询+A103-4付款处理查询)技术文档.md`
|
||||
> 最后更新:2026-09-17
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与范围
|
||||
|
||||
`IBfiService` 中共有 9 个查询接口返回 `BfiPageVO<T>` 分页结构,各接口的分页翻页逻辑完全一致(页码递增、空页/末页终止、防死循环保护)。原实现在 `PaymentApplicationServiceImpl` 中以私有方法 `queryPage` 承载,无法被其他查询场景复用,现抽取为 `BfiUtil.queryAllPages` 通用工具。
|
||||
|
||||
设计目标:
|
||||
|
||||
- **一处实现,全部查询复用**:付款结果回写(A103-2/A103-4)之外,后续 A102-4 应付勾稽、A302-4 应收核对、A303-4 开票结果回写、A801-3/A301-1/A301-2 收票收款认领等场景接入时零成本复用
|
||||
- **调用方只关心"取第 N 页"**:翻页推进、终止判断、全量汇总由工具类负责
|
||||
- **防死循环**:金蝶侧 `lastPage` 异常(恒为 false)时按最大页数保护终止
|
||||
|
||||
## 2. 适用接口清单
|
||||
|
||||
| 接口编号 | 名称 | `IBfiService` 方法 | 返回结构 |
|
||||
| --- | --- | --- | --- |
|
||||
| A103-2 | 付款申请单查询 | `queryPayApply` | `BfiPageVO<BfiPayApplyVO>` |
|
||||
| A103-4 | 付款处理查询 | `queryPayBill` | `BfiPageVO<BfiPayBillVO>` |
|
||||
| A102-4 | 财务应付单查询 | `queryApBill` | `BfiPageVO<BfiApBillVO>` |
|
||||
| A302-4 | 财务应收单查询 | `queryArBill` | `BfiPageVO<BfiArBillVO>` |
|
||||
| A303-4 | 开票申请单查询 | `queryInvoiceApply` | `BfiPageVO<BfiInvoiceApplyVO>` |
|
||||
| A902-1 | 客户查询 | `queryCustomer` | `BfiPageVO<BfiCustomerVO>` |
|
||||
| A801-3 | 收票单查询 | `queryReceivedInvoice` | `BfiPageVO<BfiReceivedInvoiceVO>` |
|
||||
| A301-1 | 收款单查询 | `queryRecBill` | `BfiPageVO<BfiRecBillVO>` |
|
||||
| A301-2 | 收款认领公告查询 | `queryClaimAnnounce` | `BfiPageVO<BfiClaimAnnounceVO>` |
|
||||
|
||||
不适用:`queryInvoicePool`(A801-1,直接返回 List)、`queryExchangeRate`(A801-4,单页调用由 `BfiServiceImpl` 内部处理)。
|
||||
|
||||
## 3. API 定义
|
||||
|
||||
```java
|
||||
// 默认最大页数保护(50 页)
|
||||
public static <T> List<T> queryAllPages(IntFunction<BfiPageVO<T>> pageFetcher)
|
||||
|
||||
// 自定义最大页数保护
|
||||
public static <T> List<T> queryAllPages(IntFunction<BfiPageVO<T>> pageFetcher, int maxPages)
|
||||
```
|
||||
|
||||
| 参数 | 说明 |
|
||||
| --- | --- |
|
||||
| `pageFetcher` | 页码 → 分页结果函数。lambda 内负责按传入页码设置请求参数(页码必设,页大小按各接口限制设置)并调用 `IBfiService` 对应查询方法 |
|
||||
| `maxPages` | 最大页数保护,防止金蝶侧 `lastPage` 异常导致死循环;默认 `BfiUtil.DEFAULT_MAX_PAGES = 50` |
|
||||
| 返回值 | 全量数据行汇总列表(按页序追加);无数据时返回空列表 |
|
||||
|
||||
## 4. 翻页终止条件(按判断顺序)
|
||||
|
||||
| 序 | 条件 | 说明 |
|
||||
| --- | --- | --- |
|
||||
| 1 | 返回页为 null 或数据行为空 | 本页无数据即止 |
|
||||
| 2 | `lastPage = true` | 金蝶标识已最后一页 |
|
||||
| 3 | `rows.size() < 响应体 pageSize` | 末页数据不满一页,无需继续;**pageSize 取响应体返回值**(`BfiPageVO.pageSize`),调用方无需额外传入,响应体未返回 pageSize 时自动退化为依赖条件 2 与最大页数保护 |
|
||||
| 4 | 页码超过 `maxPages` | 防御性终止,异常场景兜底 |
|
||||
|
||||
## 5. 用法示例
|
||||
|
||||
```java
|
||||
// A103-2 付款申请单查询:来源单据号批量查询
|
||||
private List<BfiPayApplyVO> queryPayApplyPages(List<String> awrpSrcCodes) {
|
||||
BfiPayApplyQueryDTO param = new BfiPayApplyQueryDTO();
|
||||
param.setAwrpSrcCode(awrpSrcCodes);
|
||||
param.setAwrpSrcSys(bfiPayApplySrcSys);
|
||||
return BfiUtil.queryAllPages(pageNo -> {
|
||||
param.setPageNo(pageNo);
|
||||
param.setPageSize(PAGE_SIZE);
|
||||
return bfiService.queryPayApply(param);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
lambda 内通过闭包复用同一参数对象,仅需覆盖 `pageNo`;请求参数的各接口约束(如 A103-4 付款组织编码必填、时间范围不超过 1 个月)由调用方在构造参数时保证。
|
||||
|
||||
## 6. 设计说明
|
||||
|
||||
| 决策 | 说明 |
|
||||
| --- | --- |
|
||||
| 抽取为 `IntFunction<BfiPageVO<T>>` 而非传入 DTO | 各查询接口的入参 DTO、Feign 方法签名各不相同,仅"按页码取结果"语义一致;函数式入参对调用方约束最小,DTO 字段设置留在调用方(编译期类型安全) |
|
||||
| 不再需要 `rowsGetter` 参数 | 全部查询统一返回 `BfiPageVO`,`getRows` 内聚到工具类;早期版本传入 `BfiPageVO::getRows` 属冗余 |
|
||||
| pageSize 判满取响应体 | 早期版本要求调用方传入请求 pageSize,两处易失配;响应体 `pageSize` 为金蝶实际执行值,更可靠 |
|
||||
| 空列表而非 null | 无数据返回空列表,调用方无需判空 |
|
||||
|
||||
## 7. 迁移记录
|
||||
|
||||
| 变更 | 内容 |
|
||||
| --- | --- |
|
||||
| 新增 | `BfiUtil.queryAllPages` 两个重载 + `DEFAULT_MAX_PAGES` 常量 |
|
||||
| 删除 | `PaymentApplicationServiceImpl` 私有方法 `queryPage` 及 `PAY_RESULT_MAX_PAGES` 常量 |
|
||||
| 替换 | 付款结果回写 `queryPayApplyPages` / `queryPayBillPages` 改调 `BfiUtil.queryAllPages` |
|
||||
|
||||
## 8. 涉及代码位置
|
||||
|
||||
| 内容 | 位置 |
|
||||
| --- | --- |
|
||||
| 工具类 | `blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/util/BfiUtil.java` |
|
||||
| 当前唯一使用方 | `blade-service/blade-transport/.../service/impl/PaymentApplicationServiceImpl.java`(queryPayApplyPages / queryPayBillPages) |
|
||||
@@ -0,0 +1,167 @@
|
||||
# 付款管理金蝶付款结果定时回写(A103-2 付款申请单查询 + A103-4 付款处理查询)技术文档
|
||||
|
||||
> 适用系统:TMS(BladeX 微服务平台,服务:blade-transport)
|
||||
> 对接系统:金蝶云·星瀚(业财融合 BFI,经 iPaaS 网关)
|
||||
> 关联文档:`doc/docs/TMS业财融合-集成流程图文字版与业务流程梳理.md`(4.3 节步骤 11)、`付款申请同步金蝶(星瀚)技术文档.md`、`2026-09-16-付款管理正式付款同步金蝶(财务应付单+付款申请下推)技术文档.md`、`2026-09-17-BFI分页查询通用翻页工具(BfiUtil-queryAllPages)技术文档.md`
|
||||
> 最后更新:2026-09-17
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景与范围
|
||||
|
||||
付款申请推送金蝶(A103-1/A103-3)成功后,金蝶侧依次完成:财务共享审批(黑盒,机器人处理)→ 付款处理 → 付款完成。原实现将"付款完成"整段模拟(`paidAmount = 申请金额`、`MOCK-PAY-` 前缀付款记录、履约付款数据直接置 `paid`),本文档建设真实回写链路并停用模拟:
|
||||
|
||||
- 触发入口:
|
||||
- **定时任务**:`PaymentKingdeeResultTask`,`@Scheduled` 默认每 10 分钟(`thirdParty.bfi.payResult.cron`),开关 `thirdParty.bfi.payResult.syncEnabled`
|
||||
- **手动触发**:付款管理页面"同步付款结果"按钮(权限码复用 `payment_application_sync`)→ `POST /payment-application/sync-kingdee-result`
|
||||
- 对接接口:
|
||||
- **A103-2 付款申请单查询** `POST /ierp/kapi/v2/awrp/ap/ap_payapply/query`:按 `awrp_src_code`(=paymentNo)批量查询,回填金蝶单据编号(billno)、单据状态、付款状态、已付金额
|
||||
- **A103-4 付款处理查询** `POST /ierp/kapi/v2/awrp/cas/cas_paybill/query`:按付款人(公司)编码 + 业务日期窗口查询,回填付款记录(付款单号、付款日期、付款金额、整体报文留档)
|
||||
- 联动更新:付款完成(paystatus=Alreadypay)时复用既有逻辑联动履约付款数据(预结算预付记录 `blade_pre_settlement_advance`、正式结算付款记录 `blade_formal_settlement_payment`)
|
||||
|
||||
## 2. 业务流程
|
||||
|
||||
### 2.1 集成链路(对应方案文档 4.3 节步骤 11)
|
||||
|
||||
```
|
||||
付款申请已推送金蝶(kingdeeStatus = synced)
|
||||
→ 定时任务(每10分钟)
|
||||
→ A103-2 付款申请单查询(awrp_src_code 批量,50单/批)
|
||||
回填 kingdee_bill_id / kingdee_bill_no(billno)/ kingdeeStatus / paid_amount
|
||||
→ A103-4 付款处理查询(付款中/已付款单据,按付款组织分组 + 最近1个月窗口)
|
||||
回填付款记录 blade_payment_application_record(并清理历史 MOCK-PAY- 模拟记录)
|
||||
→ 付款完成(Alreadypay):联动履约付款数据(预结算预付、正式结算付款记录)
|
||||
→ (后续环节)付款附件推送 A902、合同系统履约同步 C106-2,待建设
|
||||
```
|
||||
|
||||
### 2.2 定时回写时序
|
||||
|
||||
```
|
||||
PaymentKingdeeResultTask PaymentApplicationServiceImpl 星瀚(BFI)
|
||||
│ @Scheduled 每10分钟 │ │
|
||||
│ syncKingdeePaymentResult() ──────>│ │
|
||||
│ │ ①查本地 approved + synced/paying │
|
||||
│ │ ②awrp_src_code 分批(50) ─────────────────>│ A103-2
|
||||
│ │ <── billno/billstatus/paystatus/entry ────│
|
||||
│ │ 逐单独立事务:回填单号/状态/已付金额 │
|
||||
│ │ Alreadypay → 联动履约付款数据 │
|
||||
│ │ ③付款组织分组 + 最近1个月窗口 ──────────────>│ A103-4
|
||||
│ │ <── 付款处理单(sourcebillid/sourcebillnumber)│
|
||||
│ │ 勾稽匹配 → 回填付款记录(幂等) │
|
||||
│ <── 执行摘要日志 ──────────────────│ │
|
||||
```
|
||||
|
||||
### 2.3 查询范围与幂等约定
|
||||
|
||||
| 约定 | 说明 |
|
||||
| --- | --- |
|
||||
| A103-2 查询范围 | `approvalStatus = approved` 且 `kingdeeStatus IN (synced, paying)`;`paid/closed` 为终态不再轮询 |
|
||||
| A103-2 防重键 | `awrp_src_code`(=paymentNo)+ `awrp_src_sys`(=WLTMS)批量查询,50 单/批,自动翻页(页大小 200,最大 50 页保护) |
|
||||
| 同号多单 | 同一 `awrp_src_code` 返回多条时取 `modifytime` 最新的一条 |
|
||||
| A103-4 查询范围 | 付款中/已付款单据(本轮 A103-2 查到 `paystatus ≠ Unpaid` 的)+ 已付款但尚无真实付款记录的单据(补偿轮询) |
|
||||
| A103-4 入参约束 | 付款人(公司)编码必填(优先取 A103-2 返回 `payorg_number`,兜底按合同所属组织解析);业务日期窗口取最近 1 个月(接口限制不超过 1 个月) |
|
||||
| A103-4 勾稽键 | `sourcebillid`(=付款申请单星瀚id `kingdee_bill_id`)优先,`sourcebillnumber`(=付款申请单编号 `kingdee_bill_no`)兜底 |
|
||||
| 付款记录幂等 | 按 `payment_application_id + payment_no`(=付款处理单号)判重,存在则更新;回填时清理该申请单下 `MOCK-PAY-` 前缀的历史模拟记录 |
|
||||
| 逐单隔离 | 每笔申请通过 `SpringUtil` 自代理走独立事务(`applyPayApplyResult` / `applyPayBillRecord`),单条失败不中断批次,异常记录日志后继续 |
|
||||
|
||||
### 2.4 状态映射(kingdeeStatus 扩展)
|
||||
|
||||
| A103-2 返回 | 本地 kingdeeStatus | 展示名 | 是否终态 |
|
||||
| --- | --- | --- | --- |
|
||||
| `paystatus = Alreadypay` | `paid` | 已付款 | 是(触发履约联动) |
|
||||
| `billstatus = D`(已关闭)且未付款 | `closed` | 已关闭 | 是 |
|
||||
| `paystatus = Inpayment` | `paying` | 付款中 | 否(继续轮询) |
|
||||
| `billstatus = A暂存/B已提交/C已审核` + `paystatus = Unpaid` | `synced` | 已生成 | 否(继续轮询) |
|
||||
|
||||
既有状态保持不变:`unsynced` 未生成、`failed` 生成失败(可重推)。
|
||||
|
||||
## 3. 字段映射关系
|
||||
|
||||
### 3.1 A103-2 回写(`BfiPayApplyVO` → `PaymentApplication`)
|
||||
|
||||
| 星瀚字段 | 含义 | 落库字段 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| id | 星瀚单据内部主键 | `kingdee_bill_id` | A902 附件上传等后续环节使用 |
|
||||
| billno | 单据编号(FKSQ-xxxx) | `kingdee_bill_no` | 展示用金蝶单据号;A103-4 勾稽键之一 |
|
||||
| billstatus | 单据状态 A/B/C/D | `kingdee_status` | 见 2.4 状态映射 |
|
||||
| paystatus | 付款状态 | `kingdee_status` | 见 2.4 状态映射 |
|
||||
| entry[].e_paidamt | 分录已付金额(汇总) | `paid_amount` | 分录缺失且已付款时兜底取申请金额 |
|
||||
| payorg_number | 付款组织编码 | —(内存) | A103-4 付款人(公司)编码入参 |
|
||||
|
||||
### 3.2 A103-4 回填(`BfiPayBillVO` → `PaymentApplicationRecord`)
|
||||
|
||||
| 星瀚字段 | 含义 | 落库字段 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| billno | 付款处理单号 | `payment_no` / `kingdee_bill_no` | 幂等判重键 |
|
||||
| paydate | 付款日期 | `paid_date` | 取前 10 位解析 yyyy-MM-dd,解析失败置空并告警 |
|
||||
| actpayamt | 付款金额 | `paid_amount` | 2 位小数 |
|
||||
| 整体报文 | 付款处理单全量 JSON | `voucher_json` | 凭证号、银行流水、电子回单等信息留档追溯 |
|
||||
|
||||
## 4. 付款完成联动(履约付款数据)
|
||||
|
||||
付款状态变为 `paid` 时复用既有逻辑(与原模拟阶段一致,数据源改为真实回写值):
|
||||
|
||||
| 联动项 | 逻辑 | 位置 |
|
||||
| --- | --- | --- |
|
||||
| 预结算预付记录 | `syncPreSettlementAdvance`:`paid_amount` = 申请金额、`bill_status = paid`、`kingdee_advance_no` = 金蝶单据号,刷新预结算单预付汇总与正式结算来源预付统计 | PaymentApplicationServiceImpl |
|
||||
| 正式结算付款记录 | `syncFormalSettlementPayments`:按申请单-结算单关联分摊已付金额,创建/更新 `blade_formal_settlement_payment`(`bill_status = paid`、金蝶单据号),刷新结算单付款汇总 | 同上 |
|
||||
|
||||
部分付款(`paying`)仅更新付款申请 `paid_amount` 与付款记录,**不**触发履约联动(待付款完成一次性联动)。
|
||||
|
||||
## 5. 模拟逻辑停用说明
|
||||
|
||||
- `mockPaymentRecords()`(`MOCK-PAY-` 付款记录模拟)与 `syncKingdee` 内的付款完成模拟代码段**保留方法体、注释调用处**并备注原因:联调环境无金蝶付款数据时可恢复注释模拟使用。
|
||||
- `syncKingdee` 现仅落 `kingdee_bill_id` + `kingdee_status = synced`;`kingdee_bill_no`、`paid_amount`、付款记录、履约付款数据全部由定时回写驱动。
|
||||
- 存量数据兼容:
|
||||
- 已 `synced` 单据(`kingdee_bill_no` 存的是旧语义星瀚id)由任务首轮 A103-2 按 paymentNo 查询自动修正为 billno;
|
||||
- 手动重推幂等返回兼容旧数据(`kingdee_bill_id` 为空时回退返回 `kingdee_bill_no`)。
|
||||
|
||||
## 6. 数据库变更
|
||||
|
||||
| 变更 | 内容 | 脚本 |
|
||||
| --- | --- | --- |
|
||||
| 新增列 | `blade_payment_application.kingdee_bill_id` varchar(32):金蝶单据id(星瀚内部主键) | `doc/sql/transport/blade_payment_application_kingdee_bill_id_20260917.sql`(幂等可重复执行) |
|
||||
| 语义调整 | `kingdee_bill_no`:星瀚id → **金蝶单据编号 billno**(由回写任务回填) | 无 DDL |
|
||||
| 建表模板 | `blade_payment_application_20260821.sql` 已同步包含新列 | 新装环境直接执行 |
|
||||
|
||||
## 7. 配置项(Nacos:`thirdParty.bfi.payResult.*`)
|
||||
|
||||
模板见 `doc/nacos/blade-dev.yaml`、`blade-prod.yaml`、`third-party-api.yaml`:
|
||||
|
||||
| 配置键 | 环境变量 | 默认值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| thirdParty.bfi.payResult.syncEnabled | BFI_PAY_RESULT_SYNC_ENABLED | true | 回写任务开关 |
|
||||
| thirdParty.bfi.payResult.cron | BFI_PAY_RESULT_CRON | 0 */10 * * * ? | 调度表达式(默认每10分钟) |
|
||||
|
||||
调度启用:`TransportApplication` 增加 `@EnableScheduling`。
|
||||
|
||||
## 8. 异常处理
|
||||
|
||||
| 场景 | 行为 |
|
||||
| --- | --- |
|
||||
| A103-2/A103-4 接口级失败(status=false / errorCode≠0) | `BfiServiceImpl.commonCall` 抛 `BfiException`,当前批次终止,下轮任务自动重试 |
|
||||
| 单笔回写异常 | 记录 error 日志(含 paymentNo 与堆栈),计数后继续下一单 |
|
||||
| A103-4 付款组织编码获取失败(无 A103-2 数据且合同缺失/组织编码为空) | warn 日志跳过该单,下轮重试 |
|
||||
| 付款日期解析失败 | warn 日志,`paid_date` 置空,其余字段正常回填 |
|
||||
| 手动触发与定时任务并发 | 同单以 `updateById` 主键更新兜底,最终一致;付款记录按单号判重不重复插入 |
|
||||
|
||||
## 9. 涉及代码位置
|
||||
|
||||
| 内容 | 位置 |
|
||||
| --- | --- |
|
||||
| 定时任务入口 | `blade-service/blade-transport/.../task/PaymentKingdeeResultTask.java` |
|
||||
| 回写编排 | `blade-service/blade-transport/.../service/impl/PaymentApplicationServiceImpl.java`(syncKingdeePaymentResult :409 / applyPayApplyResult :512 / applyPayBillRecord :538 / queryPayApplyPages / queryPayBillPages / kingdeeResultStatus / matchPayBill) |
|
||||
| 手动触发接口 | `blade-service/blade-transport/.../controller/PaymentApplicationController.java`(POST /sync-kingdee-result) |
|
||||
| 状态常量 | `blade-service/blade-transport/.../constant/KingdeeBfiConstant.java`(KINGDEE_STATUS_* / BFI_PAY_STATUS_* / BFI_BILL_STATUS_CLOSED) |
|
||||
| 实体/翻译 | `blade-transport-api/.../pojo/entity/PaymentApplication.java`(kingdeeBillId);`transport/wrapper/PaymentApplicationWrapper.java`(状态翻译) |
|
||||
| BFI 客户端 | `blade-third-party-api/blade-bfi-api/.../service/IBfiService.java`(queryPayApply / queryPayBill,本次无改动);分页通用翻页 `util/BfiUtil.java`(queryAllPages,A102-4/A302-4/A303-4 等分页查询可复用) |
|
||||
| 前端 | `tms-erp-web/src/views/payment/payment-application.vue`(同步付款结果按钮);`src/api/payment/paymentApplication.js`(syncKingdeeResult / kingdeeStatusOptions) |
|
||||
| SQL | `doc/sql/transport/blade_payment_application_kingdee_bill_id_20260917.sql`;建表模板 `blade_payment_application_20260821.sql` |
|
||||
| Nacos 配置模板 | `doc/nacos/blade-dev.yaml`、`blade-prod.yaml`、`third-party-api.yaml`(thirdParty.bfi.payResult.*) |
|
||||
|
||||
## 10. 遗留事项与联调确认
|
||||
|
||||
1. **A103-4 勾稽键语义待联调确认**:以 `sourcebillid = 付款申请单星瀚id`、`sourcebillnumber = 付款申请单编号(billno)` 假设实现,联调时需用真实付款处理单验证;若金蝶侧源单指向不同,仅需调整 `matchPayBill` 匹配逻辑。
|
||||
2. **付款日期格式**:`paydate` 按前 10 位 yyyy-MM-dd 兼容解析,金蝶若返回纯时间戳需调整 `parseKingdeeDate`。
|
||||
3. **A103-2 分页翻页**:`awrp_src_code` 批量查询理论单页返回(批次 50 < 页大小 200),翻页保护仅防御异常场景。
|
||||
4. **后续环节待建设**:付款附件推送 A902(凭 `kingdee_bill_id`)、合同系统履约同步 C106-2、付款申请反审核异常回收(B901 解锁)。
|
||||
@@ -140,6 +140,12 @@ thirdParty:
|
||||
exratetable: ${BFI_PAY_APPLY_EXRATE_TABLE:01}
|
||||
# 外部单据类型
|
||||
srcType: ${BFI_PAY_APPLY_SRC_TYPE:0KzXs}
|
||||
# 金蝶付款结果定时回写(A103-2 付款申请单查询 / A103-4 付款处理查询)
|
||||
payResult:
|
||||
# 回写任务开关
|
||||
syncEnabled: ${BFI_PAY_RESULT_SYNC_ENABLED:true}
|
||||
# 调度 cron,默认每10分钟
|
||||
cron: ${BFI_PAY_RESULT_CRON:0 */10 * * * ?}
|
||||
|
||||
#百度OCR配置,API Key和Secret Key请通过环境变量注入
|
||||
baidu:
|
||||
@@ -153,6 +159,15 @@ baidu:
|
||||
token-refresh-advance: 1m
|
||||
|
||||
|
||||
#MinIO文件存储配置
|
||||
file:
|
||||
storage:
|
||||
minio:
|
||||
endpoint: http://8.148.239.157:9000
|
||||
access-key-id: transport
|
||||
access-key-secret: transport
|
||||
bucket-name: transport
|
||||
|
||||
powerjob:
|
||||
worker:
|
||||
enabled: false
|
||||
|
||||
@@ -112,6 +112,12 @@ thirdParty:
|
||||
exratetable: ${BFI_PAY_APPLY_EXRATE_TABLE:01}
|
||||
# 外部单据类型
|
||||
srcType: ${BFI_PAY_APPLY_SRC_TYPE:0KzXs}
|
||||
# 金蝶付款结果定时回写(A103-2 付款申请单查询 / A103-4 付款处理查询)
|
||||
payResult:
|
||||
# 回写任务开关
|
||||
syncEnabled: ${BFI_PAY_RESULT_SYNC_ENABLED:true}
|
||||
# 调度 cron,默认每10分钟
|
||||
cron: ${BFI_PAY_RESULT_CRON:0 */10 * * * ?}
|
||||
|
||||
#百度OCR配置,API Key和Secret Key请通过环境变量注入
|
||||
baidu:
|
||||
|
||||
@@ -43,3 +43,9 @@ thirdParty:
|
||||
exratetable: ${BFI_PAY_APPLY_EXRATE_TABLE:01}
|
||||
# 外部单据类型
|
||||
srcType: ${BFI_PAY_APPLY_SRC_TYPE:0KzXs}
|
||||
# 金蝶付款结果定时回写(A103-2 付款申请单查询 / A103-4 付款处理查询)
|
||||
payResult:
|
||||
# 回写任务开关
|
||||
syncEnabled: ${BFI_PAY_RESULT_SYNC_ENABLED:true}
|
||||
# 调度 cron,默认每10分钟
|
||||
cron: ${BFI_PAY_RESULT_CRON:0 */10 * * * ?}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
-- 付款管理:金蝶付款结果定时回写(A103-2 付款申请单查询 / A103-4 付款处理查询)
|
||||
-- 新增 kingdee_bill_id:保存 A103-1/A103-3 推送返回的星瀚单据内部主键(A902 附件上传等使用);
|
||||
-- kingdee_bill_no 字段语义调整为保存金蝶单据编号(billno,如 FKSQ-xxxx),由定时回写任务回填
|
||||
|
||||
-- 以下结构升级支持重复执行
|
||||
SET @ddl_sql = (
|
||||
SELECT IF(COUNT(*) = 0,
|
||||
'ALTER TABLE `blade_payment_application` ADD COLUMN `kingdee_bill_id` varchar(32) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT ''金蝶单据id(星瀚内部主键)'' AFTER `kingdee_bill_no`',
|
||||
'SELECT 1')
|
||||
FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'blade_payment_application'
|
||||
AND COLUMN_NAME = 'kingdee_bill_id'
|
||||
);
|
||||
PREPARE ddl_stmt FROM @ddl_sql;
|
||||
EXECUTE ddl_stmt;
|
||||
DEALLOCATE PREPARE ddl_stmt;
|
||||
Reference in New Issue
Block a user