diff --git a/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSyncResultRequest.java b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSyncResultRequest.java new file mode 100644 index 00000000..9112a6c4 --- /dev/null +++ b/blade-service-api/blade-transport-api/src/main/java/org/springblade/transport/pojo/dto/PaymentApplicationSyncResultRequest.java @@ -0,0 +1,34 @@ +/** + * BladeX Commercial License Agreement + * Copyright (c) 2018-2099, https://bladex.cn. All rights reserved. + *

+ * Use of this software is governed by the Commercial License Agreement obtained after purchasing a license from BladeX. + *

+ * 1. This software is for development use only under a valid license from BladeX. + *

+ * 2. Redistribution of this software's source code to any third party without a commercial license is strictly prohibited. + *

+ * 3. Licensees may copyright their own code but cannot use segments from this software for such purposes. + * Copyright of this software remains with BladeX. + *

+ * Using this software signifies agreement to this License, and the software must not be used for illegal purposes. + *

+ * THIS SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY. The author is not liable for any claims arising from secondary or illegal development. + *

+ * Author: Chill Zhuang (bladejava@qq.com) + */ +package org.springblade.transport.pojo.dto; + +import lombok.Data; + +import java.io.Serial; +import java.io.Serializable; +import java.util.List; + +/** 付款申请手动回写金蝶付款结果请求。 @author Chill */ +@Data +public class PaymentApplicationSyncResultRequest implements Serializable { + @Serial private static final long serialVersionUID = 1L; + /** 付款单据号列表,为空回写全部待回写单据 */ + private List paymentNos; +} diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java index f2003c5f..dad47f59 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/PaymentApplicationController.java @@ -33,6 +33,7 @@ import org.springblade.transport.constant.HcsSyncConstant; import org.springblade.transport.listener.KingdeeApprovalSyncListener; import org.springblade.transport.pojo.dto.PaymentApplicationSaveRequest; import org.springblade.transport.pojo.dto.PaymentApplicationStatusRequest; +import org.springblade.transport.pojo.dto.PaymentApplicationSyncResultRequest; import org.springblade.transport.pojo.entity.PaymentApplication; import org.springblade.transport.pojo.vo.PaymentApplicationReferenceAmountVO; import org.springblade.transport.pojo.vo.PaymentApplicationVO; @@ -142,6 +143,8 @@ public class PaymentApplicationController extends BladeController { } @PostMapping("/sync-kingdee-result") @ApiOperationSupport(order = 12) - @Operation(summary = "手动回写金蝶付款结果(A103-2/A103-4)") - public R syncKingdeeResult() { return R.data(paymentApplicationService.syncKingdeePaymentResult()); } + @Operation(summary = "手动回写金蝶付款结果(A103-2/A103-4),可指定付款单据号") + public R syncKingdeeResult(@RequestBody(required = false) PaymentApplicationSyncResultRequest request) { + return R.data(paymentApplicationService.syncKingdeePaymentResult(request == null ? null : request.getPaymentNos())); + } } diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java index c55c874f..5824514a 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/IPaymentApplicationService.java @@ -101,11 +101,13 @@ public interface IPaymentApplicationService extends BaseService paymentNos); /** * 回写单笔付款申请的金蝶付款申请单查询结果(A103-2),单据级事务 diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java index 36354a9a..4c4b0e0f 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/PaymentApplicationServiceImpl.java @@ -949,33 +949,41 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl paymentNos) { return kingdeeSyncLockSupport.runSingleFlight(PAY_RESULT_SYNC_LOCK_PREFIX, bfiProperties.getPayResult().getLockLeaseSeconds(), - "金蝶付款结果回写", this::executePaymentResultSync); + "金蝶付款结果回写", () -> this.executePaymentResultSync(paymentNos)); } /** - * 金蝶付款结果回写主体,由 {@link #syncKingdeePaymentResult()} 在分布式锁保护下调用,执行流程: + * 金蝶付款结果回写主体,由 {@link #syncKingdeePaymentResult(List)} 在分布式锁保护下调用,执行流程: *

 	 * ① 圈定待回写付款申请:审批通过且金蝶状态为 已生成/付款中/生成失败(终态已付款/已关闭不再轮询),
-	 *    生成失败单据一并纳入:金蝶侧已生成说明上次推送实际成功(如下推超时但远端已落单、本地事务回滚),
+	 *    手动指定付款单据号(paymentNos)时仅回写指定单据;生成失败单据一并纳入:
+	 *    金蝶侧已生成说明上次推送实际成功(如下推超时但远端已落单、本地事务回滚),
 	 *    查得即回填纠正为已生成,避免人工重推产生重复单据,未查得则保持生成失败等待人工重推;
-	 * ② A103-2 付款申请单查询回填:按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改一条,
+	 * ② A103-2 付款申请单查询回填:指定单据号时按金蝶单据号(billno)精确查询(无金蝶单据号兜底按来源单号),
+	 *    定时全量回写按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改一条,
 	 *    回填金蝶单号/内码/同步状态/已付金额并联动预结算与正式结算付款记录;连续两轮查询未命中按金蝶反审核删除解锁;
 	 * ③ A103-4 付款处理查询补付款记录:付款中/已付款单据 + 已付款无付款记录单据(补偿),
 	 *    按付款组织 + awrp_sourcebillno(金蝶单据号)数组精确查询,双键勾稽后回填付款记录;
 	 * ④ 汇总执行摘要:各计数随流程累计,单笔失败仅计数不中断整轮
 	 * 
* + * @param paymentNos 付款单据号列表,为空回写全部待回写单据 * @return 执行摘要 */ - private String executePaymentResultSync() { - // ① 待回写付款申请:审批通过且金蝶状态为 已生成/付款中/生成失败(终态已付款/已关闭不再轮询) + private String executePaymentResultSync(List paymentNos) { + // ① 待回写付款申请:审批通过且金蝶状态为 已生成/付款中/生成失败(终态已付款/已关闭不再轮询), + // 手动指定付款单据号时仅圈定指定单据 List applications = list(Wrappers.lambdaQuery() .eq(PaymentApplication::getApprovalStatus, TransportBusiConstant.APPROVED) .in(PaymentApplication::getKingdeeStatus, KingdeeBfiConstant.KINGDEE_STATUS_SYNCED, KingdeeBfiConstant.KINGDEE_STATUS_PAYING, KingdeeBfiConstant.KINGDEE_STATUS_FAILED) + .in(Func.isNotEmpty(paymentNos), PaymentApplication::getPaymentNo, paymentNos) .orderByAsc(PaymentApplication::getId)); + if (Func.isNotEmpty(paymentNos)) { + log.info("手动回写金蝶付款结果, 指定付款单据号: {}, 圈定待回写单据 {} 笔", paymentNos, applications.size()); + } // 自代理:applyPayApplyResult/applyPayBillRecord 带单据级事务,跨方法调用确保注解生效,单笔失败仅回滚自身 IPaymentApplicationService self = SpringUtil.getBean(IPaymentApplicationService.class); int updated = 0; @@ -983,11 +991,17 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl payApplyMap = new HashMap<>(); if (!applications.isEmpty()) { - // ② A103-2 付款申请单查询:按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改的一条 - List paymentNos = applications.stream() + // ② A103-2 付款申请单查询:指定单据号时按金蝶单据号(billno)精确查询,无金蝶单据号的兜底按来源单号查询; + // 定时全量回写按 paymentNo(awrp_src_code)分批查询,同一来源单号取最近修改的一条 + List billNos = applications.stream() + .map(PaymentApplication::getKingdeeBillNo).filter(Func::isNotEmpty).distinct().toList(); + List fallbackSrcCodes = applications.stream() + .filter(application -> Func.isEmpty(application.getKingdeeBillNo())) .map(PaymentApplication::getPaymentNo).filter(Func::isNotEmpty).distinct().toList(); - this.batchForeach(paymentNos, PAY_RESULT_QUERY_BATCH, batch -> - payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPages(batch)))); + this.batchForeach(billNos, PAY_RESULT_QUERY_BATCH, + batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPagesByBillNo(batch)))); + this.batchForeach(fallbackSrcCodes, PAY_RESULT_QUERY_BATCH, + batch -> payApplyMap.putAll(this.latestPayApplyBySrcCode(this.queryPayApplyPages(batch)))); // 逐单回写:查得即回填并清零消失计数;未查得累计消失轮次(批次其余单据正常返回,排除服务整体故障) for (PaymentApplication application : applications) { BfiPayApplyVO payApply = payApplyMap.get(application.getPaymentNo()); @@ -1195,6 +1209,23 @@ public class PaymentApplicationServiceImpl extends BaseServiceImpl queryPayApplyPagesByBillNo(List billNos) { + BfiPayApplyQueryDTO param = new BfiPayApplyQueryDTO(); + param.setBillno(billNos); + return BfiUtil.queryAllPages(pageNo -> { + param.setPageNo(pageNo); + param.setPageSize(PAY_RESULT_PAGE_SIZE); + return bfiService.queryPayApply(param); + }); + } + /** * A103-2 幂等预检:按来源单号查询金蝶付款申请单是否已生成,同一来源单号取最近修改的一条。 * 用于正式付款链路恢复场景:财务应付单已生成说明上次同步可能已保存成功而在下推/落库环节中断, diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/task/PaymentKingdeeResultTask.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/task/PaymentKingdeeResultTask.java index e5e4fb87..9d1a39d5 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/task/PaymentKingdeeResultTask.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/task/PaymentKingdeeResultTask.java @@ -67,7 +67,7 @@ public class PaymentKingdeeResultTask { } // ① A103-2/A103-4 付款结果回写 try { - String summary = paymentApplicationService.syncKingdeePaymentResult(); + String summary = paymentApplicationService.syncKingdeePaymentResult(null); log.info("定时回写金蝶付款结果:{}", summary); } catch (Exception exception) { log.error("定时回写金蝶付款结果异常", exception); diff --git a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/feign/IBfiClient.java b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/feign/IBfiClient.java index e16bc3df..acd4c40e 100644 --- a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/feign/IBfiClient.java +++ b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/feign/IBfiClient.java @@ -188,6 +188,7 @@ public interface IBfiClient { * @param pageSize 页数 * @param awrpSrcCode 来源单据号 * @param awrpSrcSys 系统编号 + * @param billno 金蝶单据号,按单据号精确查询时不传来源单据号 * @param modifytimeS 修改时间-开始 * @param modifytimeE 修改时间-结束 * @return 付款申请单分页数据 @@ -198,6 +199,7 @@ public interface IBfiClient { @RequestParam("pageSize") Integer pageSize, @RequestParam(value = "awrp_src_code", required = false) List awrpSrcCode, @RequestParam(value = "awrp_src_sys", required = false) String awrpSrcSys, + @RequestParam(value = "billno", required = false) List billno, @RequestParam(value = "modifytimeS", required = false) String modifytimeS, @RequestParam(value = "modifytimeE", required = false) String modifytimeE); diff --git a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/pojo/dto/BfiPayApplyQueryDTO.java b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/pojo/dto/BfiPayApplyQueryDTO.java index a56e7766..72c2938b 100644 --- a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/pojo/dto/BfiPayApplyQueryDTO.java +++ b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/pojo/dto/BfiPayApplyQueryDTO.java @@ -54,6 +54,10 @@ public class BfiPayApplyQueryDTO implements Serializable { * 来源单据号,业务系统对应的单据号,高频查询需要推送该字段 */ private List awrpSrcCode; + /** + * 金蝶单据号,按单据号精确查询时不传来源单据号(awrp_src_code) + */ + private List billno; /** * 系统编号 */ diff --git a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/service/impl/BfiServiceImpl.java b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/service/impl/BfiServiceImpl.java index bb6e2c1c..8acc0a34 100644 --- a/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/service/impl/BfiServiceImpl.java +++ b/blade-third-party-api/blade-bfi-api/src/main/java/org/springblade/thirdparty/bfi/service/impl/BfiServiceImpl.java @@ -270,7 +270,8 @@ public class BfiServiceImpl implements IBfiService { } String accessToken = getAccessToken(); BfiResultVO> result = commonCall("付款申请单查询", () -> client.payApplyQuery(accessToken, - param.getPageNo(), param.getPageSize(), param.getAwrpSrcCode(), param.getAwrpSrcSys(), param.getModifytimeS(), param.getModifytimeE())); + param.getPageNo(), param.getPageSize(), param.getAwrpSrcCode(), param.getAwrpSrcSys(), param.getBillno(), + param.getModifytimeS(), param.getModifytimeE())); return result.getData(); }