diff --git a/jsowell-admin/src/main/java/com/jsowell/web/controller/index/indexController.java b/jsowell-admin/src/main/java/com/jsowell/web/controller/index/indexController.java index 70050214d..2aeb0d60a 100644 --- a/jsowell-admin/src/main/java/com/jsowell/web/controller/index/indexController.java +++ b/jsowell-admin/src/main/java/com/jsowell/web/controller/index/indexController.java @@ -5,33 +5,20 @@ import com.jsowell.common.core.controller.BaseController; import com.jsowell.common.response.RestApiResponse; import com.jsowell.common.util.StringUtils; import com.jsowell.pile.dto.IndexQueryDTO; -import com.jsowell.pile.service.MemberBasicInfoService; +import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO; +import com.jsowell.pile.service.IndexDataService; +import com.jsowell.pile.service.BusinessFinancialService; import com.jsowell.pile.service.OrderBasicInfoService; import com.jsowell.pile.service.PileBasicInfoService; -import com.jsowell.pile.service.PileConnectorInfoService; import com.jsowell.pile.service.PileStationInfoService; -import com.jsowell.pile.service.PileMerchantInfoService; -import com.jsowell.pile.service.BusinessFinancialService; -import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO; -import com.jsowell.pile.util.UserUtils; import com.jsowell.pile.vo.web.*; -import com.jsowell.common.core.domain.vo.AuthorizedDeptVO; -import com.jsowell.common.util.SecurityUtils; -import com.jsowell.common.constant.Constants; -import com.jsowell.pile.domain.PileMerchantInfo; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import org.springframework.util.CollectionUtils; -import java.math.BigDecimal; -import java.math.RoundingMode; -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; /** * 首页数据展示Controller @@ -48,23 +35,14 @@ public class indexController extends BaseController { @Autowired private OrderBasicInfoService orderBasicInfoService; - @Autowired - private MemberBasicInfoService memberBasicInfoService; - @Autowired private PileStationInfoService pileStationInfoService; - @Autowired - private PileConnectorInfoService pileConnectorInfoService; - - @Autowired - private PileMerchantInfoService pileMerchantInfoService; - @Autowired private BusinessFinancialService businessFinancialService; @Autowired - private Executor threadPoolTaskExecutor; + private IndexDataService indexDataService; @PostMapping("/getGeneralSituation") public RestApiResponse getGeneralSituation(@RequestBody IndexQueryDTO dto) { @@ -141,7 +119,7 @@ public class indexController extends BaseController { /** * 大数据平台-总览数据(性能优化版) - * 权限解析在主线程完成,按merchantId或stationId维度查询,不用SQL SUM,Java汇总 + * 业务逻辑在IndexDataService中实现 */ @PostMapping("/getBigDataOverview") public RestApiResponse getBigDataOverview(@RequestBody(required = false) IndexQueryDTO dto) { @@ -149,168 +127,7 @@ public class indexController extends BaseController { logger.info("大数据平台总览数据查询 param:{}", JSON.toJSONString(dto)); RestApiResponse response; try { - BigDataOverviewVO overviewVO = new BigDataOverviewVO(); - - // === 第一步:主线程做权限解析(依赖SecurityContext,不可异步) === - // 平台管理员:merchantIdList为空,直接查全量(不加任何过滤) - // 运营商管理员:使用merchantId维度 - // 站点管理员:使用stationId维度 - AuthParams authParams = resolveAuthParams(); - - // demo账号标记 - boolean isDemo = false; - try { - isDemo = "demo".equals(SecurityUtils.getUsername()); - } catch (Exception ignored) {} - - // === 第二步:所有DB查询并行执行 === - final List finalMerchantIdList = authParams.merchantIdList; - final List finalStationIdList = authParams.stationIdList; - final boolean isPlatformAdmin = authParams.isPlatformAdmin; - - // 带权限过滤的查询:根据账号级别选择维度 - CompletableFuture situationFuture; - - if (isPlatformAdmin) { - // 平台管理员:直接查全量,不加merchant_id/station_id过滤 - situationFuture = CompletableFuture.supplyAsync( - () -> pileBasicInfoService.aggregateReportByMerchantIds(null), threadPoolTaskExecutor); - } else if (!CollectionUtils.isEmpty(finalMerchantIdList)) { - // 运营商管理员:按merchantId查 - final List midList = finalMerchantIdList; - situationFuture = CompletableFuture.supplyAsync( - () -> pileBasicInfoService.aggregateReportByMerchantIds(midList), threadPoolTaskExecutor); - } else if (!CollectionUtils.isEmpty(finalStationIdList)) { - // 站点管理员:按stationId查 - final List sidList = finalStationIdList; - situationFuture = CompletableFuture.supplyAsync( - () -> pileBasicInfoService.aggregateReportByStationIds(sidList), threadPoolTaskExecutor); - } else { - // 非平台管理员但未解析出任何可查维度(商户未绑定/部门下无站点): - // 返回空数据,避免空列表退化为无过滤的全量查询导致越权 - logger.warn("大数据平台总览数据查询:当前账号无任何可查的商户/站点维度,返回空数据"); - situationFuture = CompletableFuture.completedFuture(emptySituationVO()); - } - - // 数据范围:平台管理员不限制(null),下级账号按各自的merchantIdList/stationIdList过滤 - final boolean hasDataScope = isPlatformAdmin - || !CollectionUtils.isEmpty(finalMerchantIdList) - || !CollectionUtils.isEmpty(finalStationIdList); - final List scopeMerchantIds = isPlatformAdmin ? null : finalMerchantIdList; - final List scopeStationIds = isPlatformAdmin ? null : finalStationIdList; - - // 其余指标查询(与累计字段同一数据范围;日期在代码中生成,不在数据库中运算) - java.time.LocalDate today = java.time.LocalDate.now(); - String todayStart = today.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00"; - String todayEnd = today.plusDays(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00"; - String monthStart = today.withDayOfMonth(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); - String monthEnd = today.plusDays(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); - final String fTodayStart = todayStart; - final String fTodayEnd = todayEnd; - final String fMonthStart = monthStart; - final String fMonthEnd = monthEnd; - - CompletableFuture totalUsersFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> memberBasicInfoService.countTotalMembers(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(0L); - CompletableFuture totalStationsFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> pileStationInfoService.countTotalStations(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(0L); - CompletableFuture dailyNewUsersFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> memberBasicInfoService.countTodayNewMembers(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(0L); - CompletableFuture todayAmountFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> orderBasicInfoService.getTodayTransactionAmount(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); - CompletableFuture todayElecFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> orderBasicInfoService.getTodayElectricity(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); - CompletableFuture monthlyAvgFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> pileBasicInfoService.getMonthlyAvgElectricity(fMonthStart, fMonthEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); - CompletableFuture totalGunsFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> pileConnectorInfoService.countTotalConnectors(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(0L); - CompletableFuture onlinePilesFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> pileConnectorInfoService.countOnlinePiles(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(0L); - // 充电桩数量统计:总数/直流/交流同源(保证 直流 + 交流 = 总数) - CompletableFuture pilesStatFuture = hasDataScope - ? CompletableFuture.supplyAsync( - () -> pileBasicInfoService.countPilesStat(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) - : CompletableFuture.completedFuture(new PileCountStatVO()); - - // 等待所有异步查询完成 - CompletableFuture.allOf( - situationFuture, - totalUsersFuture, totalStationsFuture, dailyNewUsersFuture, - todayAmountFuture, todayElecFuture, monthlyAvgFuture, - totalGunsFuture, onlinePilesFuture, pilesStatFuture - ).join(); - - // === 第三步:组装结果 === - IndexGeneralSituationVO situation = situationFuture.get(); - String totalChargingAmount = situation.getTotalChargingAmount(); - String totalChargingDegree = situation.getTotalChargingDegree(); - PileCountStatVO pilesStat = pilesStatFuture.get(); - Long totalPileCount = pilesStat != null ? pilesStat.getTotalPiles() : null; - Long dcPileCount = pilesStat != null ? pilesStat.getDcPileCount() : null; - Long acPileCount = pilesStat != null ? pilesStat.getAcPileCount() : null; - - // 总订单数从settle_order_report的GROUP BY结果中获取(charge_num汇总) - String totalOrderCountStr = situation.getTotalChargingQuantity(); - Long totalOrderCount = (totalOrderCountStr != null && !totalOrderCountStr.isEmpty()) - ? Long.parseLong(totalOrderCountStr.replaceAll("[.](.*)", "")) : 0L; - - overviewVO.setTotalUsers(totalUsersFuture.get()); - overviewVO.setTotalOrders(totalOrderCount); - overviewVO.setTotalTransactionAmount(totalChargingAmount); - overviewVO.setTotalElectricity(totalChargingDegree); - overviewVO.setTotalPiles(totalPileCount != null ? totalPileCount : 0L); - overviewVO.setTotalStations(totalStationsFuture.get()); - overviewVO.setDailyNewUsers(dailyNewUsersFuture.get()); - BigDecimal todayAmount = todayAmountFuture.get(); - overviewVO.setTodayTransactionAmount(todayAmount != null ? todayAmount.toPlainString() : "0"); - BigDecimal todayElec = todayElecFuture.get(); - overviewVO.setTodayElectricity(todayElec != null ? todayElec.toPlainString() : "0"); - BigDecimal monthlyAvg = monthlyAvgFuture.get(); - overviewVO.setMonthlyAvgElectricity(monthlyAvg != null ? monthlyAvg.toPlainString() : "0"); - overviewVO.setTotalGuns(totalGunsFuture.get()); - overviewVO.setOnlinePiles(onlinePilesFuture.get()); - overviewVO.setDcPileCount(dcPileCount != null ? dcPileCount : 0L); - overviewVO.setAcPileCount(acPileCount != null ? acPileCount : 0L); - - // 节能减排计算 - BigDecimal totalElecKwh = new BigDecimal(totalChargingDegree); - BigDecimal carbonKg = totalElecKwh.multiply(new BigDecimal("0.5306")); - BigDecimal carbonTon = carbonKg.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP); - overviewVO.setCarbonReduction(carbonTon.toPlainString()); - if (totalOrderCount != null && totalOrderCount > 0) { - BigDecimal avgCarbon = carbonKg.divide(new BigDecimal(totalOrderCount), 2, RoundingMode.HALF_UP); - overviewVO.setAvgCarbonPerOrder(avgCarbon.toPlainString()); - } else { - overviewVO.setAvgCarbonPerOrder("0"); - } - BigDecimal fuelSaved = totalElecKwh.multiply(new BigDecimal("8")) - .divide(new BigDecimal("15"), 2, RoundingMode.HALF_UP); - overviewVO.setFuelSaved(fuelSaved.toPlainString()); - BigDecimal coalSaved = totalElecKwh.multiply(new BigDecimal("0.000404")) - .setScale(2, RoundingMode.HALF_UP); - overviewVO.setStandardCoalSaved(coalSaved.toPlainString()); - - // demo账号展示效果处理(节能减排基于处理后的电量/订单数重算) - if (isDemo) { - applyDemoEffect(overviewVO); - } - + BigDataOverviewVO overviewVO = indexDataService.getOverview(); response = new RestApiResponse<>(overviewVO); } catch (Exception e) { logger.error("大数据平台总览数据查询错误", e); @@ -319,114 +136,6 @@ public class indexController extends BaseController { logger.info("大数据平台总览数据查询 result:{}", JSON.toJSONString(response)); return response; } - - /** - * 权限参数内部类 - */ - private static class AuthParams { - boolean isPlatformAdmin = false; - List merchantIdList = new ArrayList<>(); - List stationIdList = new ArrayList<>(); - } - - /** - * 解析当前用户权限参数 - * - 平台管理员:标记isPlatformAdmin=true,不需要任何过滤条件 - * - 运营商管理员:根据deptId获取merchantId - * - 站点管理员:使用stationId - */ - private AuthParams resolveAuthParams() { - AuthParams params = new AuthParams(); - AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap(); - if (authorizedMap == null) { - return params; - } - List stationDeptIds = authorizedMap.getStationDeptIds(); - List merchantDeptIds = authorizedMap.getMerchantDeptIds(); - - if (!CollectionUtils.isEmpty(stationDeptIds)) { - // 站点管理员:使用站点维度 - List list = pileStationInfoService.queryByStationDeptIds(stationDeptIds); - if (!CollectionUtils.isEmpty(list)) { - params.stationIdList.addAll(list); - } - } else if (!CollectionUtils.isEmpty(merchantDeptIds)) { - // 运营商管理员:根据deptId获取merchantId - for (String deptId : merchantDeptIds) { - PileMerchantInfo merchant = pileMerchantInfoService.queryInfoByDeptId(deptId); - if (merchant != null && merchant.getId() != null) { - params.merchantIdList.add(String.valueOf(merchant.getId())); - } - } - } else { - // 平台管理员:直接查全量,不需要任何过滤 - params.isPlatformAdmin = true; - } - return params; - } - - /** - * 空权限维度时的占位汇总数据(全为0,避免下游NPE) - */ - private static IndexGeneralSituationVO emptySituationVO() { - IndexGeneralSituationVO vo = new IndexGeneralSituationVO(); - vo.setTotalChargingAmount("0"); - vo.setTotalSettleAmount("0"); - vo.setTotalChargingDegree("0"); - vo.setTotalChargingQuantity("0"); - return vo; - } - - /** - * demo账号展示效果处理 - * 所有总量类指标(用户、订单、金额、电量、站点、桩、枪等)按统一倍数调整,保证各维度数据比例一致; - * 节能减排基于调整后的累计电量/订单数重算,其中"单次充电平均减碳量"为比值型指标,调整后保持不变 - */ - private static void applyDemoEffect(BigDataOverviewVO vo) { - BigDecimal multiplier = new BigDecimal(Constants.THREE); - vo.setTotalUsers(multiply(vo.getTotalUsers(), multiplier)); - vo.setTotalOrders(multiply(vo.getTotalOrders(), multiplier)); - vo.setTotalTransactionAmount(multiply(vo.getTotalTransactionAmount(), multiplier)); - vo.setTotalElectricity(multiply(vo.getTotalElectricity(), multiplier)); - vo.setTotalPiles(multiply(vo.getTotalPiles(), multiplier)); - vo.setTotalStations(multiply(vo.getTotalStations(), multiplier)); - vo.setDailyNewUsers(multiply(vo.getDailyNewUsers(), multiplier)); - vo.setTodayTransactionAmount(multiply(vo.getTodayTransactionAmount(), multiplier)); - vo.setTodayElectricity(multiply(vo.getTodayElectricity(), multiplier)); - vo.setMonthlyAvgElectricity(multiply(vo.getMonthlyAvgElectricity(), multiplier)); - vo.setTotalGuns(multiply(vo.getTotalGuns(), multiplier)); - vo.setOnlinePiles(multiply(vo.getOnlinePiles(), multiplier)); - vo.setDcPileCount(multiply(vo.getDcPileCount(), multiplier)); - vo.setAcPileCount(multiply(vo.getAcPileCount(), multiplier)); - - // 节能减排按调整后的累计电量/订单数重算 - BigDecimal totalElecKwh = new BigDecimal(vo.getTotalElectricity()); - BigDecimal carbonKg = totalElecKwh.multiply(new BigDecimal("0.5306")); - BigDecimal carbonTon = carbonKg.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP); - vo.setCarbonReduction(carbonTon.toPlainString()); - Long totalOrderCount = vo.getTotalOrders(); - if (totalOrderCount != null && totalOrderCount > 0) { - BigDecimal avgCarbon = carbonKg.divide(new BigDecimal(totalOrderCount), 2, RoundingMode.HALF_UP); - vo.setAvgCarbonPerOrder(avgCarbon.toPlainString()); - } else { - vo.setAvgCarbonPerOrder("0"); - } - BigDecimal fuelSaved = totalElecKwh.multiply(new BigDecimal("8")) - .divide(new BigDecimal("15"), 2, RoundingMode.HALF_UP); - vo.setFuelSaved(fuelSaved.toPlainString()); - BigDecimal coalSaved = totalElecKwh.multiply(new BigDecimal("0.000404")) - .setScale(2, RoundingMode.HALF_UP); - vo.setStandardCoalSaved(coalSaved.toPlainString()); - } - - private static Long multiply(Long value, BigDecimal multiplier) { - return value == null ? null : new BigDecimal(value).multiply(multiplier).longValue(); - } - - private static String multiply(String value, BigDecimal multiplier) { - return value == null ? null : new BigDecimal(value).multiply(multiplier).toString(); - } - /** * 大数据平台-充电站地图数据 */ diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/IndexDataService.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/IndexDataService.java new file mode 100644 index 000000000..6fba003ca --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/IndexDataService.java @@ -0,0 +1,21 @@ +package com.jsowell.pile.service; + +import com.jsowell.pile.vo.web.BigDataOverviewVO; + +/** + * 首页数据Service(供indexController使用) + * 大数据平台-总览数据 + * + * @author jsowell + */ +public interface IndexDataService { + + /** + * 获取大数据平台总览数据 + * 权限解析依赖SecurityContext(不可异步),按当前登录账号的商户/站点维度过滤; + * 所有指标与累计字段使用同一数据范围,保证口径一致 + * + * @return 总览数据 + */ + BigDataOverviewVO getOverview(); +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/IndexDataServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/IndexDataServiceImpl.java new file mode 100644 index 000000000..91d7d3875 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/IndexDataServiceImpl.java @@ -0,0 +1,334 @@ +package com.jsowell.pile.service.impl; + +import com.jsowell.common.constant.Constants; +import com.jsowell.common.core.domain.vo.AuthorizedDeptVO; +import com.jsowell.common.util.SecurityUtils; +import com.jsowell.pile.domain.PileMerchantInfo; +import com.jsowell.pile.service.IndexDataService; +import com.jsowell.pile.service.MemberBasicInfoService; +import com.jsowell.pile.service.OrderBasicInfoService; +import com.jsowell.pile.service.PileBasicInfoService; +import com.jsowell.pile.service.PileConnectorInfoService; +import com.jsowell.pile.service.PileMerchantInfoService; +import com.jsowell.pile.service.PileStationInfoService; +import com.jsowell.pile.util.UserUtils; +import com.jsowell.pile.vo.web.BigDataOverviewVO; +import com.jsowell.pile.vo.web.IndexGeneralSituationVO; +import com.jsowell.pile.vo.web.PileCountStatVO; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.util.CollectionUtils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; + +/** + * 首页数据Service(供indexController使用)- 大数据平台总览数据(性能优化版) + * 权限解析在主线程完成,按merchantId或stationId维度查询,不用SQL SUM,Java汇总 + * + * @author jsowell + */ +@Slf4j +@Service +public class IndexDataServiceImpl implements IndexDataService { + + @Autowired + private PileBasicInfoService pileBasicInfoService; + + @Autowired + private OrderBasicInfoService orderBasicInfoService; + + @Autowired + private MemberBasicInfoService memberBasicInfoService; + + @Autowired + private PileStationInfoService pileStationInfoService; + + @Autowired + private PileConnectorInfoService pileConnectorInfoService; + + @Autowired + private PileMerchantInfoService pileMerchantInfoService; + + @Autowired + private Executor threadPoolTaskExecutor; + + @Override + public BigDataOverviewVO getOverview() { + BigDataOverviewVO overviewVO = new BigDataOverviewVO(); + + // === 第一步:主线程做权限解析(依赖SecurityContext,不可异步) === + // 平台管理员:merchantIdList为空,直接查全量(不加任何过滤) + // 运营商管理员:使用merchantId维度 + // 站点管理员:使用stationId维度 + AuthParams authParams = resolveAuthParams(); + + // demo账号标记 + boolean isDemo = false; + try { + isDemo = "demo".equals(SecurityUtils.getUsername()); + } catch (Exception ignored) {} + + // === 第二步:所有DB查询并行执行 === + final List finalMerchantIdList = authParams.merchantIdList; + final List finalStationIdList = authParams.stationIdList; + final boolean isPlatformAdmin = authParams.isPlatformAdmin; + + // 带权限过滤的查询:根据账号级别选择维度 + CompletableFuture situationFuture; + + if (isPlatformAdmin) { + // 平台管理员:直接查全量,不加merchant_id/station_id过滤 + situationFuture = CompletableFuture.supplyAsync( + () -> pileBasicInfoService.aggregateReportByMerchantIds(null), threadPoolTaskExecutor); + } else if (!CollectionUtils.isEmpty(finalMerchantIdList)) { + // 运营商管理员:按merchantId查 + final List midList = finalMerchantIdList; + situationFuture = CompletableFuture.supplyAsync( + () -> pileBasicInfoService.aggregateReportByMerchantIds(midList), threadPoolTaskExecutor); + } else if (!CollectionUtils.isEmpty(finalStationIdList)) { + // 站点管理员:按stationId查 + final List sidList = finalStationIdList; + situationFuture = CompletableFuture.supplyAsync( + () -> pileBasicInfoService.aggregateReportByStationIds(sidList), threadPoolTaskExecutor); + } else { + // 非平台管理员但未解析出任何可查维度(商户未绑定/部门下无站点): + // 返回空数据,避免空列表退化为无过滤的全量查询导致越权 + log.warn("大数据平台总览数据查询:当前账号无任何可查的商户/站点维度,返回空数据"); + situationFuture = CompletableFuture.completedFuture(emptySituationVO()); + } + + // 数据范围:平台管理员不限制(null),下级账号按各自的merchantIdList/stationIdList过滤 + final boolean hasDataScope = isPlatformAdmin + || !CollectionUtils.isEmpty(finalMerchantIdList) + || !CollectionUtils.isEmpty(finalStationIdList); + final List scopeMerchantIds = isPlatformAdmin ? null : finalMerchantIdList; + final List scopeStationIds = isPlatformAdmin ? null : finalStationIdList; + + // 其余指标查询(与累计字段同一数据范围;日期在代码中生成,不在数据库中运算) + java.time.LocalDate today = java.time.LocalDate.now(); + String todayStart = today.format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00"; + String todayEnd = today.plusDays(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00"; + String monthStart = today.withDayOfMonth(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); + String monthEnd = today.plusDays(1).format(java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd")); + final String fTodayStart = todayStart; + final String fTodayEnd = todayEnd; + final String fMonthStart = monthStart; + final String fMonthEnd = monthEnd; + + CompletableFuture totalUsersFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> memberBasicInfoService.countTotalMembers(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(0L); + CompletableFuture totalStationsFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> pileStationInfoService.countTotalStations(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(0L); + CompletableFuture dailyNewUsersFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> memberBasicInfoService.countTodayNewMembers(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(0L); + CompletableFuture todayAmountFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> orderBasicInfoService.getTodayTransactionAmount(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); + CompletableFuture todayElecFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> orderBasicInfoService.getTodayElectricity(fTodayStart, fTodayEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); + CompletableFuture monthlyAvgFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> pileBasicInfoService.getMonthlyAvgElectricity(fMonthStart, fMonthEnd, scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(java.math.BigDecimal.ZERO); + CompletableFuture totalGunsFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> pileConnectorInfoService.countTotalConnectors(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(0L); + CompletableFuture onlinePilesFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> pileConnectorInfoService.countOnlinePiles(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(0L); + // 充电桩数量统计:总数/直流/交流同源(保证 直流 + 交流 = 总数) + CompletableFuture pilesStatFuture = hasDataScope + ? CompletableFuture.supplyAsync( + () -> pileBasicInfoService.countPilesStat(scopeMerchantIds, scopeStationIds), threadPoolTaskExecutor) + : CompletableFuture.completedFuture(new PileCountStatVO()); + + // 等待所有异步查询完成 + CompletableFuture.allOf( + situationFuture, + totalUsersFuture, totalStationsFuture, dailyNewUsersFuture, + todayAmountFuture, todayElecFuture, monthlyAvgFuture, + totalGunsFuture, onlinePilesFuture, pilesStatFuture + ).join(); + + // === 第三步:组装结果 === + IndexGeneralSituationVO situation = situationFuture.join(); + String totalChargingAmount = situation.getTotalChargingAmount(); + String totalChargingDegree = situation.getTotalChargingDegree(); + PileCountStatVO pilesStat = pilesStatFuture.join(); + Long totalPileCount = pilesStat != null ? pilesStat.getTotalPiles() : null; + Long dcPileCount = pilesStat != null ? pilesStat.getDcPileCount() : null; + Long acPileCount = pilesStat != null ? pilesStat.getAcPileCount() : null; + + // 总订单数从settle_order_report的GROUP BY结果中获取(charge_num汇总) + String totalOrderCountStr = situation.getTotalChargingQuantity(); + Long totalOrderCount = (totalOrderCountStr != null && !totalOrderCountStr.isEmpty()) + ? Long.parseLong(totalOrderCountStr.replaceAll("[.](.*)", "")) : 0L; + + overviewVO.setTotalUsers(totalUsersFuture.join()); + overviewVO.setTotalOrders(totalOrderCount); + overviewVO.setTotalTransactionAmount(totalChargingAmount); + overviewVO.setTotalElectricity(totalChargingDegree); + overviewVO.setTotalPiles(totalPileCount != null ? totalPileCount : 0L); + overviewVO.setTotalStations(totalStationsFuture.join()); + overviewVO.setDailyNewUsers(dailyNewUsersFuture.join()); + BigDecimal todayAmount = todayAmountFuture.join(); + overviewVO.setTodayTransactionAmount(todayAmount != null ? todayAmount.toPlainString() : "0"); + BigDecimal todayElec = todayElecFuture.join(); + overviewVO.setTodayElectricity(todayElec != null ? todayElec.toPlainString() : "0"); + BigDecimal monthlyAvg = monthlyAvgFuture.join(); + overviewVO.setMonthlyAvgElectricity(monthlyAvg != null ? monthlyAvg.toPlainString() : "0"); + overviewVO.setTotalGuns(totalGunsFuture.join()); + overviewVO.setOnlinePiles(onlinePilesFuture.join()); + overviewVO.setDcPileCount(dcPileCount != null ? dcPileCount : 0L); + overviewVO.setAcPileCount(acPileCount != null ? acPileCount : 0L); + + // 节能减排计算 + BigDecimal totalElecKwh = new BigDecimal(totalChargingDegree); + BigDecimal carbonKg = totalElecKwh.multiply(new BigDecimal("0.5306")); + BigDecimal carbonTon = carbonKg.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP); + overviewVO.setCarbonReduction(carbonTon.toPlainString()); + if (totalOrderCount != null && totalOrderCount > 0) { + BigDecimal avgCarbon = carbonKg.divide(new BigDecimal(totalOrderCount), 2, RoundingMode.HALF_UP); + overviewVO.setAvgCarbonPerOrder(avgCarbon.toPlainString()); + } else { + overviewVO.setAvgCarbonPerOrder("0"); + } + BigDecimal fuelSaved = totalElecKwh.multiply(new BigDecimal("8")) + .divide(new BigDecimal("15"), 2, RoundingMode.HALF_UP); + overviewVO.setFuelSaved(fuelSaved.toPlainString()); + BigDecimal coalSaved = totalElecKwh.multiply(new BigDecimal("0.000404")) + .setScale(2, RoundingMode.HALF_UP); + overviewVO.setStandardCoalSaved(coalSaved.toPlainString()); + + // demo账号展示效果处理(节能减排基于处理后的电量/订单数重算) + if (isDemo) { + applyDemoEffect(overviewVO); + } + + return overviewVO; + } + + /** + * 权限参数内部类 + */ + private static class AuthParams { + boolean isPlatformAdmin = false; + List merchantIdList = new ArrayList<>(); + List stationIdList = new ArrayList<>(); + } + + /** + * 解析当前用户权限参数 + * - 平台管理员:标记isPlatformAdmin=true,不需要任何过滤条件 + * - 运营商管理员:根据deptId获取merchantId + * - 站点管理员:使用stationId + */ + private AuthParams resolveAuthParams() { + AuthParams params = new AuthParams(); + AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap(); + if (authorizedMap == null) { + return params; + } + List stationDeptIds = authorizedMap.getStationDeptIds(); + List merchantDeptIds = authorizedMap.getMerchantDeptIds(); + + if (!CollectionUtils.isEmpty(stationDeptIds)) { + // 站点管理员:使用站点维度 + List list = pileStationInfoService.queryByStationDeptIds(stationDeptIds); + if (!CollectionUtils.isEmpty(list)) { + params.stationIdList.addAll(list); + } + } else if (!CollectionUtils.isEmpty(merchantDeptIds)) { + // 运营商管理员:根据deptId获取merchantId + for (String deptId : merchantDeptIds) { + PileMerchantInfo merchant = pileMerchantInfoService.queryInfoByDeptId(deptId); + if (merchant != null && merchant.getId() != null) { + params.merchantIdList.add(String.valueOf(merchant.getId())); + } + } + } else { + // 平台管理员:直接查全量,不需要任何过滤 + params.isPlatformAdmin = true; + } + return params; + } + + /** + * 空权限维度时的占位汇总数据(全为0,避免下游NPE) + */ + private static IndexGeneralSituationVO emptySituationVO() { + IndexGeneralSituationVO vo = new IndexGeneralSituationVO(); + vo.setTotalChargingAmount("0"); + vo.setTotalSettleAmount("0"); + vo.setTotalChargingDegree("0"); + vo.setTotalChargingQuantity("0"); + return vo; + } + + /** + * demo账号展示效果处理 + * 所有总量类指标(用户、订单、金额、电量、站点、桩、枪等)按统一倍数调整,保证各维度数据比例一致; + * 节能减排基于调整后的累计电量/订单数重算,其中"单次充电平均减碳量"为比值型指标,调整后保持不变 + */ + private static void applyDemoEffect(BigDataOverviewVO vo) { + BigDecimal multiplier = new BigDecimal(Constants.THREE); + vo.setTotalUsers(multiply(vo.getTotalUsers(), multiplier)); + vo.setTotalOrders(multiply(vo.getTotalOrders(), multiplier)); + vo.setTotalTransactionAmount(multiply(vo.getTotalTransactionAmount(), multiplier)); + vo.setTotalElectricity(multiply(vo.getTotalElectricity(), multiplier)); + vo.setTotalPiles(multiply(vo.getTotalPiles(), multiplier)); + vo.setTotalStations(multiply(vo.getTotalStations(), multiplier)); + vo.setDailyNewUsers(multiply(vo.getDailyNewUsers(), multiplier)); + vo.setTodayTransactionAmount(multiply(vo.getTodayTransactionAmount(), multiplier)); + vo.setTodayElectricity(multiply(vo.getTodayElectricity(), multiplier)); + vo.setMonthlyAvgElectricity(multiply(vo.getMonthlyAvgElectricity(), multiplier)); + vo.setTotalGuns(multiply(vo.getTotalGuns(), multiplier)); + vo.setOnlinePiles(multiply(vo.getOnlinePiles(), multiplier)); + vo.setDcPileCount(multiply(vo.getDcPileCount(), multiplier)); + vo.setAcPileCount(multiply(vo.getAcPileCount(), multiplier)); + + // 节能减排按调整后的累计电量/订单数重算 + BigDecimal totalElecKwh = new BigDecimal(vo.getTotalElectricity()); + BigDecimal carbonKg = totalElecKwh.multiply(new BigDecimal("0.5306")); + BigDecimal carbonTon = carbonKg.divide(new BigDecimal("1000"), 2, RoundingMode.HALF_UP); + vo.setCarbonReduction(carbonTon.toPlainString()); + Long totalOrderCount = vo.getTotalOrders(); + if (totalOrderCount != null && totalOrderCount > 0) { + BigDecimal avgCarbon = carbonKg.divide(new BigDecimal(totalOrderCount), 2, RoundingMode.HALF_UP); + vo.setAvgCarbonPerOrder(avgCarbon.toPlainString()); + } else { + vo.setAvgCarbonPerOrder("0"); + } + BigDecimal fuelSaved = totalElecKwh.multiply(new BigDecimal("8")) + .divide(new BigDecimal("15"), 2, RoundingMode.HALF_UP); + vo.setFuelSaved(fuelSaved.toPlainString()); + BigDecimal coalSaved = totalElecKwh.multiply(new BigDecimal("0.000404")) + .setScale(2, RoundingMode.HALF_UP); + vo.setStandardCoalSaved(coalSaved.toPlainString()); + } + + private static Long multiply(Long value, BigDecimal multiplier) { + return value == null ? null : new BigDecimal(value).multiply(multiplier).longValue(); + } + + private static String multiply(String value, BigDecimal multiplier) { + return value == null ? null : new BigDecimal(value).multiply(multiplier).toString(); + } +}