fix: 修复员工导入异步实现,实现真正的非阻塞异步
问题分析: - Service方法同时使用@Async和CompletableFuture.supplyAsync - Controller调用future.get()会阻塞等待 - 这不是真正的异步 修复方案: - 移除@Async注解 - Service方法使用CompletableFuture.runAsync()异步执行doImport - Service方法改为void返回类型,立即返回 - Controller不调用future.get(),自己构建响应 - 实现真正的异步非阻塞导入 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -28,7 +28,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
/**
|
||||
* 员工信息Controller
|
||||
@@ -137,11 +136,13 @@ public class CcdiEmployeeController extends BaseController {
|
||||
return error("至少需要一条数据");
|
||||
}
|
||||
|
||||
// 异步导入,立即返回taskId
|
||||
CompletableFuture<ImportResultVO> future = employeeService.importEmployeeAsync(list, updateSupport);
|
||||
// 提交异步任务
|
||||
employeeService.importEmployeeAsync(list, updateSupport);
|
||||
|
||||
// future已经完成,get()不会阻塞
|
||||
ImportResultVO result = future.get();
|
||||
// 立即返回,不等待后台任务完成
|
||||
ImportResultVO result = new ImportResultVO();
|
||||
result.setStatus("PROCESSING");
|
||||
result.setMessage("导入任务已提交,正在后台处理");
|
||||
|
||||
return AjaxResult.success("导入任务已提交,正在后台处理", result);
|
||||
}
|
||||
|
||||
@@ -92,9 +92,8 @@ public interface ICcdiEmployeeService {
|
||||
*
|
||||
* @param excelList Excel数据列表
|
||||
* @param isUpdateSupport 是否更新已存在的数据
|
||||
* @return 任务结果Future
|
||||
*/
|
||||
CompletableFuture<ImportResultVO> importEmployeeAsync(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport);
|
||||
void importEmployeeAsync(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport);
|
||||
|
||||
/**
|
||||
* 查询导入状态
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.ruoyi.common.utils.StringUtils;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -236,11 +235,9 @@ public class CcdiEmployeeServiceImpl implements ICcdiEmployeeService {
|
||||
*
|
||||
* @param excelList Excel数据列表
|
||||
* @param isUpdateSupport 是否更新已存在的数据
|
||||
* @return 任务结果Future
|
||||
*/
|
||||
@Override
|
||||
@Async("importExecutor")
|
||||
public CompletableFuture<ImportResultVO> importEmployeeAsync(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport) {
|
||||
public void importEmployeeAsync(List<CcdiEmployeeExcel> excelList, Boolean isUpdateSupport) {
|
||||
String taskId = UUID.randomUUID().toString();
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
@@ -259,24 +256,16 @@ public class CcdiEmployeeServiceImpl implements ICcdiEmployeeService {
|
||||
redisTemplate.opsForHash().putAll(statusKey, statusData);
|
||||
redisTemplate.expire(statusKey, 7, TimeUnit.DAYS);
|
||||
|
||||
// 使用CompletableFuture.supplyAsync在独立的executor中异步执行doImport
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
// 在独立线程中异步执行导入,立即返回
|
||||
CompletableFuture.runAsync(() -> {
|
||||
try {
|
||||
// 在独立线程中执行导入
|
||||
// 在后台线程中执行导入
|
||||
ImportResult result = doImport(excelList, isUpdateSupport, taskId);
|
||||
|
||||
// 更新最终状态
|
||||
String finalStatus = result.getFailureCount() == 0 ? "SUCCESS" : "PARTIAL_SUCCESS";
|
||||
updateImportStatus(taskId, finalStatus, result, startTime);
|
||||
|
||||
// 构建返回结果
|
||||
ImportResultVO resultVO = new ImportResultVO();
|
||||
resultVO.setTaskId(taskId);
|
||||
resultVO.setStatus(finalStatus);
|
||||
resultVO.setMessage("导入完成");
|
||||
|
||||
return resultVO;
|
||||
|
||||
} catch (Exception e) {
|
||||
// 处理异常
|
||||
Map<String, Object> errorData = new HashMap<>();
|
||||
@@ -284,13 +273,6 @@ public class CcdiEmployeeServiceImpl implements ICcdiEmployeeService {
|
||||
errorData.put("message", "导入失败: " + e.getMessage());
|
||||
errorData.put("endTime", System.currentTimeMillis());
|
||||
redisTemplate.opsForHash().putAll(statusKey, errorData);
|
||||
|
||||
ImportResultVO resultVO = new ImportResultVO();
|
||||
resultVO.setTaskId(taskId);
|
||||
resultVO.setStatus("FAILED");
|
||||
resultVO.setMessage("导入失败: " + e.getMessage());
|
||||
|
||||
return resultVO;
|
||||
}
|
||||
}, importExecutor);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user