fix(ops): 修复巡检异步处理的 @Async 自调用和事务可见性问题
Code review 发现两个关键缺陷: 1. @Async 自调用:triggerAttributionAsync() 在同一类内调用, Spring AOP 代理不生效,实际同步执行 2. 事务可见性:异步任务可能在事务提交前读取未持久化数据 修复方案: - 提取 InspectionAsyncHandler(独立 @Component),@Async 通过代理生效 - 使用 TransactionSynchronizationManager.afterCommit() 确保事务提交后触发 - 修复 InspectionRectificationServiceImpl 的 null 安全问题 - 修复 InspectionTemplateServiceImpl 更新路径缺少 id 空校验 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
package com.viewsh.module.ops.environment.service.inspection;
|
||||
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 巡检异步处理器(独立 Bean,解决 @Async 自调用问题)
|
||||
*
|
||||
* 负责在事务提交后异步执行:
|
||||
* 1. 归属判定
|
||||
* 2. 整改工单创建
|
||||
*/
|
||||
@Component
|
||||
@Slf4j
|
||||
public class InspectionAsyncHandler {
|
||||
|
||||
@Resource
|
||||
private InspectionAttributionService inspectionAttributionService;
|
||||
|
||||
@Resource
|
||||
private InspectionRectificationService inspectionRectificationService;
|
||||
|
||||
/**
|
||||
* 异步处理不合格巡检的后续逻辑
|
||||
*/
|
||||
@Async
|
||||
public void handleFailedInspection(Long recordId, Long areaId) {
|
||||
// 1. 归属判定
|
||||
try {
|
||||
inspectionAttributionService.determineAttribution(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[handleFailedInspection] 归属判定异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
|
||||
// 2. 创建整改工单(不论归属判定结果,不合格即需整改)
|
||||
try {
|
||||
inspectionRectificationService.createRectificationOrder(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[handleFailedInspection] 整改工单创建异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,9 +8,10 @@ import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecor
|
||||
import com.viewsh.module.ops.environment.dal.mysql.inspection.OpsInspectionRecordMapper;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import java.util.List;
|
||||
@@ -35,10 +36,7 @@ public class InspectionRecordServiceImpl implements InspectionRecordService {
|
||||
private OpsInspectionRecordItemMapper inspectionRecordItemMapper;
|
||||
|
||||
@Resource
|
||||
private InspectionAttributionService inspectionAttributionService;
|
||||
|
||||
@Resource
|
||||
private InspectionRectificationService inspectionRectificationService;
|
||||
private InspectionAsyncHandler inspectionAsyncHandler;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@@ -69,32 +67,19 @@ public class InspectionRecordServiceImpl implements InspectionRecordService {
|
||||
.toList();
|
||||
inspectionRecordItemMapper.insertBatch(items);
|
||||
|
||||
// 4. 不合格时异步触发归属判定(Task 6 实现)
|
||||
// 4. 不合格时,在事务提交后异步触发归属判定 + 整改工单
|
||||
if (resultStatus == RESULT_STATUS_FAILED) {
|
||||
triggerAttributionAsync(record.getId(), submitReqVO.getAreaId());
|
||||
Long recordId = record.getId();
|
||||
Long areaId = submitReqVO.getAreaId();
|
||||
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
|
||||
@Override
|
||||
public void afterCommit() {
|
||||
inspectionAsyncHandler.handleFailedInspection(recordId, areaId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return record.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步触发归属判定 + 整改工单创建
|
||||
*/
|
||||
@Async
|
||||
public void triggerAttributionAsync(Long recordId, Long areaId) {
|
||||
// 1. 归属判定
|
||||
try {
|
||||
inspectionAttributionService.determineAttribution(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[triggerAttributionAsync] 归属判定异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
|
||||
// 2. 创建整改工单(不论归属判定结果,不合格即需整改)
|
||||
try {
|
||||
inspectionRectificationService.createRectificationOrder(recordId, areaId);
|
||||
} catch (Exception e) {
|
||||
log.error("[triggerAttributionAsync] 整改工单创建异常: recordId={}, areaId={}", recordId, areaId, e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -36,8 +36,9 @@ public class InspectionRectificationServiceImpl implements InspectionRectificati
|
||||
public Long createRectificationOrder(Long recordId, Long areaId) {
|
||||
// 1. 获取区域信息
|
||||
OpsBusAreaDO area = opsBusAreaMapper.selectById(areaId);
|
||||
String areaName = area != null ? area.getAreaName() : "未知区域";
|
||||
Integer standardDuration = area != null ? area.getStandardDuration() : 30;
|
||||
String areaName = (area != null) ? area.getAreaName() : "未知区域";
|
||||
int expectedDuration = (area != null && area.getStandardDuration() != null)
|
||||
? area.getStandardDuration() : 30;
|
||||
|
||||
// 2. 构建整改工单请求
|
||||
CleanOrderAutoCreateReqDTO createReq = new CleanOrderAutoCreateReqDTO();
|
||||
@@ -47,7 +48,7 @@ public class InspectionRectificationServiceImpl implements InspectionRectificati
|
||||
createReq.setDescription("巡检发现不合格,需重新清洁。巡检记录ID:" + recordId);
|
||||
createReq.setPriority(PriorityEnum.P1.getPriority());
|
||||
createReq.setAreaId(areaId);
|
||||
createReq.setExpectedDuration(standardDuration != null ? standardDuration : 30);
|
||||
createReq.setExpectedDuration(expectedDuration);
|
||||
createReq.setCleaningType("SPOT");
|
||||
createReq.setDifficultyLevel(3);
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ public class InspectionTemplateServiceImpl implements InspectionTemplateService
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateTemplate(InspectionTemplateSaveReqVO updateReqVO) {
|
||||
if (updateReqVO.getId() == null) {
|
||||
throw exception(INSPECTION_TEMPLATE_NOT_FOUND);
|
||||
}
|
||||
validateTemplateExists(updateReqVO.getId());
|
||||
OpsInspectionTemplateDO updateObj = BeanUtils.toBean(updateReqVO, OpsInspectionTemplateDO.class);
|
||||
inspectionTemplateMapper.updateById(updateObj);
|
||||
|
||||
Reference in New Issue
Block a user