Merge branch 'feature-business-minigram' into dev

This commit is contained in:
Lemon
2026-04-10 14:40:37 +08:00
16 changed files with 1433 additions and 25 deletions

View File

@@ -8,7 +8,12 @@ import com.jsowell.common.exception.BusinessException;
import com.jsowell.common.response.RestApiResponse;
import com.jsowell.pile.dto.MerchantOrderReportDTO;
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
import com.jsowell.pile.service.BusinessFinancialService;
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -87,4 +92,60 @@ public class BusinessFinancialController extends BaseController {
}
return response;
}
/**
* 查询经营分析
*
* @param dto 查询参数
* @return 经营分析
*/
@PostMapping("/operationAnalysis")
public RestApiResponse<?> getBusinessOperationAnalysis(@RequestBody BusinessOperationAnalysisQueryDTO dto) {
logger.info("查询经营分析 params:{}", JSONObject.toJSONString(dto));
RestApiResponse<?> response;
try {
if (dto == null) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
BusinessOperationAnalysisVO result = businessFinancialService.getBusinessOperationAnalysis(dto);
response = new RestApiResponse<>(result);
logger.info("查询经营分析成功 startTime:{}, endTime:{}, selectedMetricCode:{}",
dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode());
} catch (BusinessException e) {
logger.warn("查询经营分析业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e);
response = new RestApiResponse<>(e.getCode(), e.getMessage());
} catch (Exception e) {
logger.error("查询经营分析系统异常 params:{}", JSONObject.toJSONString(dto), e);
response = new RestApiResponse<>(e);
}
return response;
}
/**
* 查询经营规模
*
* @param dto 查询参数
* @return 经营规模数据(含指标卡片和曲线图)
*/
@PostMapping("/businessScale")
public RestApiResponse<?> getBusinessScale(@RequestBody BusinessScaleQueryDTO dto) {
logger.info("查询经营规模 params:{}", JSONObject.toJSONString(dto));
RestApiResponse<?> response;
try {
if (dto == null) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
BusinessScaleVO result = businessFinancialService.getBusinessScale(dto);
response = new RestApiResponse<>(result);
logger.info("查询经营规模成功 startTime:{}, endTime:{}, selectedMetricCode:{}",
dto.getStartTime(), dto.getEndTime(), dto.getSelectedMetricCode());
} catch (BusinessException e) {
logger.warn("查询经营规模业务异常 code:{}, message:{}", e.getCode(), e.getMessage(), e);
response = new RestApiResponse<>(e.getCode(), e.getMessage());
} catch (Exception e) {
logger.error("查询经营规模系统异常 params:{}", JSONObject.toJSONString(dto), e);
response = new RestApiResponse<>(e);
}
return response;
}
}

View File

@@ -38,7 +38,7 @@ spring:
master:
# url: jdbc:mysql://106.14.94.149:3306/jsowell_pre?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
# username: jsowell_pre
url: jdbc:mysql://192.168.0.4:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
url: jdbc:mysql://192.168.0.32:3306/jsowell_dev?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
username: jsowell_dev
password: 123456
# 从库数据源

View File

