高性能Java并发编程:如何优雅地使用CompletableFuture进行异步编排

使用例子

java 复制代码
  /**
     * 收入日报数据组装
     */
    private <T extends StoreBaseStaDto> void getDoDailyIncomeReportInfo(List<OrganizeGeneralDetailVO> storeInfos
            , T params, String tenantId, List<FtbDataAnalysisReportDetails> analysisReportDetails) {
        Map<String, String> storeMap = storeInfos.stream().collect(Collectors.toMap(OrganizeGeneralDetailVO::getId, OrganizeGeneralDetailVO::getCashierStoreId));
        Map<String, OrganizeGeneralDetailVO> storeInfoMap = storeInfos.stream()
                .collect(Collectors.toMap(OrganizeGeneralDetailVO::getId, organizeGeneralDetailVO -> organizeGeneralDetailVO));
        // 门店信息
        StoreRevenueAnalysisStaDto aDTO = new StoreRevenueAnalysisStaDto();
        aDTO.setDimension(DimensionTypeEnums.MONTH);
        aDTO.setOpenStartDate(params.getOpenStartDate());
        aDTO.setOpenEndDate(params.getOpenEndDate());
        aDTO.setStartDate(params.getStartDate());
        aDTO.setEndDate(params.getEndDate());
        aDTO.setStoreMap(storeMap);
        StoreRevenueAnalysisStaDto bDTO = new StoreRevenueAnalysisStaDto();
        BeanUtils.copyProperties(aDTO, bDTO);
        bDTO.setDimension(DimensionTypeEnums.DAY);
        // 创建任务列表,每个任务都添加异常处理
        CompletableFuture<Map<String, DailyIncomeReportStoreInfoVO>> storeMonthInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreMonthInfoStaMap(aDTO));
                    } catch (Exception e) {
                        log.error("门店信息目标查询失败", e);
                        return new HashMap<>();
                    }
                }, executor);
        CompletableFuture<Map<String, DailyIncomeReportIntegratedVO>> storeDaySynthesisInfoFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDaySynthesisInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("综合信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor);
        CompletableFuture<Map<String, DailyIncomeReportEatInVO>> storeDayEatInInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayDineInInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("堂食信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportTakeAwayFoodVO>> storeDayTakeOutInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayTakeOutInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("外卖信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportCorrectionVO>> storeDayBadCommentInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayCorrectionInfoStaMap(bDTO));
                    } catch (Exception e) {
                        log.error("中差评信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        CompletableFuture<Map<String, DailyIncomeReportDeskVisitVO>> storeDayTableVisitInfoStaMapFuture = CompletableFuture.supplyAsync(
                () -> {
                    try {
                        return TenantSupplierWrapper.supplierWrapper(tenantId, () -> dailyIncomeReportService.getStoreDayDeskVisitInfoStaMap(bDTO, tenantId));
                    } catch (Exception e) {
                        log.error("桌访信息查询失败", e);
                        return new HashMap<>();
                    }
                }, executor
        );
        // 合并所有任务
        CompletableFuture<Void> allTasks = CompletableFuture.allOf(
                storeMonthInfoStaMapFuture, storeDaySynthesisInfoFuture,
                storeDayEatInInfoStaMapFuture, storeDayTakeOutInfoStaMapFuture,
                storeDayBadCommentInfoStaMapFuture, storeDayTableVisitInfoStaMapFuture);
        try {
            // 设置超时时间,比如30秒
            allTasks.get(30, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            log.error("收入日报数据查询超时,已取消未完成的任务", e);
            // 取消所有未完成的任务
            storeMonthInfoStaMapFuture.cancel(true);
            storeDaySynthesisInfoFuture.cancel(true);
            storeDayEatInInfoStaMapFuture.cancel(true);
            storeDayTakeOutInfoStaMapFuture.cancel(true);
            storeDayBadCommentInfoStaMapFuture.cancel(true);
            storeDayTableVisitInfoStaMapFuture.cancel(true);
            // 根据业务需求,可以选择抛出异常或返回部分结果
            // 这里我们选择继续处理已完成的,但如果需要严格处理,可以抛出异常
        } catch (InterruptedException e) {
            log.error("收入日报数据查询被中断", e);
            Thread.currentThread().interrupt();
            // 同样取消所有任务
            storeMonthInfoStaMapFuture.cancel(true);
            storeDaySynthesisInfoFuture.cancel(true);
            storeDayEatInInfoStaMapFuture.cancel(true);
            storeDayTakeOutInfoStaMapFuture.cancel(true);
            storeDayBadCommentInfoStaMapFuture.cancel(true);
            storeDayTableVisitInfoStaMapFuture.cancel(true);
        } catch (ExecutionException e) {
            log.error("收入日报数据查询执行异常", e);
            // 由于每个任务都有异常处理,这里通常不会发生,但为了安全起见
        }
        // 安全地获取结果,避免join()抛出异常
        Map<String, DailyIncomeReportStoreInfoVO> storeMonthInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportIntegratedVO> storeDaySynthesisInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportEatInVO> storeDayEatInInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportTakeAwayFoodVO> storeDayTakeOutInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportCorrectionVO> storeDayBadCommentInfoStaMap = new HashMap<>();
        Map<String, DailyIncomeReportDeskVisitVO> storeDayTableVisitInfoStaMap = new HashMap<>();
        try {
            if (!storeMonthInfoStaMapFuture.isCompletedExceptionally()) {
                storeMonthInfoStaMap = storeMonthInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取门店信息目标结果失败", e);
        }
        try {
            if (!storeDaySynthesisInfoFuture.isCompletedExceptionally()) {
                storeDaySynthesisInfoStaMap = storeDaySynthesisInfoFuture.get();
            }
        } catch (Exception e) {
            log.error("获取综合信息结果失败", e);
        }
        try {
            if (!storeDayEatInInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayEatInInfoStaMap = storeDayEatInInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取堂食信息结果失败", e);
        }
        try {
            if (!storeDayTakeOutInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayTakeOutInfoStaMap = storeDayTakeOutInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取外卖信息结果失败", e);
        }
        try {
            if (!storeDayBadCommentInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayBadCommentInfoStaMap = storeDayBadCommentInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取中差评信息结果失败", e);
        }
        try {
            if (!storeDayTableVisitInfoStaMapFuture.isCompletedExceptionally()) {
                storeDayTableVisitInfoStaMap = storeDayTableVisitInfoStaMapFuture.get();
            }
        } catch (Exception e) {
            log.error("获取桌访信息结果失败", e);
        }
        // 提前计算并缓存所有需要的数据
        Map<String, BigDecimal> areaEfficiencyCache = new HashMap<>();
        Map<String, BigDecimal> turnoverRateCache = new HashMap<>();
        // 预计算坪效
        for (Map.Entry<String, DailyIncomeReportIntegratedVO> entry : storeDaySynthesisInfoStaMap.entrySet()) {
            DailyIncomeReportIntegratedVO vo = entry.getValue();
            OrganizeGeneralDetailVO storeInfo = storeInfoMap.get(entry.getKey());
            if (storeInfo != null && vo != null) {
                vo.setStoreArea(storeInfo.getAreaNum());
                if (vo.getStoreArea() != null && vo.getTodayIncome() != null) {
                    BigDecimal areaEfficiency = NumberUtil.div(vo.getTodayIncome(), vo.getStoreArea(), 2);
                    vo.setAreaEfficiency(areaEfficiency);
                    areaEfficiencyCache.put(entry.getKey(), areaEfficiency);
                }
            }
        }
        // 预计算翻台率
        for (Map.Entry<String, DailyIncomeReportEatInVO> entry : storeDayEatInInfoStaMap.entrySet()) {
            DailyIncomeReportEatInVO vo = entry.getValue();
            OrganizeGeneralDetailVO storeInfo = storeInfoMap.get(entry.getKey());
            if (storeInfo != null && vo != null) {
                vo.setTableCount(storeInfo.getDeskCount());
                if (vo.getTableCount() != null && vo.getOrderCount() != null) {
                    BigDecimal turnoverRate = NumberUtil.div(vo.getOrderCount(), vo.getTableCount(), 2);
                    vo.setTurnoverRate(turnoverRate);
                    turnoverRateCache.put(entry.getKey(), turnoverRate);
                }
            }
        }
        // 组装最终数据
        for (FtbDataAnalysisReportDetails reportDetails : analysisReportDetails) {
            OrganizeGeneralDetailVO organizeGeneralDetailVO = storeInfoMap.get(reportDetails.getStoreId());
            if (organizeGeneralDetailVO == null) {
                // 跳过没有门店信息的数据
                continue;
            }
            DailyIncomeReportVO dailyIncomeReportVO = new DailyIncomeReportVO();
            // 门店信息目标
            DailyIncomeReportStoreInfoVO dailyIncomeReportStoreInfoVO = storeMonthInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportStoreInfoVO)) {
                dailyIncomeReportStoreInfoVO.setStoreName(reportDetails.getStoreName());
                dailyIncomeReportVO.setStoreInfo(dailyIncomeReportStoreInfoVO);
            }
            // 综合 - 直接从缓存中获取已计算的坪效
            DailyIncomeReportIntegratedVO dailyIncomeReportIntegratedVO = storeDaySynthesisInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportIntegratedVO)) {
                // 如果预计算时已经设置,这里可以直接使用
                if (dailyIncomeReportIntegratedVO.getAreaEfficiency() == null && areaEfficiencyCache.containsKey(reportDetails.getStoreId())) {
                    dailyIncomeReportIntegratedVO.setAreaEfficiency(areaEfficiencyCache.get(reportDetails.getStoreId()));
                }
                dailyIncomeReportVO.setIntegrated(dailyIncomeReportIntegratedVO);
            }
            // 堂食 - 直接从缓存中获取已计算的翻台率
            DailyIncomeReportEatInVO dailyIncomeReportEatInVO = storeDayEatInInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportEatInVO)) {
                // 如果预计算时已经设置,这里可以直接使用
                if (dailyIncomeReportEatInVO.getTurnoverRate() == null && turnoverRateCache.containsKey(reportDetails.getStoreId())) {
                    dailyIncomeReportEatInVO.setTurnoverRate(turnoverRateCache.get(reportDetails.getStoreId()));
                }
                dailyIncomeReportVO.setEatIn(dailyIncomeReportEatInVO);
            }
            // 外卖
            DailyIncomeReportTakeAwayFoodVO dailyIncomeReportTakeAwayFoodVO = storeDayTakeOutInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportTakeAwayFoodVO)) {
                dailyIncomeReportVO.setTakeAwayFood(dailyIncomeReportTakeAwayFoodVO);
            }
            // 中差评
            DailyIncomeReportCorrectionVO dailyIncomeReportCorrectionVO = storeDayBadCommentInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportCorrectionVO)) {
                dailyIncomeReportVO.setCorrection(dailyIncomeReportCorrectionVO);
            }
            // 桌访
            DailyIncomeReportDeskVisitVO dailyIncomeReportDeskVisitVO = storeDayTableVisitInfoStaMap.get(reportDetails.getStoreId());
            if (Objects.nonNull(dailyIncomeReportDeskVisitVO)) {
                dailyIncomeReportVO.setDeskVisit(dailyIncomeReportDeskVisitVO);
            }
            reportDetails.setIncomeDailyReport(JSON.toJSONString(dailyIncomeReportVO));
        }
    }
