feat: 新增付款申请审批通过即时发起特批能力

HcsImmediatePushSupport 增加付款即时推送入口,复用 repushPayment 发起 C106-3;
按“审批即发起”要求不受 immediate-enabled 开关控制,失败留痕由扫描或手动重推兜底。
This commit is contained in:
刘泉佳
2026-10-23 17:22:21 +08:00
parent 3ddf044810
commit 1b7b8ae571
@@ -32,8 +32,10 @@ import org.springblade.core.redis.cache.BladeRedis;
import org.springblade.hcs.config.HcsProperties;
import org.springblade.transport.constant.HcsSyncConstant;
import org.springblade.transport.mapper.ContractManageMapper;
import org.springblade.transport.mapper.PaymentApplicationMapper;
import org.springblade.transport.mapper.ProjectApplyMapper;
import org.springblade.transport.pojo.entity.ContractManage;
import org.springblade.transport.pojo.entity.PaymentApplication;
import org.springblade.transport.pojo.entity.ProjectApply;
import org.springframework.stereotype.Component;
import org.springframework.transaction.support.TransactionSynchronization;
@@ -50,8 +52,9 @@ import java.util.concurrent.atomic.AtomicInteger;
/**
* HCS 审批通过即时推送(前端接入设计 §6,2026-10-10 增补)
* <p>
* 项目立项(C102)/合同(C103)审批通过(含变更通过)事务提交后立即异步触发单条推送,不再等待扫描周期;
* 执行体复用 {@link HcsProjectSyncSupport#repushProject(Long)} / {@link HcsContractSyncSupport#repushContract(Long)}
* 项目立项(C102)/合同(C103)/付款申请(C106 付款特批)审批通过(含变更通过)事务提交后立即触发单条推送,不再等待扫描周期;
* 执行体复用 {@link HcsProjectSyncSupport#repushProject(Long)} / {@link HcsContractSyncSupport#repushContract(Long)} /
* {@link HcsPaymentSyncSupport#repushPayment(Long)}
* (已含审批状态校验、失败 markFailed、高风险特批挂起判断),失败仅留痕,由 HcsPushTask 扫描兜底补偿
* (ADR-0001 修订:审批代码埋点为一行触发调用,推送逻辑仍收敛在 hcs support 包)。
* <p>
@@ -65,6 +68,10 @@ import java.util.concurrent.atomic.AtomicInteger;
* (审批响应会等待附件转传 + HCS 调用完成,与正式结算单审批一致),
* 失败原因经 {@link #pollSyncFailure()} 交给 Controller 拼进审批响应(照 KingdeeApprovalSyncListener);
* MK 回调路径仍异步,避免阻塞第三方回调。
* <p>
* 付款申请按"审批即发起特批"要求:即时推送不受 thirdParty.hcs.push.immediate-enabled 开关控制
* ({@link #pushPaymentAndWait(Long)} / {@link #pushPaymentAfterCommit(Long)}),
* 审批通过即以 C106-3 发起特批,履约计划与履约记录等特批通过后补推。
*
* @author Chill
*/
@@ -91,8 +98,10 @@ public class HcsImmediatePushSupport {
private final HcsProperties hcsProperties;
private final HcsProjectSyncSupport hcsProjectSyncSupport;
private final HcsContractSyncSupport hcsContractSyncSupport;
private final HcsPaymentSyncSupport hcsPaymentSyncSupport;
private final ProjectApplyMapper projectApplyMapper;
private final ContractManageMapper contractManageMapper;
private final PaymentApplicationMapper paymentApplicationMapper;
private final BladeRedis bladeRedis;
/**
@@ -110,7 +119,7 @@ public class HcsImmediatePushSupport {
* @param id 项目立项单ID
*/
public void pushProjectAfterCommit(Long id) {
this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_PROJECT, id, () -> this.pushProjectNow(id));
this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_PROJECT, id, () -> this.pushProjectNow(id), true);
}
/**
@@ -119,7 +128,17 @@ public class HcsImmediatePushSupport {
* @param id 合同单ID
*/
public void pushContractAfterCommit(Long id) {
this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_CONTRACT, id, () -> this.pushContractNow(id));
this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_CONTRACT, id, () -> this.pushContractNow(id), true);
}
/**
* 付款申请审批通过后即时发起付款特批(C106-3,无需特批则直接推履约数据):
* 须在审批事务内调用(afterCommit 触发);按"审批即发起特批"要求不受 immediate-enabled 开关控制
*
* @param id 付款申请单ID
*/
public void pushPaymentAfterCommit(Long id) {
this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_PAYMENT, id, () -> this.pushPaymentNow(id), false);
}
/**
@@ -146,6 +165,16 @@ public class HcsImmediatePushSupport {
this.pushWithLock(HcsSyncConstant.BIZ_TYPE_PROJECT, id, () -> this.pushProjectNow(id));
}
/**
* 付款申请"通过"按钮路径:在审批事务提交返回后由 Controller 同步调用,失败原因留在当前线程供其取走;
* 按"审批即发起特批"要求不受 immediate-enabled 开关控制
*
* @param id 付款申请单ID
*/
public void pushPaymentAndWait(Long id) {
this.pushWithLock(HcsSyncConstant.BIZ_TYPE_PAYMENT, id, () -> this.pushPaymentNow(id));
}
/**
* 取走并清除当前审批请求的合规推送失败原因;null 表示推送成功、未触发或仍异步进行
*/
@@ -156,10 +185,12 @@ public class HcsImmediatePushSupport {
}
/**
* 开关校验后注册提交时机:有活动事务注册 afterCommit 回调(回滚不推送),无事务(MK 回调单条更新自动提交)直接异步
* 提交时机注册:有活动事务注册 afterCommit 回调(回滚不推送),无事务(MK 回调单条更新自动提交)直接异步
*
* @param requireSwitch 是否受 immediate-enabled 开关控制(项目/合同是,付款特批按"审批即发起"否)
*/
private void submitAfterCommit(String bizType, Long id, Runnable pushAction) {
if (!hcsProperties.getPush().isImmediateEnabled()) {
private void submitAfterCommit(String bizType, Long id, Runnable pushAction, boolean requireSwitch) {
if (requireSwitch && !hcsProperties.getPush().isImmediateEnabled()) {
return;
}
Runnable pushTask = () -> this.pushWithLock(bizType, id, pushAction);
@@ -242,6 +273,26 @@ public class HcsImmediatePushSupport {
}
}
/**
* 付款推送执行体:非审批通过/已同步短路跳过;失败已由 Support markFailed,仅留痕交扫描/手动重推补偿
*/
private void pushPaymentNow(Long id) {
try {
PaymentApplication payment = paymentApplicationMapper.selectById(id);
if (payment == null || !APPROVED_STATUSES.contains(payment.getApprovalStatus())
|| HcsSyncConstant.HCS_STATUS_SYNCED.equals(payment.getHcsSyncStatus())) {
return;
}
hcsPaymentSyncSupport.repushPayment(id);
} catch (Exception exception) {
log.error("HCS付款即时推送失败(可手动重推或扫描任务补偿):id={},{}", id, exception.getMessage(), exception);
// 失败原因留在当前线程供审批 Controller 取走;异步路径无人取走,仅影响线程池自身
String reason = exception.getMessage() == null ? "合规接口调用异常" : exception.getMessage();
SYNC_FAILURE.set(reason.startsWith("HCS付款申请推送失败:")
? reason.substring("HCS付款申请推送失败:".length()) : reason);
}
}
@PreDestroy
public void shutdown() {
pushExecutor.shutdown();