@@ -0,0 +1,100 @@
package com.jsowell.common.util;
import java.math.BigDecimal;
import java.math.RoundingMode;
/**
* BigDecimal工具类
*
* @author Auto
* @Date 2026/4/1
*/
public class BigDecimalUtils {
/**
* 工具类不允许实例化
*/
private BigDecimalUtils() {
}
/**
* 空值转零
*
* @param value 原始值
* @return 非空数值
*/
public static BigDecimal nullToZero(BigDecimal value) {
return value == null ? BigDecimal.ZERO : value;
}
/**
* 安全除法
*
* @param dividend 被除数
* @param divisor 除数
* @param scale 保留小数位
* @return 计算结果
*/
public static BigDecimal safeDivide(BigDecimal dividend, BigDecimal divisor, int scale) {
BigDecimal safeDividend = nullToZero(dividend);
BigDecimal safeDivisor = nullToZero(divisor);
if (safeDivisor.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
return safeDividend.divide(safeDivisor, scale, RoundingMode.HALF_UP);
}
/**
* 标准化数值显示
*
* @param value 原始值
* @return 标准化后的数值
*/
public static BigDecimal normalize(BigDecimal value) {
BigDecimal safeValue = nullToZero(value);
if (safeValue.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
return safeValue.stripTrailingZeros();
}
/**
* 将字符串安全转换为BigDecimal
*
* @param value 原始字符串
* @return BigDecimal值
*/
public static BigDecimal parseOrZero(String value) {
if (StringUtils.isBlank(value)) {
return BigDecimal.ZERO;
}
try {
return new BigDecimal(value.trim());
} catch (Exception e) {
return BigDecimal.ZERO;
}
}
/**
* 计算环比百分比
*
* @param currentValue 当前周期值
* @param previousValue 上周期值
* @return 环比字符串
*/
public static String calculateRate(BigDecimal currentValue, BigDecimal previousValue) {
BigDecimal safeCurrentValue = nullToZero(currentValue);
BigDecimal safePreviousValue = nullToZero(previousValue);
if (safePreviousValue.compareTo(BigDecimal.ZERO) == 0) {
return "0%";
}
BigDecimal rate = safeCurrentValue.subtract(safePreviousValue)
.divide(safePreviousValue, 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
BigDecimal normalizedRate = normalize(rate);
if (normalizedRate.compareTo(BigDecimal.ZERO) > 0) {
return "+" + normalizedRate.toPlainString() + "%";
}
return normalizedRate.toPlainString() + "%";
}
}

View File

@@ -0,0 +1,41 @@
package com.jsowell.pile.dto.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 经营状态/经营诊断查询DTO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessOperationAnalysisQueryDTO {
/**
* 开始时间格式yyyy-MM-dd
*/
private String startTime;
/**
* 结束时间格式yyyy-MM-dd
*/
private String endTime;
/**
* 站点id列表
*/
private List<String> stationIdList;
/**
* 当前选中的指标编码
*/
private String selectedMetricCode;
}

View File

@@ -0,0 +1,39 @@
package com.jsowell.pile.dto.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 经营分析日期区间DTO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessOperationDateRangeDTO {
/**
* 当前周期开始时间
*/
private String currentStart;
/**
* 当前周期结束时间
*/
private String currentEnd;
/**
* 上周期开始时间
*/
private String previousStart;
/**
* 上周期结束时间
*/
private String previousEnd;
}

View File

@@ -0,0 +1,55 @@
package com.jsowell.pile.dto.business;
import lombok.Data;
import java.math.BigDecimal;
/**
* 经营分析汇总DTO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
public class BusinessOperationSummaryDTO {
/**
* 订单总额
*/
private BigDecimal orderAmount = BigDecimal.ZERO;
/**
* 服务费总额
*/
private BigDecimal serviceAmount = BigDecimal.ZERO;
/**
* 总用电度数
*/
private BigDecimal useElectricity = BigDecimal.ZERO;
/**
* 订单量
*/
private BigDecimal orderCount = BigDecimal.ZERO;
/**
* 总充电时长
*/
private BigDecimal chargeTime = BigDecimal.ZERO;
/**
* 单均服务费
*/
private BigDecimal avgServiceFee = BigDecimal.ZERO;
/**
* 单均度数
*/
private BigDecimal avgElectricity = BigDecimal.ZERO;
/**
* 单均充电时长
*/
private BigDecimal avgChargeTime = BigDecimal.ZERO;
}

View File

@@ -0,0 +1,43 @@
package com.jsowell.pile.dto.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 经营规模查询DTO
*
* @author zhangziao
* @date 2026/4/10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessScaleQueryDTO {
/**
* 开始时间格式yyyy-MM-dd
*/
private String startTime;
/**
* 结束时间格式yyyy-MM-dd
*/
private String endTime;
/**
* 站点id列表
*/
private List<String> stationIdList;
/**
* 当前选中的指标编码,用于查询对应的曲线图数据
* ORDER_AMOUNT-订单总额, ORDER_COUNT-充电订单量,
* SERVICE_AMOUNT-充电服务费, USE_ELECTRICITY-充电电量
*/
private String selectedMetricCode;
}

View File

@@ -3,6 +3,11 @@ package com.jsowell.pile.service;
import com.jsowell.common.core.page.PageResponse;
import com.jsowell.pile.dto.MerchantOrderReportDTO;
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
@@ -16,4 +21,28 @@ public interface BusinessFinancialService {
* @return 分页结果
*/
PageResponse queryParkingCouponRecords(ParkingCouponRecordQueryDTO dto);
/**
* 查询经营分析
*
* @param dto 查询条件
* @return 经营分析
*/
BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto);
/**
* 查询枪均效率分析
*
* @param dto 查询条件
* @return 枪均效率分析
*/
BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto);
/**
* 查询经营规模
*
* @param dto 查询条件
* @return 经营规模数据(含指标卡片和曲线图)
*/
BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto);
}

View File

