From 0f9ad090db4603e8a504d9f1ff72c6d99ac610f1 Mon Sep 17 00:00:00 2001 From: glx <783262171@qq.com> Date: Mon, 15 Jun 2026 18:07:05 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AE=A1=E6=A0=B8=E5=90=8E?= =?UTF-8?q?=E4=B8=9A=E5=8A=A1=E5=9B=9E=E8=B0=83=E4=BA=8B=E5=8A=A1=E8=8C=83?= =?UTF-8?q?=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../admin/model/entity/MiniContentAudit.java | 4 ++ .../admin/service/ContentAuditService.java | 13 ++++ .../impl/AuditExecutorServiceImpl.java | 46 ++++++++++++-- .../impl/BizAuditStatusHandlerImpl.java | 3 - .../service/impl/ContentAuditServiceImpl.java | 62 +++++++++++++++---- 5 files changed, 109 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/youlai/boot/admin/model/entity/MiniContentAudit.java b/src/main/java/com/youlai/boot/admin/model/entity/MiniContentAudit.java index 49ebcd5..98cf982 100644 --- a/src/main/java/com/youlai/boot/admin/model/entity/MiniContentAudit.java +++ b/src/main/java/com/youlai/boot/admin/model/entity/MiniContentAudit.java @@ -87,5 +87,9 @@ public class MiniContentAudit implements Serializable { @Schema(description = "触发来源:create发布/report举报") private String triggerType; + @TableField("callback_status") + @Schema(description = "审核后业务回调状态:pending-待定,done-完成") + private String callbackStatus; + } diff --git a/src/main/java/com/youlai/boot/admin/service/ContentAuditService.java b/src/main/java/com/youlai/boot/admin/service/ContentAuditService.java index bfd4877..b5c4c6d 100644 --- a/src/main/java/com/youlai/boot/admin/service/ContentAuditService.java +++ b/src/main/java/com/youlai/boot/admin/service/ContentAuditService.java @@ -11,4 +11,17 @@ public interface ContentAuditService extends IService { MiniContentAudit getByModuleAndBizId(String moduleCode, Long bizId); + /** + * 执行业务回调(审核通过/不通过时同步更新业务实体状态)。 + * 在轮询场景中直接调用,在同步审核场景中由 executeAudit 外层事务调用。 + */ + void executeCallback(Long auditId); + + /** + * 重试所有 callback_status = 'pending' 的回调。 + * + * @return 成功执行的数量 + */ + int retryPendingCallbacks(); + } diff --git a/src/main/java/com/youlai/boot/admin/service/impl/AuditExecutorServiceImpl.java b/src/main/java/com/youlai/boot/admin/service/impl/AuditExecutorServiceImpl.java index 3385425..d0d6cf2 100644 --- a/src/main/java/com/youlai/boot/admin/service/impl/AuditExecutorServiceImpl.java +++ b/src/main/java/com/youlai/boot/admin/service/impl/AuditExecutorServiceImpl.java @@ -16,8 +16,9 @@ import com.youlai.boot.common.util.AliyunContentAuditUtil; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.support.TransactionTemplate; import java.lang.reflect.Method; import java.util.*; @@ -38,6 +39,7 @@ public class AuditExecutorServiceImpl implements AuditExecutorService { private final ContentAuditService contentAuditService; private final ContentAuditTaskService contentAuditTaskService; private final AliyunContentAuditUtil aliyunContentAuditUtil; + private final PlatformTransactionManager transactionManager; /** * 执行内容审核(供业务层在内容创建/修改后调用)。 @@ -48,8 +50,32 @@ public class AuditExecutorServiceImpl implements AuditExecutorService { * @return {status, auditId};配置关闭或无配置时返回 null */ @Override - @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class) public Map executeAudit(String moduleCode, Long bizId, AuditContentDTO content, String triggerType) { + // 1) 审核核心逻辑在独立事务中运行,异常不影响业务主事务 + TransactionTemplate txTemplate = new TransactionTemplate(transactionManager); + txTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); + Map result = txTemplate.execute(status -> doExecuteAudit(moduleCode, bizId, content, triggerType)); + + // 2) 业务回调在当前事务中执行,避免跨事务锁冲突 + if (result != null && result.get("auditId") != null) { + String auditStatus = (String) result.get("status"); + if (!AuditConstants.STATUS_REVIEWING.equals(auditStatus)) { + try { + contentAuditService.executeCallback((Long) result.get("auditId")); + } catch (Exception e) { + log.error("业务回调失败, auditId={}, status={}", result.get("auditId"), auditStatus, e); + } + } + } + + return result; + } + + /** + * 审核核心逻辑(在独立事务中运行)。 + * 包含:查配置 → 创建审核记录 → 创建任务 → 调用阿里云机审 → 汇总结果 → 更新审核状态 + */ + private Map doExecuteAudit(String moduleCode, Long bizId, AuditContentDTO content, String triggerType) { // 1) 查询审核配置 MiniContentAuditConfig config = findAuditConfig(moduleCode); if (config == null || isAuditDisabled(config)) { @@ -68,7 +94,6 @@ public class AuditExecutorServiceImpl implements AuditExecutorService { // 4) 按审核类型执行 if (AuditConstants.AUDIT_TYPE_MANUAL.equals(auditType)) { - // 人工审核 return executeManualAudit(auditId); } @@ -501,13 +526,24 @@ public class AuditExecutorServiceImpl implements AuditExecutorService { MiniContentAudit audit = contentAuditService.getById(auditId); if (audit != null && AuditConstants.AUDIT_TYPE_MACHINE.equals(audit.getAuditType())) { List tasks = contentAuditTaskService.listTasksByAuditId(auditId); - aggregateTaskResultsAndUpdateAudit(auditId, tasks); + Map result = aggregateTaskResultsAndUpdateAudit(auditId, tasks); + if (result != null) { + String status = (String) result.get("status"); + if (!AuditConstants.STATUS_REVIEWING.equals(status)) { + contentAuditService.executeCallback(auditId); + } + } } } catch (Exception e) { log.error("重新汇总审核失败, auditId={}", auditId, e); } } + int retried = contentAuditService.retryPendingCallbacks(); + if (retried > 0) { + log.info("重试pending业务回调完成, 成功={}", retried); + } + log.info("异步审核轮询完成, 已处理={}, 受影响审核={}", processedCount, affectedAuditIds.size()); return processedCount; } diff --git a/src/main/java/com/youlai/boot/admin/service/impl/BizAuditStatusHandlerImpl.java b/src/main/java/com/youlai/boot/admin/service/impl/BizAuditStatusHandlerImpl.java index da22065..7915b12 100644 --- a/src/main/java/com/youlai/boot/admin/service/impl/BizAuditStatusHandlerImpl.java +++ b/src/main/java/com/youlai/boot/admin/service/impl/BizAuditStatusHandlerImpl.java @@ -11,7 +11,6 @@ import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; import java.util.Date; @@ -38,7 +37,6 @@ public class BizAuditStatusHandlerImpl implements BizAuditStatusHandler { private String bucketName; @Override - @Transactional(rollbackFor = Exception.class) public void onAuditPassed(String moduleCode, Long bizId) { log.info("审核通过, moduleCode={}, bizId={}", moduleCode, bizId); switch (moduleCode) { @@ -51,7 +49,6 @@ public class BizAuditStatusHandlerImpl implements BizAuditStatusHandler { } @Override - @Transactional(rollbackFor = Exception.class) public void onAuditRejected(String moduleCode, Long bizId) { log.info("审核不通过, moduleCode={}, bizId={}", moduleCode, bizId); switch (moduleCode) { diff --git a/src/main/java/com/youlai/boot/admin/service/impl/ContentAuditServiceImpl.java b/src/main/java/com/youlai/boot/admin/service/impl/ContentAuditServiceImpl.java index 65edbae..2bfb2a9 100644 --- a/src/main/java/com/youlai/boot/admin/service/impl/ContentAuditServiceImpl.java +++ b/src/main/java/com/youlai/boot/admin/service/impl/ContentAuditServiceImpl.java @@ -13,6 +13,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.Date; +import java.util.List; @Slf4j @Service @@ -48,23 +49,62 @@ public class ContentAuditServiceImpl extends ServiceImpl pendingList = this.list(new LambdaQueryWrapper() + .eq(MiniContentAudit::getCallbackStatus, "pending") + .in(MiniContentAudit::getStatus, AuditConstants.STATUS_PASSED, AuditConstants.STATUS_REJECTED)); + if (pendingList.isEmpty()) { + return 0; + } + log.info("开始重试待回调审核, 数量={}", pendingList.size()); + int doneCount = 0; + for (MiniContentAudit audit : pendingList) { try { - MiniContentAudit audit = this.getById(auditId); - if (audit != null) { - if (AuditConstants.STATUS_PASSED.equals(status)) { - bizAuditStatusHandler.onAuditPassed(audit.getModuleCode(), audit.getBizId()); - } else { - bizAuditStatusHandler.onAuditRejected(audit.getModuleCode(), audit.getBizId()); - } - } + executeCallback(audit.getId()); + doneCount++; } catch (Exception e) { - log.error("回调业务审核状态处理器失败, auditId={}, status={}", auditId, status, e); + log.error("重试回调失败, auditId={}", audit.getId(), e); } } + log.info("重试待回调审核完成, 成功={}, 总数={}", doneCount, pendingList.size()); + return doneCount; } @Override