update 首页数据大屏

This commit is contained in:
Lemon
2026-05-26 16:19:19 +08:00
parent 52b1535216
commit 05699d5686
15 changed files with 483 additions and 106 deletions

View File

@@ -10,14 +10,28 @@ 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
@@ -43,6 +57,15 @@ public class indexController extends BaseController {
@Autowired
private PileConnectorInfoService pileConnectorInfoService;
@Autowired
private PileMerchantInfoService pileMerchantInfoService;
@Autowired
private BusinessFinancialService businessFinancialService;
@Autowired
private Executor threadPoolTaskExecutor;
@PostMapping("/getGeneralSituation")
public RestApiResponse<?> getGeneralSituation(@RequestBody IndexQueryDTO dto) {
logger.info("首页基础数据查询 param:{}", JSON.toJSONString(dto));
@@ -117,7 +140,8 @@ public class indexController extends BaseController {
}
/**
* 大数据平台-总览数据
* 大数据平台-总览数据(性能优化版)
* 权限解析在主线程完成按merchantId或stationId维度查询不用SQL SUMJava汇总
*/
@PostMapping("/getBigDataOverview")
public RestApiResponse<?> getBigDataOverview(@RequestBody(required = false) IndexQueryDTO dto) {
@@ -126,47 +150,145 @@ public class indexController extends BaseController {
RestApiResponse<?> response;
try {
BigDataOverviewVO overviewVO = new BigDataOverviewVO();
overviewVO.setTotalUsers(memberBasicInfoService.countTotalMembers());
overviewVO.setTotalOrders(orderBasicInfoService.countTotalOrders());
IndexGeneralSituationVO generalSituation = pileBasicInfoService.getGeneralSituation(dto);
overviewVO.setTotalTransactionAmount(generalSituation.getTotalChargingAmount());
overviewVO.setTotalElectricity(generalSituation.getTotalChargingDegree());
overviewVO.setTotalPiles(Long.valueOf(generalSituation.getTotalPileQuantity()));
overviewVO.setTotalStations(pileStationInfoService.countTotalStations());
// 新增字段
overviewVO.setDailyNewUsers(memberBasicInfoService.countTodayNewMembers());
java.math.BigDecimal todayAmount = pileBasicInfoService.getTodayTransactionAmount();
// === 第一步主线程做权限解析依赖SecurityContext不可异步 ===
// 平台管理员merchantIdList为空直接查全量不加任何过滤
// 运营商管理员使用merchantId维度
// 站点管理员使用stationId维度
AuthParams authParams = resolveAuthParams();
// demo账号标记
boolean isDemo = false;
try {
isDemo = "demo".equals(SecurityUtils.getUsername());
} catch (Exception ignored) {}
// === 第二步所有DB查询并行执行 ===
final List<String> finalMerchantIdList = authParams.merchantIdList;
final List<String> finalStationIdList = authParams.stationIdList;
final boolean isPlatformAdmin = authParams.isPlatformAdmin;
// 带权限过滤的查询:根据账号级别选择维度
CompletableFuture<IndexGeneralSituationVO> situationFuture;
CompletableFuture<Long> totalPilesFuture;
if (isPlatformAdmin) {
// 平台管理员直接查全量不加merchant_id/station_id过滤
situationFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.aggregateReportByMerchantIds(null), threadPoolTaskExecutor);
totalPilesFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.countTotalPilesByMerchantIds(null), threadPoolTaskExecutor);
} else if (!CollectionUtils.isEmpty(finalMerchantIdList)) {
// 运营商管理员按merchantId查
final List<String> midList = finalMerchantIdList;
situationFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.aggregateReportByMerchantIds(midList), threadPoolTaskExecutor);
totalPilesFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.countTotalPilesByMerchantIds(midList), threadPoolTaskExecutor);
} else {
// 站点管理员按stationId查
final List<String> sidList = finalStationIdList;
situationFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.aggregateReportByStationIds(sidList), threadPoolTaskExecutor);
totalPilesFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.countTotalPilesByStationIds(sidList), threadPoolTaskExecutor);
}
// 不需要权限过滤的全局查询(日期在代码中生成,不在数据库中运算)
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<Long> totalUsersFuture = CompletableFuture.supplyAsync(
() -> memberBasicInfoService.countTotalMembers(), threadPoolTaskExecutor);
CompletableFuture<Long> totalStationsFuture = CompletableFuture.supplyAsync(
() -> pileStationInfoService.countTotalStations(), threadPoolTaskExecutor);
CompletableFuture<Long> dailyNewUsersFuture = CompletableFuture.supplyAsync(
() -> memberBasicInfoService.countTodayNewMembers(fTodayStart, fTodayEnd), threadPoolTaskExecutor);
CompletableFuture<BigDecimal> todayAmountFuture = CompletableFuture.supplyAsync(
() -> orderBasicInfoService.getTodayTransactionAmount(fTodayStart, fTodayEnd), threadPoolTaskExecutor);
CompletableFuture<BigDecimal> todayElecFuture = CompletableFuture.supplyAsync(
() -> orderBasicInfoService.getTodayElectricity(fTodayStart, fTodayEnd), threadPoolTaskExecutor);
CompletableFuture<BigDecimal> monthlyAvgFuture = CompletableFuture.supplyAsync(
() -> pileBasicInfoService.getMonthlyAvgElectricity(fMonthStart, fMonthEnd), threadPoolTaskExecutor);
CompletableFuture<Long> totalGunsFuture = CompletableFuture.supplyAsync(
() -> pileConnectorInfoService.countTotalConnectors(), threadPoolTaskExecutor);
CompletableFuture<Long> onlinePilesFuture = CompletableFuture.supplyAsync(
() -> pileConnectorInfoService.countOnlinePiles(), threadPoolTaskExecutor);
CompletableFuture<Long> dcPilesFuture = CompletableFuture.supplyAsync(
() -> pileConnectorInfoService.countDcPiles(), threadPoolTaskExecutor);
CompletableFuture<Long> acPilesFuture = CompletableFuture.supplyAsync(
() -> pileConnectorInfoService.countAcPiles(), threadPoolTaskExecutor);
// 等待所有异步查询完成
CompletableFuture.allOf(
situationFuture, totalPilesFuture,
totalUsersFuture, totalStationsFuture, dailyNewUsersFuture,
todayAmountFuture, todayElecFuture, monthlyAvgFuture,
totalGunsFuture, onlinePilesFuture, dcPilesFuture, acPilesFuture
).join();
// === 第三步:组装结果 ===
IndexGeneralSituationVO situation = situationFuture.get();
String totalChargingAmount = situation.getTotalChargingAmount();
String totalChargingDegree = situation.getTotalChargingDegree();
Long totalPileCount = totalPilesFuture.get();
// 总订单数从settle_order_report的GROUP BY结果中获取charge_num汇总
String totalOrderCountStr = situation.getTotalChargingQuantity();
Long totalOrderCount = (totalOrderCountStr != null && !totalOrderCountStr.isEmpty())
? Long.parseLong(totalOrderCountStr.replaceAll("[.](.*)", "")) : 0L;
// demo账号数据放大3倍
if (isDemo) {
BigDecimal multiplier = new BigDecimal(Constants.THREE);
totalChargingAmount = new BigDecimal(situation.getTotalSettleAmount()).multiply(multiplier).toString();
totalChargingDegree = new BigDecimal(totalChargingDegree).multiply(multiplier).toString();
totalPileCount = totalPileCount != null ? totalPileCount * 3 : 0L;
totalOrderCount = totalOrderCount * 3;
}
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");
java.math.BigDecimal todayElec = pileBasicInfoService.getTodayElectricity();
BigDecimal todayElec = todayElecFuture.get();
overviewVO.setTodayElectricity(todayElec != null ? todayElec.toPlainString() : "0");
java.math.BigDecimal monthlyAvg = pileBasicInfoService.getMonthlyAvgElectricity();
BigDecimal monthlyAvg = monthlyAvgFuture.get();
overviewVO.setMonthlyAvgElectricity(monthlyAvg != null ? monthlyAvg.toPlainString() : "0");
overviewVO.setTotalGuns(pileConnectorInfoService.countTotalConnectors());
overviewVO.setOnlinePiles(pileConnectorInfoService.countOnlinePiles());
overviewVO.setOnlineStations(pileConnectorInfoService.countDcPiles());
overviewVO.setOnlineGuns(pileConnectorInfoService.countAcPiles());
overviewVO.setTotalGuns(totalGunsFuture.get());
overviewVO.setOnlinePiles(onlinePilesFuture.get());
overviewVO.setOnlineStations(dcPilesFuture.get());
overviewVO.setOnlineGuns(acPilesFuture.get());
// 节能减排计算
java.math.BigDecimal totalElecKwh = new java.math.BigDecimal(generalSituation.getTotalChargingDegree());
// 累计碳减排 (吨) = 累计充电量(kWh) × 0.5306 / 1000
java.math.BigDecimal carbonKg = totalElecKwh.multiply(new java.math.BigDecimal("0.5306"));
java.math.BigDecimal carbonTon = carbonKg.divide(new java.math.BigDecimal("1000"), 2, java.math.RoundingMode.HALF_UP);
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());
// 单次充电平均减碳量 (kg) = 累计碳减排量(kg) / 总订单数
Long totalOrderCount = overviewVO.getTotalOrders();
if (totalOrderCount != null && totalOrderCount > 0) {
java.math.BigDecimal avgCarbon = carbonKg.divide(new java.math.BigDecimal(totalOrderCount), 2, java.math.RoundingMode.HALF_UP);
BigDecimal avgCarbon = carbonKg.divide(new BigDecimal(totalOrderCount), 2, RoundingMode.HALF_UP);
overviewVO.setAvgCarbonPerOrder(avgCarbon.toPlainString());
} else {
overviewVO.setAvgCarbonPerOrder("0");
}
// 节约燃油 (升) = 累计充电量(kWh) × 8 / 15
java.math.BigDecimal fuelSaved = totalElecKwh.multiply(new java.math.BigDecimal("8"))
.divide(new java.math.BigDecimal("15"), 2, java.math.RoundingMode.HALF_UP);
BigDecimal fuelSaved = totalElecKwh.multiply(new BigDecimal("8"))
.divide(new BigDecimal("15"), 2, RoundingMode.HALF_UP);
overviewVO.setFuelSaved(fuelSaved.toPlainString());
// 相当于替代标准煤 (吨) = 累计充电量(kWh) × 0.000404
java.math.BigDecimal coalSaved = totalElecKwh.multiply(new java.math.BigDecimal("0.000404"))
.setScale(2, java.math.RoundingMode.HALF_UP);
BigDecimal coalSaved = totalElecKwh.multiply(new BigDecimal("0.000404"))
.setScale(2, RoundingMode.HALF_UP);
overviewVO.setStandardCoalSaved(coalSaved.toPlainString());
response = new RestApiResponse<>(overviewVO);
} catch (Exception e) {
logger.error("大数据平台总览数据查询错误", e);
@@ -176,6 +298,51 @@ public class indexController extends BaseController {
return response;
}
/**
* 权限参数内部类
*/
private static class AuthParams {
boolean isPlatformAdmin = false;
List<String> merchantIdList = new ArrayList<>();
List<String> 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<String> stationDeptIds = authorizedMap.getStationDeptIds();
List<String> merchantDeptIds = authorizedMap.getMerchantDeptIds();
if (!CollectionUtils.isEmpty(stationDeptIds)) {
// 站点管理员:使用站点维度
List<String> 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;
}
/**
* 大数据平台-充电站地图数据
*/
@@ -190,7 +357,7 @@ public class indexController extends BaseController {
logger.error("大数据平台充电站地图数据查询错误", e);
response = new RestApiResponse<>("00200006", "大数据平台充电站地图数据查询错误");
}
logger.info("大数据平台充电站地图数据查询 result:{}", JSON.toJSONString(response));
// logger.info("大数据平台充电站地图数据查询 result:{}", JSON.toJSONString(response));
return response;
}
@@ -255,4 +422,24 @@ public class indexController extends BaseController {
return response;
}
/**
* 大数据平台-站点订单排行前10条
*/
@PostMapping("/getStationOrderRank")
public RestApiResponse<?> getStationOrderRank() {
logger.info("大数据平台站点订单排行查询");
RestApiResponse<?> response;
try {
BusinessOperationAnalysisQueryDTO dto = new BusinessOperationAnalysisQueryDTO();
dto.setPageNum(1);
dto.setPageSize(10);
com.jsowell.common.core.page.PageResponse pageResponse = businessFinancialService.getStationOrderRank(dto);
response = new RestApiResponse<>(pageResponse);
} catch (Exception e) {
logger.error("大数据平台站点订单排行查询错误", e);
response = new RestApiResponse<>("00200010", "站点订单排行查询错误");
}
return response;
}
}