@@ -5,24 +5,54 @@ import com.github.pagehelper.PageInfo;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.jsowell.adapay.service.AdapayService;
import com.jsowell.adapay.vo.AdapayAccountBalanceVO;
import com.jsowell.common.util.BigDecimalUtils;
import com.jsowell.common.core.domain.vo.AuthorizedDeptVO;
import com.jsowell.common.core.page.PageResponse;
import com.jsowell.common.enums.ykc.ReturnCodeEnum;
import com.jsowell.common.exception.BusinessException;
import com.jsowell.pile.dto.MerchantOrderReportDTO;
import com.jsowell.pile.dto.ParkingCouponRecordQueryDTO;
import com.jsowell.pile.dto.business.BusinessOperationAnalysisQueryDTO;
import com.jsowell.pile.dto.business.BusinessOperationDateRangeDTO;
import com.jsowell.pile.dto.business.BusinessOperationSummaryDTO;
import com.jsowell.pile.dto.business.BusinessScaleQueryDTO;
import com.jsowell.pile.domain.PileConnectorInfo;
import com.jsowell.pile.domain.SettleOrderReport;
import com.jsowell.pile.service.BusinessFinancialService;
import com.jsowell.pile.service.CarCouponRecordService;
import com.jsowell.pile.service.ClearingWithdrawInfoService;
import com.jsowell.pile.service.PileConnectorInfoService;
import com.jsowell.pile.service.PileStationInfoService;
import com.jsowell.pile.service.SettleOrderReportService;
import com.jsowell.pile.util.UserUtils;
import com.jsowell.pile.vo.uniapp.business.BusinessGunEfficiencyAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessOperationAnalysisVO;
import com.jsowell.pile.vo.uniapp.business.BusinessOperationDiagnosisItemVO;
import com.jsowell.pile.vo.uniapp.business.BusinessOperationMetricVO;
import com.jsowell.pile.vo.uniapp.business.BusinessScaleChartVO;
import com.jsowell.pile.vo.uniapp.business.BusinessScaleMetricVO;
import com.jsowell.pile.vo.uniapp.business.BusinessScaleVO;
import com.jsowell.pile.vo.web.MerchantOrderReportVO;
import com.jsowell.pile.vo.web.ParkingCouponRecordVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
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.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
/**
* 运营端小程序财务相关Service
@@ -31,6 +61,30 @@ import java.util.List;
@Service
public class BusinessFinancialServiceImpl implements BusinessFinancialService {
private static final String METRIC_ORDER_AMOUNT = "ORDER_AMOUNT";
private static final String METRIC_AVG_SERVICE_FEE = "AVG_SERVICE_FEE";
private static final String METRIC_AVG_ELECTRICITY = "AVG_ELECTRICITY";
private static final String FACTOR_ORDER_COUNT = "ORDER_COUNT";
private static final String FACTOR_AVG_ELECTRICITY = "AVG_ELECTRICITY";
private static final String FACTOR_AVG_CHARGE_TIME = "AVG_CHARGE_TIME";
private static final String SCALE_METRIC_ORDER_AMOUNT = "ORDER_AMOUNT";
private static final String SCALE_METRIC_ORDER_COUNT = "ORDER_COUNT";
private static final String SCALE_METRIC_SERVICE_AMOUNT = "SERVICE_AMOUNT";
private static final String SCALE_METRIC_USE_ELECTRICITY = "USE_ELECTRICITY";
/**
* 财务查询通用线程池(支持多方法并行查询)
* 核心线程数4覆盖经营规模(2路)、我的钱包(3路)、经营分析(2路)、枪均效率(2路)等场景
*/
private static final ExecutorService FINANCIAL_THREAD_POOL = Executors.newFixedThreadPool(
4, r -> {
Thread t = new Thread(r, "business-financial-query");
t.setDaemon(true);
return t;
});
@Autowired
private SettleOrderReportService settleOrderReportService;
@@ -46,49 +100,63 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
@Autowired
private PileStationInfoService pileStationInfoService;
@Autowired
private PileConnectorInfoService pileConnectorInfoService;
/**
* 我的钱包查询
* 优化订单报表DB、汇付余额HTTP、提现金额DB 三个独立数据源并行查询
* @param dto
* @return
*/
@Override
public MerchantOrderReportVO getMyWallet(MerchantOrderReportDTO dto) {
// 查询运营商订单报表
MerchantOrderReportVO result = settleOrderReportService.getMerchantOrderReportV2(dto);
String merchantId = dto.getMerchantId();
// 查询账户余额
AdapayAccountBalanceVO accountBalanceVO = new AdapayAccountBalanceVO();
// 并行查询三个独立数据源
CompletableFuture<MerchantOrderReportVO> reportFuture = CompletableFuture.supplyAsync(
() -> settleOrderReportService.getMerchantOrderReportV2(dto), FINANCIAL_THREAD_POOL);
CompletableFuture<AdapayAccountBalanceVO> balanceFuture = CompletableFuture.supplyAsync(() -> {
try {
return adapayService.queryAdapayAccountBalance(merchantId);
} catch (BaseAdaPayException e) {
log.error("查询汇付账户余额异常 merchantId:{}", merchantId, e);
return null;
}
}, FINANCIAL_THREAD_POOL);
CompletableFuture<BigDecimal> withdrawFuture = CompletableFuture.supplyAsync(() -> {
try {
BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(merchantId);
return withdraw != null ? withdraw : BigDecimal.ZERO;
} catch (Exception e) {
log.error("查询累计提现金额异常 merchantId:{}", merchantId, e);
return BigDecimal.ZERO;
}
}, FINANCIAL_THREAD_POOL);
// 等待所有查询完成
MerchantOrderReportVO result = reportFuture.join();
AdapayAccountBalanceVO accountBalanceVO = balanceFuture.join();
BigDecimal totalWithdraw = withdrawFuture.join();
// 设置账户余额
BigDecimal acctBalance = BigDecimal.ZERO;
BigDecimal pendingAmount = BigDecimal.ZERO;
try {
accountBalanceVO = adapayService.queryAdapayAccountBalance(dto.getMerchantId());
if (accountBalanceVO == null) {
log.error("我的钱包查询异常 查询出accountBalanceVO 为null");
return result;
}
BigDecimal canWithdrawAmount = null;
if (accountBalanceVO != null) {
if (accountBalanceVO.getAcctBalance() != null) {
acctBalance = accountBalanceVO.getAcctBalance();
}
if (accountBalanceVO.getPendingAmount() != null) {
pendingAmount = accountBalanceVO.getPendingAmount();
}
} catch (BaseAdaPayException e) {
log.error("查询汇付账户余额异常 merchantId:{}", dto.getMerchantId(), e);
canWithdrawAmount = accountBalanceVO.getLastAvlBalance();
} else {
log.error("我的钱包查询异常 查询出accountBalanceVO 为null");
}
result.getMerchantOrderReport().setAcctBalance(acctBalance);
result.getMerchantOrderReport().setPendingAmount(pendingAmount);
result.getMerchantOrderReport().setCanWithdrawAmount(accountBalanceVO.getLastAvlBalance());
// 查询累计提现金额
BigDecimal totalWithdraw = BigDecimal.ZERO;
try {
BigDecimal withdraw = clearingWithdrawInfoService.queryTotalWithdraw(dto.getMerchantId());
if (withdraw != null) {
totalWithdraw = withdraw;
}
} catch (Exception e) {
log.error("查询累计提现金额异常 merchantId:{}", dto.getMerchantId(), e);
}
result.getMerchantOrderReport().setCanWithdrawAmount(canWithdrawAmount);
result.getMerchantOrderReport().setTotalWithdraw(totalWithdraw);
return result;
@@ -148,4 +216,573 @@ public class BusinessFinancialServiceImpl implements BusinessFinancialService {
.total(pageInfo.getTotal())
.build();
}
/**
* 查询经营分析
* 优化:当前周期和上周期汇总并行查询
*
* @param dto 查询条件
* @return 经营分析
*/
@Override
public BusinessOperationAnalysisVO getBusinessOperationAnalysis(BusinessOperationAnalysisQueryDTO dto) {
if (dto == null) {
return null;
}
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusDays(6);
dto.setStartTime(startDate.toString());
dto.setEndTime(endDate.toString());
}
BusinessOperationDateRangeDTO range = buildDateRange(dto);
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
// 并行查询当前周期和上周期原始数据
CompletableFuture<List<SettleOrderReport>> currentReportFuture = CompletableFuture.supplyAsync(
() -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()),
FINANCIAL_THREAD_POOL);
CompletableFuture<List<SettleOrderReport>> previousReportFuture = CompletableFuture.supplyAsync(
() -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()),
FINANCIAL_THREAD_POOL);
BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReportFuture.join());
BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReportFuture.join());
List<BusinessOperationMetricVO> metricList = new ArrayList<>();
metricList.add(buildMetric(METRIC_ORDER_AMOUNT, "订单总额",
currentSummary.getOrderAmount(), previousSummary.getOrderAmount()));
metricList.add(buildMetric(METRIC_AVG_SERVICE_FEE, "单均服务费",
currentSummary.getAvgServiceFee(), previousSummary.getAvgServiceFee()));
metricList.add(buildMetric(METRIC_AVG_ELECTRICITY, "单均度数",
currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity()));
BusinessOperationDiagnosisItemVO diagnosisItem = buildSelectedDiagnosisItem(dto, currentSummary, previousSummary);
return BusinessOperationAnalysisVO.builder()
.currentStartTime(range.getCurrentStart())
.currentEndTime(range.getCurrentEnd())
.previousStartTime(range.getPreviousStart())
.previousEndTime(range.getPreviousEnd())
.metricList(metricList)
.diagnosisItem(diagnosisItem)
.build();
}
/**
* 查询枪均效率分析
* 优化订单汇总DB查询与枪口数量DB查询并行执行
*
* @param dto 查询条件
* @return 枪均效率分析
*/
@Override
public BusinessGunEfficiencyAnalysisVO getBusinessGunEfficiencyAnalysis(BusinessOperationAnalysisQueryDTO dto) {
if (dto == null) {
return null;
}
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusDays(6);
dto.setStartTime(startDate.toString());
dto.setEndTime(endDate.toString());
}
BusinessOperationDateRangeDTO range = buildDateRange(dto);
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
// 并行查询订单汇总和枪口数量
CompletableFuture<BusinessOperationSummaryDTO> summaryFuture = CompletableFuture.supplyAsync(
() -> buildSummaryFromReports(
queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd())),
FINANCIAL_THREAD_POOL);
CompletableFuture<Integer> connectorCountFuture = CompletableFuture.supplyAsync(
() -> countConnectors(stationIdList), FINANCIAL_THREAD_POOL);
BusinessOperationSummaryDTO summary = summaryFuture.join();
int connectorCount = connectorCountFuture.join();
BigDecimal connectorCountBd = BigDecimal.valueOf(connectorCount);
BigDecimal dayCount = BigDecimal.valueOf(calculateInclusiveDays(range.getCurrentStart(), range.getCurrentEnd()));
BigDecimal connectorDayCount = connectorCountBd.multiply(dayCount);
BigDecimal gunAvgDailyServiceFee = BigDecimalUtils.safeDivide(summary.getServiceAmount(), connectorDayCount, 2);
BigDecimal gunAvgDailyElectricity = BigDecimalUtils.safeDivide(summary.getUseElectricity(), connectorDayCount, 4);
BigDecimal avgServiceFeePerDegree = BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getUseElectricity(), 4);
BigDecimal gunAvgDailyChargeTimeMinutes = BigDecimalUtils.safeDivide(summary.getChargeTime(), connectorDayCount, 2);
BigDecimal avgPower = BigDecimalUtils.safeDivide(
summary.getUseElectricity().multiply(new BigDecimal("60")),
summary.getChargeTime(),
4);
return BusinessGunEfficiencyAnalysisVO.builder()
.startTime(range.getCurrentStart())
.endTime(range.getCurrentEnd())
.gunAvgDailyServiceFee(BigDecimalUtils.normalize(gunAvgDailyServiceFee))
.gunAvgDailyElectricity(BigDecimalUtils.normalize(gunAvgDailyElectricity))
.avgServiceFeePerDegree(BigDecimalUtils.normalize(avgServiceFeePerDegree))
.gunAvgDailyChargeTime(formatMinutesToHourMinute(gunAvgDailyChargeTimeMinutes))
.avgPower(BigDecimalUtils.normalize(avgPower))
.build();
}
/**
* 构建当前选中指标对应的最大影响因素
*
* @param dto 查询条件
* @param currentSummary 当前周期汇总
* @param previousSummary 上周期汇总
* @return 经营诊断指标
*/
private BusinessOperationDiagnosisItemVO buildSelectedDiagnosisItem(BusinessOperationAnalysisQueryDTO dto,
BusinessOperationSummaryDTO currentSummary,
BusinessOperationSummaryDTO previousSummary) {
String selectedMetricCode = dto.getSelectedMetricCode();
if (METRIC_AVG_SERVICE_FEE.equals(selectedMetricCode)) {
return buildDiagnosis(METRIC_AVG_SERVICE_FEE, "单均服务费",
FACTOR_AVG_ELECTRICITY, "单均度数",
currentSummary.getAvgElectricity(), previousSummary.getAvgElectricity());
}
if (METRIC_AVG_ELECTRICITY.equals(selectedMetricCode)) {
return buildDiagnosis(METRIC_AVG_ELECTRICITY, "单均度数",
FACTOR_AVG_CHARGE_TIME, "单均充电时长",
currentSummary.getAvgChargeTime(), previousSummary.getAvgChargeTime());
}
return buildDiagnosis(METRIC_ORDER_AMOUNT, "订单总数",
FACTOR_ORDER_COUNT, "订单量",
currentSummary.getOrderCount(), previousSummary.getOrderCount());
}
/**
* 构建经营状态指标项
*
* @param metricCode 指标编码
* @param metricName 指标名称
* @param currentValue 当前周期值
* @param previousValue 上周期值
* @return 经营状态指标
*/
private BusinessOperationMetricVO buildMetric(String metricCode, String metricName,
BigDecimal currentValue, BigDecimal previousValue) {
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
return BusinessOperationMetricVO.builder()
.metricCode(metricCode)
.metricName(metricName)
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
.changeValue(changeValue)
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
.build();
}
/**
* 构建经营诊断指标项
*
* @param metricCode 指标编码
* @param metricName 指标名称
* @param factorCode 影响因素编码
* @param factorName 影响因素名称
* @param currentValue 当前周期值
* @param previousValue 上周期值
* @return 经营诊断指标
*/
private BusinessOperationDiagnosisItemVO buildDiagnosis(String metricCode, String metricName,
String factorCode, String factorName,
BigDecimal currentValue, BigDecimal previousValue) {
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
return BusinessOperationDiagnosisItemVO.builder()
.metricCode(metricCode)
.metricName(metricName)
.factorCode(factorCode)
.factorName(factorName)
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
.changeValue(changeValue)
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
.build();
}
/**
* 根据查询条件构建当前周期和上周期日期区间
*
* @param dto 查询条件
* @return 日期区间
*/
private BusinessOperationDateRangeDTO buildDateRange(BusinessOperationAnalysisQueryDTO dto) {
try {
LocalDate currentStart = LocalDate.parse(dto.getStartTime());
LocalDate currentEnd = LocalDate.parse(dto.getEndTime());
if (currentEnd.isBefore(currentStart)) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
long days = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
LocalDate previousEnd = currentStart.minusDays(1);
LocalDate previousStart = previousEnd.minusDays(days - 1);
return BusinessOperationDateRangeDTO.builder()
.currentStart(currentStart.toString())
.currentEnd(currentEnd.toString())
.previousStart(previousStart.toString())
.previousEnd(previousEnd.toString())
.build();
} catch (BusinessException e) {
throw e;
} catch (Exception e) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
}
/**
* 解析本次查询可用的站点ID列表
*
* @param stationIdList 前端传入站点ID列表
* @return 实际查询站点ID列表
*/
private List<String> resolveStationIds(List<String> stationIdList) {
List<String> authorizedStationIds = getAuthorizedStationIds();
if (authorizedStationIds == null) {
return CollectionUtils.isEmpty(stationIdList) ? null : stationIdList;
}
if (!CollectionUtils.isEmpty(stationIdList)) {
return stationIdList.stream()
.filter(authorizedStationIds::contains)
.distinct()
.collect(Collectors.toList());
}
return authorizedStationIds;
}
/**
* 获取当前账号可访问的站点ID列表平台账号返回null表示不限制
*
* @return 站点ID列表
*/
private List<String> getAuthorizedStationIds() {
try {
AuthorizedDeptVO authorizedMap = UserUtils.getAuthorizedMap();
if (authorizedMap == null) {
return null;
}
if (!CollectionUtils.isEmpty(authorizedMap.getStationDeptIds())) {
return pileStationInfoService.queryByStationDeptIds(authorizedMap.getStationDeptIds());
}
if (!CollectionUtils.isEmpty(authorizedMap.getMerchantDeptIds())) {
return pileStationInfoService.getStationIdsByMerchantIds(authorizedMap.getMerchantDeptIds());
}
} catch (Exception e) {
log.warn("解析经营分析站点权限失败", e);
}
return null;
}
/**
* 统计站点范围内的枪口数量
*
* @param stationIdList 站点ID列表
* @return 枪口数量
*/
private int countConnectors(List<String> stationIdList) {
if (stationIdList == null) {
return pileConnectorInfoService.selectPileConnectorInfoList(new PileConnectorInfo()).size();
}
if (stationIdList.isEmpty()) {
return 0;
}
return pileConnectorInfoService.batchSelectConnectorList(stationIdList).size();
}
/**
* 计算包含起止日期在内的天数
*
* @param startTime 开始时间
* @param endTime 结束时间
* @return 天数
*/
private long calculateInclusiveDays(String startTime, String endTime) {
LocalDate startDate = LocalDate.parse(startTime);
LocalDate endDate = LocalDate.parse(endTime);
return ChronoUnit.DAYS.between(startDate, endDate) + 1;
}
/**
* 将分钟数转换为xxhxxm格式
*
* @param minutes 分钟数
* @return 格式化结果
*/
private String formatMinutesToHourMinute(BigDecimal minutes) {
long totalMinutes = BigDecimalUtils.nullToZero(minutes)
.setScale(0, RoundingMode.HALF_UP)
.longValue();
long hours = totalMinutes / 60;
long remainMinutes = totalMinutes % 60;
return String.format("%02dh%02dm", hours, remainMinutes);
}
/**
* 查询经营规模
*
* @param dto 查询条件
* @return 经营规模数据
*/
@Override
public BusinessScaleVO getBusinessScale(BusinessScaleQueryDTO dto) {
if (dto == null) {
return null;
}
if (StringUtils.isBlank(dto.getStartTime()) && StringUtils.isBlank(dto.getEndTime())) {
LocalDate endDate = LocalDate.now();
LocalDate startDate = endDate.minusDays(6);
dto.setStartTime(startDate.toString());
dto.setEndTime(endDate.toString());
}
BusinessOperationDateRangeDTO range = buildDateRangeFromScale(dto);
List<String> stationIdList = resolveStationIds(dto.getStationIdList());
// 并行查询当前周期和上周期的原始报表数据,各只查一次
CompletableFuture<List<SettleOrderReport>> currentReportFuture = CompletableFuture.supplyAsync(
() -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()),
FINANCIAL_THREAD_POOL);
CompletableFuture<List<SettleOrderReport>> previousReportFuture = CompletableFuture.supplyAsync(
() -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()),
FINANCIAL_THREAD_POOL);
// 等待两个查询完成
List<SettleOrderReport> currentReports = currentReportFuture.join();
List<SettleOrderReport> previousReports = previousReportFuture.join();
// 从同一份原始数据分别构建汇总指标和每日明细
BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReports);
BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReports);
Map<String, SettleOrderReport> currentDailyMap = buildDailyMapFromReports(currentReports);
Map<String, SettleOrderReport> previousDailyMap = buildDailyMapFromReports(previousReports);
// 构建指标卡片
List<BusinessScaleMetricVO> metricList = new ArrayList<>();
metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_AMOUNT, "订单总额", "",
currentSummary.getOrderAmount(), previousSummary.getOrderAmount()));
metricList.add(buildScaleMetric(SCALE_METRIC_ORDER_COUNT, "充电订单量", "",
currentSummary.getOrderCount(), previousSummary.getOrderCount()));
metricList.add(buildScaleMetric(SCALE_METRIC_SERVICE_AMOUNT, "充电服务费", "",
currentSummary.getServiceAmount(), previousSummary.getServiceAmount()));
metricList.add(buildScaleMetric(SCALE_METRIC_USE_ELECTRICITY, "充电电量", "",
currentSummary.getUseElectricity(), previousSummary.getUseElectricity()));
// 构建选中指标的曲线图数据复用已查询的dailyMap
String selectedMetricCode = StringUtils.isBlank(dto.getSelectedMetricCode())
? SCALE_METRIC_USE_ELECTRICITY : dto.getSelectedMetricCode();
BusinessScaleChartVO chartData = buildScaleChartFromMaps(
selectedMetricCode, range, currentDailyMap, previousDailyMap);
return BusinessScaleVO.builder()
.currentStartTime(range.getCurrentStart())
.currentEndTime(range.getCurrentEnd())
.previousStartTime(range.getPreviousStart())
.previousEndTime(range.getPreviousEnd())
.metricList(metricList)
.chartData(chartData)
.build();
}
/**
* 构建经营规模日期区间
*/
private BusinessOperationDateRangeDTO buildDateRangeFromScale(BusinessScaleQueryDTO dto) {
try {
LocalDate currentStart = LocalDate.parse(dto.getStartTime());
LocalDate currentEnd = LocalDate.parse(dto.getEndTime());
if (currentEnd.isBefore(currentStart)) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
long days = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
LocalDate previousEnd = currentStart.minusDays(1);
LocalDate previousStart = previousEnd.minusDays(days - 1);
return BusinessOperationDateRangeDTO.builder()
.currentStart(currentStart.toString())
.currentEnd(currentEnd.toString())
.previousStart(previousStart.toString())
.previousEnd(previousEnd.toString())
.build();
} catch (BusinessException e) {
throw e;
} catch (Exception e) {
throw new BusinessException(ReturnCodeEnum.CODE_PARAM_NOT_NULL_ERROR);
}
}
/**
* 构建经营规模指标卡片
*/
private BusinessScaleMetricVO buildScaleMetric(String metricCode, String metricName, String unit,
BigDecimal currentValue, BigDecimal previousValue) {
BigDecimal safeCurrentValue = BigDecimalUtils.nullToZero(currentValue);
BigDecimal safePreviousValue = BigDecimalUtils.nullToZero(previousValue);
BigDecimal changeValue = BigDecimalUtils.normalize(safeCurrentValue.subtract(safePreviousValue));
return BusinessScaleMetricVO.builder()
.metricCode(metricCode)
.metricName(metricName)
.unit(unit)
.currentValue(BigDecimalUtils.normalize(safeCurrentValue))
.previousValue(BigDecimalUtils.normalize(safePreviousValue))
.changeValue(changeValue)
.changeRate(BigDecimalUtils.calculateRate(safeCurrentValue, safePreviousValue))
.build();
}
/**
* 查询原始报表数据(供并行调用)
*/
private List<SettleOrderReport> queryRawReports(List<String> stationIdList,
String startTime, String endTime) {
if (stationIdList != null && stationIdList.isEmpty()) {
return new ArrayList<>();
}
List<SettleOrderReport> reportList = settleOrderReportService.queryOrderReport(
stationIdList, startTime, endTime);
return CollectionUtils.isEmpty(reportList) ? new ArrayList<>() : reportList;
}
/**
* 从原始报表数据构建汇总指标
*/
private BusinessOperationSummaryDTO buildSummaryFromReports(List<SettleOrderReport> reportList) {
BusinessOperationSummaryDTO summary = new BusinessOperationSummaryDTO();
if (CollectionUtils.isEmpty(reportList)) {
return summary;
}
for (SettleOrderReport report : reportList) {
summary.setOrderAmount(summary.getOrderAmount().add(BigDecimalUtils.nullToZero(report.getTotalAmount())));
summary.setServiceAmount(summary.getServiceAmount().add(BigDecimalUtils.nullToZero(report.getServiceAmount())));
summary.setUseElectricity(summary.getUseElectricity().add(BigDecimalUtils.nullToZero(report.getUseElectricity())));
summary.setOrderCount(summary.getOrderCount().add(BigDecimalUtils.parseOrZero(report.getChargeNum())));
summary.setChargeTime(summary.getChargeTime().add(BigDecimalUtils.parseOrZero(report.getChargeTime())));
}
summary.setAvgServiceFee(BigDecimalUtils.safeDivide(summary.getServiceAmount(), summary.getOrderCount(), 2));
summary.setAvgElectricity(BigDecimalUtils.safeDivide(summary.getUseElectricity(), summary.getOrderCount(), 2));
summary.setAvgChargeTime(BigDecimalUtils.safeDivide(summary.getChargeTime(), summary.getOrderCount(), 2));
return summary;
}
/**
* 从原始报表数据构建按日期分组的Map
*/
private Map<String, SettleOrderReport> buildDailyMapFromReports(List<SettleOrderReport> reportList) {
Map<String, SettleOrderReport> dailyMap = new LinkedHashMap<>();
if (CollectionUtils.isEmpty(reportList)) {
return dailyMap;
}
for (SettleOrderReport report : reportList) {
String tradeDate = report.getTradeDate();
if (StringUtils.isBlank(tradeDate)) {
continue;
}
SettleOrderReport existing = dailyMap.get(tradeDate);
if (existing == null) {
dailyMap.put(tradeDate, report);
} else {
existing.setTotalAmount(existing.getTotalAmount().add(
BigDecimalUtils.nullToZero(report.getTotalAmount())));
existing.setServiceAmount(existing.getServiceAmount().add(
BigDecimalUtils.nullToZero(report.getServiceAmount())));
existing.setUseElectricity(existing.getUseElectricity().add(
BigDecimalUtils.nullToZero(report.getUseElectricity())));
existing.setChargeNum(String.valueOf(
BigDecimalUtils.parseOrZero(existing.getChargeNum())
.add(BigDecimalUtils.parseOrZero(report.getChargeNum()))));
}
}
return dailyMap;
}
/**
* 从已有的dailyMap构建曲线图数据不再重复查DB
*/
private BusinessScaleChartVO buildScaleChartFromMaps(String metricCode,
BusinessOperationDateRangeDTO range,
Map<String, SettleOrderReport> currentDailyMap,
Map<String, SettleOrderReport> previousDailyMap) {
String metricName;
String unit;
switch (metricCode) {
case SCALE_METRIC_ORDER_AMOUNT:
metricName = "订单总额";
unit = "";
break;
case SCALE_METRIC_ORDER_COUNT:
metricName = "充电订单量";
unit = "";
break;
case SCALE_METRIC_SERVICE_AMOUNT:
metricName = "充电服务费";
unit = "";
break;
default:
metricName = "充电电量";
unit = "";
break;
}
LocalDate currentStart = LocalDate.parse(range.getCurrentStart());
LocalDate currentEnd = LocalDate.parse(range.getCurrentEnd());
LocalDate previousStart = LocalDate.parse(range.getPreviousStart());
long dayCount = ChronoUnit.DAYS.between(currentStart, currentEnd) + 1;
DateTimeFormatter displayFormatter = DateTimeFormatter.ofPattern("MM/dd");
List<BusinessScaleChartVO.ChartPoint> chartPoints = new ArrayList<>();
for (long i = 0; i < dayCount; i++) {
LocalDate currentDate = currentStart.plusDays(i);
LocalDate previousDate = previousStart.plusDays(i);
String dateKey = currentDate.toString();
SettleOrderReport currentReport = currentDailyMap.get(dateKey);
SettleOrderReport previousReport = previousDailyMap.get(previousDate.toString());
BigDecimal currentValue = extractMetricValue(metricCode, currentReport);
BigDecimal previousValue = extractMetricValue(metricCode, previousReport);
chartPoints.add(BusinessScaleChartVO.ChartPoint.builder()
.date(currentDate.format(displayFormatter))
.fullDate(dateKey)
.value(BigDecimalUtils.normalize(currentValue))
.previousValue(BigDecimalUtils.normalize(previousValue))
.changeRate(BigDecimalUtils.calculateRate(
BigDecimalUtils.nullToZero(currentValue),
BigDecimalUtils.nullToZero(previousValue)))
.build());
}
return BusinessScaleChartVO.builder()
.metricCode(metricCode)
.metricName(metricName)
.unit(unit)
.chartData(chartPoints)
.build();
}
/**
* 从报表记录中提取指定指标的值
*/
private BigDecimal extractMetricValue(String metricCode, SettleOrderReport report) {
if (report == null) {
return BigDecimal.ZERO;
}
switch (metricCode) {
case SCALE_METRIC_ORDER_AMOUNT:
return BigDecimalUtils.nullToZero(report.getTotalAmount());
case SCALE_METRIC_ORDER_COUNT:
return BigDecimalUtils.parseOrZero(report.getChargeNum());
case SCALE_METRIC_SERVICE_AMOUNT:
return BigDecimalUtils.nullToZero(report.getServiceAmount());
case SCALE_METRIC_USE_ELECTRICITY:
default:
return BigDecimalUtils.nullToZero(report.getUseElectricity());
}
}
}

