diff --git a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java index dab69e459..f80ae21e1 100644 --- a/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java +++ b/jsowell-admin/src/main/java/com/jsowell/api/uniapp/business/BusinessFinancialController.java @@ -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; + } } diff --git a/jsowell-admin/src/main/resources/application-dev.yml b/jsowell-admin/src/main/resources/application-dev.yml index 657922925..5de23e895 100644 --- a/jsowell-admin/src/main/resources/application-dev.yml +++ b/jsowell-admin/src/main/resources/application-dev.yml @@ -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 # 从库数据源 diff --git a/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java b/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java new file mode 100644 index 000000000..27371878a --- /dev/null +++ b/jsowell-common/src/main/java/com/jsowell/common/util/BigDecimalUtils.java @@ -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() + "%"; + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java new file mode 100644 index 000000000..e172e2891 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationAnalysisQueryDTO.java @@ -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 stationIdList; + + /** + * 当前选中的指标编码 + */ + private String selectedMetricCode; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java new file mode 100644 index 000000000..5fe90a8f7 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationDateRangeDTO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java new file mode 100644 index 000000000..a237b2b30 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessOperationSummaryDTO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java new file mode 100644 index 000000000..779d0a775 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/dto/business/BusinessScaleQueryDTO.java @@ -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 stationIdList; + + /** + * 当前选中的指标编码,用于查询对应的曲线图数据 + * ORDER_AMOUNT-订单总额, ORDER_COUNT-充电订单量, + * SERVICE_AMOUNT-充电服务费, USE_ELECTRICITY-充电电量 + */ + private String selectedMetricCode; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java index 5f12db792..ea94965fc 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/BusinessFinancialService.java @@ -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); } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java index 2e069d914..8cbe41293 100644 --- a/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java +++ b/jsowell-pile/src/main/java/com/jsowell/pile/service/impl/BusinessFinancialServiceImpl.java @@ -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 reportFuture = CompletableFuture.supplyAsync( + () -> settleOrderReportService.getMerchantOrderReportV2(dto), FINANCIAL_THREAD_POOL); + CompletableFuture balanceFuture = CompletableFuture.supplyAsync(() -> { + try { + return adapayService.queryAdapayAccountBalance(merchantId); + } catch (BaseAdaPayException e) { + log.error("查询汇付账户余额异常 merchantId:{}", merchantId, e); + return null; + } + }, FINANCIAL_THREAD_POOL); + CompletableFuture 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 stationIdList = resolveStationIds(dto.getStationIdList()); + + // 并行查询当前周期和上周期原始数据 + CompletableFuture> currentReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()), + FINANCIAL_THREAD_POOL); + CompletableFuture> previousReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()), + FINANCIAL_THREAD_POOL); + + BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReportFuture.join()); + BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReportFuture.join()); + + List 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 stationIdList = resolveStationIds(dto.getStationIdList()); + + // 并行查询订单汇总和枪口数量 + CompletableFuture summaryFuture = CompletableFuture.supplyAsync( + () -> buildSummaryFromReports( + queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd())), + FINANCIAL_THREAD_POOL); + CompletableFuture 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 resolveStationIds(List stationIdList) { + List 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 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 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 stationIdList = resolveStationIds(dto.getStationIdList()); + + // 并行查询当前周期和上周期的原始报表数据,各只查一次 + CompletableFuture> currentReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getCurrentStart(), range.getCurrentEnd()), + FINANCIAL_THREAD_POOL); + CompletableFuture> previousReportFuture = CompletableFuture.supplyAsync( + () -> queryRawReports(stationIdList, range.getPreviousStart(), range.getPreviousEnd()), + FINANCIAL_THREAD_POOL); + + // 等待两个查询完成 + List currentReports = currentReportFuture.join(); + List previousReports = previousReportFuture.join(); + + // 从同一份原始数据分别构建汇总指标和每日明细 + BusinessOperationSummaryDTO currentSummary = buildSummaryFromReports(currentReports); + BusinessOperationSummaryDTO previousSummary = buildSummaryFromReports(previousReports); + + Map currentDailyMap = buildDailyMapFromReports(currentReports); + Map previousDailyMap = buildDailyMapFromReports(previousReports); + + // 构建指标卡片 + List 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 queryRawReports(List stationIdList, + String startTime, String endTime) { + if (stationIdList != null && stationIdList.isEmpty()) { + return new ArrayList<>(); + } + List reportList = settleOrderReportService.queryOrderReport( + stationIdList, startTime, endTime); + return CollectionUtils.isEmpty(reportList) ? new ArrayList<>() : reportList; + } + + /** + * 从原始报表数据构建汇总指标 + */ + private BusinessOperationSummaryDTO buildSummaryFromReports(List 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 buildDailyMapFromReports(List reportList) { + Map 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 currentDailyMap, + Map 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 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()); + } + } } diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java new file mode 100644 index 000000000..957bf73ec --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessGunEfficiencyAnalysisVO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java new file mode 100644 index 000000000..1a0893bdc --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationAnalysisVO.java @@ -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 metricList; + + /** + * 当前选中指标对应的最大影响因素 + */ + private BusinessOperationDiagnosisItemVO diagnosisItem; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java new file mode 100644 index 000000000..ac66d2ca3 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationDiagnosisItemVO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java new file mode 100644 index 000000000..eb52264aa --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessOperationMetricVO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java new file mode 100644 index 000000000..1839cf47e --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleChartVO.java @@ -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 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; + } +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java new file mode 100644 index 000000000..c70c154e1 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleMetricVO.java @@ -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; +} diff --git a/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java new file mode 100644 index 000000000..9ddb32778 --- /dev/null +++ b/jsowell-pile/src/main/java/com/jsowell/pile/vo/uniapp/business/BusinessScaleVO.java @@ -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 metricList; + + /** + * 当前选中指标对应的曲线图数据 + */ + private BusinessScaleChartVO chartData; +}