From 8c8a874522e4298017351cd3fa8eb84feb7f7a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B3=89=E4=BD=B3?= <1107186916@qq.com> Date: Tue, 20 Oct 2026 21:45:09 +0800 Subject: [PATCH] =?UTF-8?q?:feat:=20=E5=90=88=E5=90=8C=E5=AE=A1=E6=89=B9?= =?UTF-8?q?=E9=80=9A=E8=BF=87=E5=90=8C=E6=AD=A5=E6=8E=A8=E9=80=81HCS?= =?UTF-8?q?=E5=B9=B6=E5=9B=9E=E4=BC=A0=E5=A4=B1=E8=B4=A5=E5=8E=9F=E5=9B=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/ContractManageController.java | 11 +++- .../impl/ContractManageServiceImpl.java | 5 +- .../support/hcs/HcsImmediatePushSupport.java | 54 +++++++++++++++++++ 3 files changed, 65 insertions(+), 5 deletions(-) diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java index 3ff3eaf1..364dbcc9 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/controller/ContractManageController.java @@ -40,6 +40,7 @@ import org.springblade.transport.excel.ContractManageExcel; import org.springblade.transport.pojo.entity.ContractManage; import org.springblade.transport.pojo.vo.ContractManageVO; import org.springblade.transport.service.IContractManageService; +import org.springblade.transport.support.hcs.HcsImmediatePushSupport; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -63,6 +64,7 @@ import java.util.Map; public class ContractManageController extends BladeController { private final IContractManageService contractManageService; + private final HcsImmediatePushSupport hcsImmediatePushSupport; @GetMapping("/detail") @ApiOperationSupport(order = 1) @@ -110,7 +112,14 @@ public class ContractManageController extends BladeController { @ApiOperationSupport(order = 7) @Operation(summary = "审批通过", description = "传入id") public R approve(@Parameter(description = "主键", required = true) @RequestParam Long id) { - return R.status(contractManageService.approve(id)); + contractManageService.approve(id); + // 审批事务已提交,再同步推送合规;失败原因以警告文案返回(data=false 供前端区分提示样式),照正式结算单审批接口 + hcsImmediatePushSupport.pushContractAndWait(id); + String syncFailure = HcsImmediatePushSupport.pollSyncFailure(); + if (syncFailure != null) { + return R.data(Boolean.FALSE, "审批通过,但合规推送失败:" + syncFailure); + } + return R.success("审批通过"); } @PostMapping("/reject") diff --git a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java index 03581c51..5e8f3900 100644 --- a/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java +++ b/blade-service/blade-transport/src/main/java/org/springblade/transport/service/impl/ContractManageServiceImpl.java @@ -48,7 +48,6 @@ import org.springblade.transport.service.IContractExeStageService; import org.springblade.transport.service.IContractManageService; import org.springblade.transport.support.TransportBusinessSupport; import org.springblade.transport.support.hcs.HcsCreditCheckSupport; -import org.springblade.transport.support.hcs.HcsImmediatePushSupport; import org.springblade.transport.wrapper.ContractManageWrapper; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -79,7 +78,6 @@ import java.util.regex.Pattern; public class ContractManageServiceImpl extends BaseServiceImpl implements IContractManageService { private final HcsContractExeStageMapper hcsContractExeStageMapper; - private final HcsImmediatePushSupport hcsImmediatePushSupport; private final HcsCreditCheckSupport hcsCreditCheckSupport; private final IContractExeStageService contractExeStageService; @@ -241,7 +239,7 @@ public class ContractManageServiceImpl extends BaseServiceImpl + * 例外:TMS 合同/项目立项的"通过"按钮路径由 Controller 在审批事务返回后分别调用 + * {@link #pushContractAndWait(Long)}、{@link #pushProjectAndWait(Long)} 同步推送 + * (审批响应会等待附件转传 + HCS 调用完成,与正式结算单审批一致), + * 失败原因经 {@link #pollSyncFailure()} 交给 Controller 拼进审批响应(照 KingdeeApprovalSyncListener); + * MK 回调路径仍异步,避免阻塞第三方回调。 * * @author Chill */ @@ -77,6 +83,11 @@ public class HcsImmediatePushSupport { */ private static final AtomicInteger THREAD_COUNTER = new AtomicInteger(1); + /** + * 当前审批请求线程的合规推送失败原因,供审批 Controller 取走提示前端 + */ + private static final ThreadLocal SYNC_FAILURE = new ThreadLocal<>(); + private final HcsProperties hcsProperties; private final HcsProjectSyncSupport hcsProjectSyncSupport; private final HcsContractSyncSupport hcsContractSyncSupport; @@ -111,6 +122,39 @@ public class HcsImmediatePushSupport { this.submitAfterCommit(HcsSyncConstant.BIZ_TYPE_CONTRACT, id, () -> this.pushContractNow(id)); } + /** + * 合同"通过"按钮路径:在审批事务提交返回后由 Controller 同步调用,失败原因留在当前线程供其取走 + * + * @param id 合同单ID + */ + public void pushContractAndWait(Long id) { + if (!hcsProperties.getPush().isImmediateEnabled()) { + return; + } + this.pushWithLock(HcsSyncConstant.BIZ_TYPE_CONTRACT, id, () -> this.pushContractNow(id)); + } + + /** + * 项目立项"通过"按钮路径:在审批事务提交返回后由 Controller 同步调用,失败原因留在当前线程供其取走 + * + * @param id 项目立项单ID + */ + public void pushProjectAndWait(Long id) { + if (!hcsProperties.getPush().isImmediateEnabled()) { + return; + } + this.pushWithLock(HcsSyncConstant.BIZ_TYPE_PROJECT, id, () -> this.pushProjectNow(id)); + } + + /** + * 取走并清除当前审批请求的合规推送失败原因;null 表示推送成功、未触发或仍异步进行 + */ + public static String pollSyncFailure() { + String failure = SYNC_FAILURE.get(); + SYNC_FAILURE.remove(); + return failure; + } + /** * 开关校验后注册提交时机:有活动事务注册 afterCommit 回调(回滚不推送),无事务(MK 回调单条更新自动提交)直接异步 */ @@ -137,6 +181,8 @@ public class HcsImmediatePushSupport { private void pushWithLock(String bizType, Long id, Runnable pushAction) { String lockKey = HcsSyncConstant.PUSH_SINGLE_LOCK_KEY_PREFIX + bizType + ":" + id; String lockHolder = UUID.randomUUID().toString(); + // 清理同线程残留的失败原因,避免线程复用串单(同步推送路径会写入) + SYNC_FAILURE.remove(); Boolean locked = bladeRedis.getValueOps() .setIfAbsent(lockKey, lockHolder, Duration.ofSeconds(HcsSyncConstant.PUSH_SINGLE_LOCK_TTL_SECONDS)); if (!Boolean.TRUE.equals(locked)) { @@ -165,6 +211,10 @@ public class HcsImmediatePushSupport { hcsProjectSyncSupport.repushProject(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); } } @@ -185,6 +235,10 @@ public class HcsImmediatePushSupport { hcsContractSyncSupport.repushContract(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); } }