View File

@@ -0,0 +1,56 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 枪均效率分析VO
*
* @author Auto
* @Date 2026/4/3
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessGunEfficiencyAnalysisVO {
/**
* 开始时间
*/
private String startTime;
/**
* 结束时间
*/
private String endTime;
/**
* 枪日均服务费(元)
*/
private BigDecimal gunAvgDailyServiceFee;
/**
* 枪日均充电量(度)
*/
private BigDecimal gunAvgDailyElectricity;
/**
* 度均服务费(元)
*/
private BigDecimal avgServiceFeePerDegree;
/**
* 枪日均充电时长格式xxhxxm
*/
private String gunAvgDailyChargeTime;
/**
* 平均功率(KW)
*/
private BigDecimal avgPower;
}

View File

@@ -0,0 +1,51 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 经营分析VO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessOperationAnalysisVO {
/**
* 当前周期开始时间
*/
private String currentStartTime;
/**
* 当前周期结束时间
*/
private String currentEndTime;
/**
* 上周期开始时间
*/
private String previousStartTime;
/**
* 上周期结束时间
*/
private String previousEndTime;
/**
* 顶部指标列表
*/
private List<BusinessOperationMetricVO> metricList;
/**
* 当前选中指标对应的最大影响因素
*/
private BusinessOperationDiagnosisItemVO diagnosisItem;
}