java 复制代码
package com.fantaibao.util.wrappers;

import jnpf.database.util.TenantDataSourceUtil;
import jnpf.util.data.DataSourceContextHolder;
import lombok.experimental.UtilityClass;

import java.util.function.Consumer;
import java.util.function.Supplier;

@UtilityClass
public class TenantSupplierWrapper {

    public <U> U supplierWrapper(String tenantId, Supplier<U> supplier) {
        try {
            TenantDataSourceUtil.switchTenant(tenantId);
            return supplier.get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            DataSourceContextHolder.clearDatasourceType();
        }
    }
}
相关推荐
惊讶的猫2 小时前
探究StringBuilder和StringBuffer的线程安全问题
java·开发语言
jmxwzy2 小时前
Spring全家桶
java·spring·rpc
Halo_tjn2 小时前
基于封装的专项 知识点
java·前端·python·算法
Fleshy数模3 小时前
从数据获取到突破限制:Python爬虫进阶实战全攻略
java·开发语言
像少年啦飞驰点、3 小时前
零基础入门 Spring Boot:从“Hello World”到可上线的 Web 应用全闭环指南
java·spring boot·web开发·编程入门·后端开发
苍煜3 小时前
万字详解Maven打包策略:从基础插件到多模块实战
java·maven
有来技术3 小时前
Spring Boot 4 + Vue3 企业级多租户 SaaS:从共享 Schema 架构到商业化套餐设计
java·vue.js·spring boot·后端
东东5163 小时前
xxx医患档案管理系统
java·spring boot·vue·毕业设计·智慧城市
一个响当当的名号4 小时前
lectrue9 索引并发控制
java·开发语言·数据库
进阶小白猿4 小时前
Java技术八股学习Day30
java·开发语言·学习