:feat: 合同审批通过同步推送HCS并回传失败原因
This commit is contained in:
+10
-1
@@ -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")
|
||||
|
||||
+1
-4
@@ -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<ContractManageMapper, ContractManage> 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<ContractManageMap
|
||||
contractManage.setContractFileJson(markContractFilesApproved(contractManage.getContractFileJson()));
|
||||
refreshArchiveStatusOnApprove(contractManage);
|
||||
updateLatestReviewingChangeRecord(contractManage, contractManage.getApprovalStatus(), "审核通过");
|
||||
// HCS 推送:审批通过(含变更通过)置待同步状态,事务提交后即时推送,扫描任务保留兜底(ADR-0001 修订,前端接入设计 §6)
|
||||
// HCS 推送:审批通过(含变更通过)置待同步状态,事务提交后由 Controller 同步推送并回传失败原因,扫描任务保留兜底
|
||||
contractManage.setHcsSyncStatus(HcsSyncConstant.HCS_STATUS_UNSYNCED);
|
||||
// 合同审批通过:履约规则未同步行统一置待同步标记,随合同链路调 C103-9(ADR-0002)
|
||||
// 审批通过自动创建默认履约计划行(承运商=付款进度款/客户=收款进度款),已有规则行时不重复创建
|
||||
@@ -250,7 +248,6 @@ public class ContractManageServiceImpl extends BaseServiceImpl<ContractManageMap
|
||||
.eq(HcsContractExeStage::getContractId, contractManage.getId())
|
||||
.isNull(HcsContractExeStage::getHcsStageFdId)
|
||||
.set(HcsContractExeStage::getRowOp, "add"));
|
||||
hcsImmediatePushSupport.pushContractAfterCommit(id);
|
||||
return updateById(contractManage);
|
||||
}
|
||||
|
||||
|
||||
+54
@@ -59,6 +59,12 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* 自建 2 线程 daemon 池(工程未启用 @EnableAsync),HCS 推送可达 20s+ 不阻塞审批响应;
|
||||
* Redis 单条锁防与扫描任务并发双推(TTL 120s,持有值比对释放,照 HcsPushTask 惯例),
|
||||
* 极端双推由 HCS 侧判重/更新幂等吸收。与扫描任务同线程模型(无租户上下文),行为一致。
|
||||
* <p>
|
||||
* 例外: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<String> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user