View File

@@ -0,0 +1,61 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 经营诊断项VO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessOperationDiagnosisItemVO {
/**
* 指标编码
*/
private String metricCode;
/**
* 指标名称
*/
private String metricName;
/**
* 最大影响因素编码
*/
private String factorCode;
/**
* 最大影响因素名称
*/
private String factorName;
/**
* 当前周期值
*/
private BigDecimal currentValue;
/**
* 上周期值
*/
private BigDecimal previousValue;
/**
* 变化值
*/
private BigDecimal changeValue;
/**
* 环比
*/
private String changeRate;
}

View File

@@ -0,0 +1,51 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 经营指标VO
*
* @author Auto
* @Date 2026/4/1
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessOperationMetricVO {
/**
* 指标编码
*/
private String metricCode;
/**
* 指标名称
*/
private String metricName;
/**
* 当前周期值
*/
private BigDecimal currentValue;
/**
* 上周期值
*/
private BigDecimal previousValue;
/**
* 变化值
*/
private BigDecimal changeValue;
/**
* 环比
*/
private String changeRate;
}

View File

@@ -0,0 +1,77 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
import java.util.List;
/**
* 经营规模曲线图数据VO
*
* @author zhangziao
* @date 2026/4/10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessScaleChartVO {
/**
* 指标编码
*/
private String metricCode;
/**
* 指标名称
*/
private String metricName;
/**
* 指标单位
*/
private String unit;
/**
* 曲线图数据点列表
*/
private List<ChartPoint> chartData;
/**
* 曲线图单个数据点
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public static class ChartPoint {
/**
* 日期格式MM/dd
*/
private String date;
/**
* 完整日期格式yyyy-MM-dd
*/
private String fullDate;
/**
* 当前周期值
*/
private BigDecimal value;
/**
* 上周期同日值
*/
private BigDecimal previousValue;
/**
* 环比
*/
private String changeRate;
}
}

View File

@@ -0,0 +1,56 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
/**
* 经营规模指标卡片VO
*
* @author zhangziao
* @date 2026/4/10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessScaleMetricVO {
/**
* 指标编码
*/
private String metricCode;
/**
* 指标名称
*/
private String metricName;
/**
* 指标单位
*/
private String unit;
/**
* 当前周期值
*/
private BigDecimal currentValue;
/**
* 上周期值
*/
private BigDecimal previousValue;
/**
* 变化值
*/
private BigDecimal changeValue;
/**
* 环比,格式如 +3.91% 或 -3.91%
*/
private String changeRate;
}

View File

@@ -0,0 +1,51 @@
package com.jsowell.pile.vo.uniapp.business;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* 经营规模VO
*
* @author zhangziao
* @date 2026/4/10
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class BusinessScaleVO {
/**
* 当前周期开始时间
*/
private String currentStartTime;
/**
* 当前周期结束时间
*/
private String currentEndTime;
/**
* 上周期开始时间
*/
private String previousStartTime;
/**
* 上周期结束时间
*/
private String previousEndTime;
/**
* 指标卡片列表(订单总额、充电订单量、充电服务费、充电电量)
*/
private List<BusinessScaleMetricVO> metricList;
/**
* 当前选中指标对应的曲线图数据
*/
private BusinessScaleChartVO chartData;
}