Prechádzať zdrojové kódy

feat(system):数据看板相关接口

wzq 1 týždeň pred
rodič
commit
ee93895b8f
18 zmenil súbory, kde vykonal 1286 pridanie a 3 odobranie
  1. 52 0
      src/main/java/com/zsElectric/boot/business/model/dto/UserRefundsOrderInfoExportDTO.java
  2. 1 1
      src/main/java/com/zsElectric/boot/business/model/query/ChargeOrderInfoQuery.java
  3. 31 0
      src/main/java/com/zsElectric/boot/business/model/query/UserRefundsOrderInfoExportQuery.java
  4. 84 0
      src/main/java/com/zsElectric/boot/system/controller/DataBoardController.java
  5. 155 0
      src/main/java/com/zsElectric/boot/system/mapper/DataBoardMapper.java
  6. 24 0
      src/main/java/com/zsElectric/boot/system/model/query/HistoryBusinessQuery.java
  7. 27 0
      src/main/java/com/zsElectric/boot/system/model/query/StationRankQuery.java
  8. 30 0
      src/main/java/com/zsElectric/boot/system/model/vo/ChargePowerTrendVO.java
  9. 47 0
      src/main/java/com/zsElectric/boot/system/model/vo/DataBoardRealTimeVO.java
  10. 47 0
      src/main/java/com/zsElectric/boot/system/model/vo/DataBoardTodayVO.java
  11. 30 0
      src/main/java/com/zsElectric/boot/system/model/vo/HistoryBusinessDataVO.java
  12. 26 0
      src/main/java/com/zsElectric/boot/system/model/vo/StationRankListVO.java
  13. 35 0
      src/main/java/com/zsElectric/boot/system/model/vo/StationRankVO.java
  14. 26 0
      src/main/java/com/zsElectric/boot/system/model/vo/UserChurnRateVO.java
  15. 59 0
      src/main/java/com/zsElectric/boot/system/service/DataBoardService.java
  16. 410 0
      src/main/java/com/zsElectric/boot/system/service/impl/DataBoardServiceImpl.java
  17. 10 2
      src/main/resources/mapper/business/ChargeOrderInfoMapper.xml
  18. 192 0
      src/main/resources/mapper/system/DataBoardMapper.xml

+ 52 - 0
src/main/java/com/zsElectric/boot/business/model/dto/UserRefundsOrderInfoExportDTO.java

@@ -0,0 +1,52 @@
+package com.zsElectric.boot.business.model.dto;
+
+import cn.idev.excel.annotation.ExcelProperty;
+import cn.idev.excel.annotation.format.DateTimeFormat;
+import cn.idev.excel.annotation.write.style.ColumnWidth;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+
+/**
+ * 退款订单导出DTO
+ *
+ * @author zsElectric
+ * @since 2025-12-17
+ */
+@Data
+@ColumnWidth(20)
+public class UserRefundsOrderInfoExportDTO {
+
+    @ExcelProperty(value = "商户订单号")
+    private String orderNo;
+
+    @ExcelProperty(value = "退款类型")
+    private String typeName;
+
+    @ExcelProperty(value = "商户退款单号")
+    private String outRefundNo;
+
+    @ExcelProperty(value = "微信支付退款单号")
+    private String refundId;
+
+    @ExcelProperty(value = "微信支付订单号")
+    private String transactionId;
+
+    @ExcelProperty(value = "退款原因")
+    private String reason;
+
+    @ExcelProperty(value = "退款受理时间")
+    @DateTimeFormat("yyyy/MM/dd HH:mm:ss")
+    private LocalDateTime acceptedTime;
+
+    @ExcelProperty(value = "退款金额(元)")
+    private BigDecimal amount;
+
+    @ExcelProperty(value = "退款状态")
+    private String status;
+
+    @ExcelProperty(value = "创建时间")
+    @DateTimeFormat("yyyy/MM/dd HH:mm:ss")
+    private LocalDateTime createTime;
+}

+ 1 - 1
src/main/java/com/zsElectric/boot/business/model/query/ChargeOrderInfoQuery.java

@@ -32,7 +32,7 @@ public class ChargeOrderInfoQuery extends BasePageQuery {
     @Schema(description = "补缴状态  0.无需补缴  1.待补缴  2.已补缴 3.部分补缴")
     private Integer maspStatus;
 
-    @Schema(description = "状态0待启动 1 充电中 2 结算中 3 已完成, 5未成功充电")
+    @Schema(description = "手机号")
     private String phoneNum;
 
     @Schema(description = "充电订单号")

+ 31 - 0
src/main/java/com/zsElectric/boot/business/model/query/UserRefundsOrderInfoExportQuery.java

@@ -0,0 +1,31 @@
+package com.zsElectric.boot.business.model.query;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDateTime;
+
+/**
+ * 退款订单导出查询对象
+ *
+ * @author zsElectric
+ * @since 2025-12-17
+ */
+@Schema(description = "退款订单导出查询对象")
+@Getter
+@Setter
+public class UserRefundsOrderInfoExportQuery {
+
+    @Schema(description = "开始时间")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime startTime;
+
+    @Schema(description = "结束时间")
+    @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
+    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime endTime;
+}

+ 84 - 0
src/main/java/com/zsElectric/boot/system/controller/DataBoardController.java

@@ -0,0 +1,84 @@
+package com.zsElectric.boot.system.controller;
+
+import com.zsElectric.boot.common.annotation.Log;
+import com.zsElectric.boot.common.enums.LogModuleEnum;
+import com.zsElectric.boot.core.web.Result;
+import com.zsElectric.boot.system.model.query.HistoryBusinessQuery;
+import com.zsElectric.boot.system.model.query.StationRankQuery;
+import com.zsElectric.boot.system.model.vo.*;
+import com.zsElectric.boot.system.service.DataBoardService;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springdoc.core.annotations.ParameterObject;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * 数据看板控制器
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Slf4j
+@RestController
+@RequiredArgsConstructor
+@Tag(name = "数据看板")
+@RequestMapping("/api/v1/dataBoard")
+public class DataBoardController {
+
+    private final DataBoardService dataBoardService;
+
+    @Operation(summary = "获取实时数据(累计总数据)")
+    @GetMapping("/realTimeData")
+    @Log(value = "获取实时数据", module = LogModuleEnum.OTHER)
+    public Result<DataBoardRealTimeVO> getRealTimeData() {
+        DataBoardRealTimeVO data = dataBoardService.getRealTimeData();
+        return Result.success(data);
+    }
+
+    @Operation(summary = "获取今日实时数据")
+    @GetMapping("/todayData")
+    @Log(value = "获取今日实时数据", module = LogModuleEnum.OTHER)
+    public Result<DataBoardTodayVO> getTodayData() {
+        DataBoardTodayVO data = dataBoardService.getTodayData();
+        return Result.success(data);
+    }
+
+    @Operation(summary = "获取充电度数时段对比趋势")
+    @GetMapping("/chargePowerTrend")
+    @Log(value = "获取充电度数时段趋势", module = LogModuleEnum.OTHER)
+    public Result<ChargePowerTrendVO> getChargePowerTrend(
+            @Parameter(description = "对比日期(格式:yyyy-MM-dd)") @RequestParam(required = false) String compareDate) {
+        ChargePowerTrendVO data = dataBoardService.getChargePowerTrend(compareDate);
+        return Result.success(data);
+    }
+
+    @Operation(summary = "获取历史营业数据")
+    @GetMapping("/historyBusinessData")
+    @Log(value = "获取历史营业数据", module = LogModuleEnum.OTHER)
+    public Result<HistoryBusinessDataVO> getHistoryBusinessData(@ParameterObject HistoryBusinessQuery query) {
+        HistoryBusinessDataVO data = dataBoardService.getHistoryBusinessData(query);
+        return Result.success(data);
+    }
+
+    @Operation(summary = "获取站点排名数据")
+    @GetMapping("/stationRankData")
+    @Log(value = "获取站点排名数据", module = LogModuleEnum.OTHER)
+    public Result<StationRankListVO> getStationRankData(@ParameterObject StationRankQuery query) {
+        StationRankListVO data = dataBoardService.getStationRankData(query);
+        return Result.success(data);
+    }
+
+    @Operation(summary = "获取用户流失率")
+    @GetMapping("/userChurnRate")
+    @Log(value = "获取用户流失率", module = LogModuleEnum.OTHER)
+    public Result<UserChurnRateVO> getUserChurnRate() {
+        UserChurnRateVO data = dataBoardService.getUserChurnRate();
+        return Result.success(data);
+    }
+}

+ 155 - 0
src/main/java/com/zsElectric/boot/system/mapper/DataBoardMapper.java

@@ -0,0 +1,155 @@
+package com.zsElectric.boot.system.mapper;
+
+import com.zsElectric.boot.system.model.vo.DataBoardRealTimeVO;
+import com.zsElectric.boot.system.model.vo.DataBoardTodayVO;
+import com.zsElectric.boot.system.model.vo.StationRankVO;
+import org.apache.ibatis.annotations.Mapper;
+import org.apache.ibatis.annotations.Param;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * 数据看板Mapper接口
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Mapper
+public interface DataBoardMapper {
+
+    /**
+     * 获取累计实时数据
+     *
+     * @return 累计实时数据
+     */
+    DataBoardRealTimeVO selectRealTimeData();
+
+    /**
+     * 获取今日实时数据
+     *
+     * @param todayStart 今日开始时间
+     * @param todayEnd 今日结束时间
+     * @return 今日实时数据
+     */
+    DataBoardTodayVO selectTodayData(@Param("todayStart") String todayStart, @Param("todayEnd") String todayEnd);
+
+    /**
+     * 获取今日退款金额
+     *
+     * @param todayStart 今日开始时间
+     * @param todayEnd 今日结束时间
+     * @return 今日退款金额
+     */
+    BigDecimal selectTodayRefundAmount(@Param("todayStart") String todayStart, @Param("todayEnd") String todayEnd);
+
+    /**
+     * 获取累计退款金额
+     *
+     * @return 累计退款金额
+     */
+    BigDecimal selectTotalRefundAmount();
+
+    /**
+     * 按小时统计充电度数
+     *
+     * @param dateStart 开始日期时间
+     * @param dateEnd 结束日期时间
+     * @return 小时-充电度数映射
+     */
+    List<Map<String, Object>> selectHourlyChargePower(@Param("dateStart") String dateStart, @Param("dateEnd") String dateEnd);
+
+    /**
+     * 按日统计充电度数
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 日期-充电度数映射
+     */
+    List<Map<String, Object>> selectDailyChargePower(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 按日统计充电金额
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 日期-充电金额映射
+     */
+    List<Map<String, Object>> selectDailyChargeAmount(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 按日统计有效订单数
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 日期-订单数映射
+     */
+    List<Map<String, Object>> selectDailyValidOrders(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 按日统计注册用户数
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 日期-用户数映射
+     */
+    List<Map<String, Object>> selectDailyRegisterUsers(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 获取热门充电站排名
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @param limit 限制数量
+     * @return 热门站点列表
+     */
+    List<StationRankVO> selectHotStations(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("limit") int limit);
+
+    /**
+     * 获取站点在时间段内的充电度数
+     *
+     * @param stationId 站点ID
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 充电度数
+     */
+    BigDecimal selectStationChargePower(@Param("stationId") String stationId, @Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 获取活跃用户数(在指定时间段内有充电记录的用户)
+     *
+     * @param startDate 开始日期
+     * @param endDate 结束日期
+     * @return 活跃用户数
+     */
+    Long selectActiveUserCount(@Param("startDate") String startDate, @Param("endDate") String endDate);
+
+    /**
+     * 获取流失用户数(在早期有充电记录,但在最近时间段内没有充电记录的用户)
+     *
+     * @param earlyStart 早期开始日期
+     * @param earlyEnd 早期结束日期
+     * @param recentStart 最近开始日期
+     * @param recentEnd 最近结束日期
+     * @return 流失用户数
+     */
+    Long selectChurnUserCount(@Param("earlyStart") String earlyStart, @Param("earlyEnd") String earlyEnd,
+                              @Param("recentStart") String recentStart, @Param("recentEnd") String recentEnd);
+
+    /**
+     * 获取今日首单金额(用户在今日的第一笔订单)
+     *
+     * @param todayStart 今日开始时间
+     * @param todayEnd 今日结束时间
+     * @return 首单金额
+     */
+    BigDecimal selectTodayFirstOrderAmount(@Param("todayStart") String todayStart, @Param("todayEnd") String todayEnd);
+
+    /**
+     * 获取累计首单金额
+     *
+     * @return 累计首单金额
+     */
+    BigDecimal selectTotalFirstOrderAmount();
+}

+ 24 - 0
src/main/java/com/zsElectric/boot/system/model/query/HistoryBusinessQuery.java

@@ -0,0 +1,24 @@
+package com.zsElectric.boot.system.model.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 数据看板-历史营业数据查询参数
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "历史营业数据查询参数")
+public class HistoryBusinessQuery {
+
+    @Schema(description = "时间维度:day-日趋势,month-月趋势", example = "day")
+    private String timeDimension;
+
+    @Schema(description = "数据类型:chargePower-充电度数,chargeAmount-充电金额,validOrders-有效订单,registerUsers-注册用户", example = "chargePower")
+    private String dataType;
+
+    @Schema(description = "年月(格式:yyyy-MM,月趋势时使用)", example = "2026-03")
+    private String yearMonth;
+}

+ 27 - 0
src/main/java/com/zsElectric/boot/system/model/query/StationRankQuery.java

@@ -0,0 +1,27 @@
+package com.zsElectric.boot.system.model.query;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+/**
+ * 数据看板-站点排名查询参数
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "站点排名查询参数")
+public class StationRankQuery {
+
+    @Schema(description = "统计类型:chargePower-按充电度数,validOrders-按有效订单数", example = "chargePower")
+    private String statisticType;
+
+    @Schema(description = "时间范围:today-今天,week-近7天,month-近30天,custom-自定义", example = "week")
+    private String timeRange;
+
+    @Schema(description = "开始日期(自定义时间范围时使用,格式:yyyy-MM-dd)", example = "2026-02-01")
+    private String startDate;
+
+    @Schema(description = "结束日期(自定义时间范围时使用,格式:yyyy-MM-dd)", example = "2026-02-05")
+    private String endDate;
+}

+ 30 - 0
src/main/java/com/zsElectric/boot/system/model/vo/ChargePowerTrendVO.java

@@ -0,0 +1,30 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 数据看板-充电度数时段对比趋势VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-充电度数时段对比趋势VO")
+public class ChargePowerTrendVO {
+
+    @Schema(description = "时段列表(0-23小时)")
+    private List<Integer> hours;
+
+    @Schema(description = "今日数据")
+    private List<BigDecimal> todayData;
+
+    @Schema(description = "对比日数据")
+    private List<BigDecimal> compareData;
+
+    @Schema(description = "对比日期")
+    private String compareDate;
+}

+ 47 - 0
src/main/java/com/zsElectric/boot/system/model/vo/DataBoardRealTimeVO.java

@@ -0,0 +1,47 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 数据看板-实时数据VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-实时数据VO")
+public class DataBoardRealTimeVO {
+
+    @Schema(description = "总充电度数")
+    private BigDecimal totalChargePower;
+
+    @Schema(description = "总充电金额")
+    private BigDecimal totalChargeAmount;
+
+    @Schema(description = "总抵扣券金额")
+    private BigDecimal totalDiscountAmount;
+
+    @Schema(description = "总服务费金额")
+    private BigDecimal totalServiceFeeAmount;
+
+    @Schema(description = "总退款金额")
+    private BigDecimal totalRefundAmount;
+
+    @Schema(description = "总实付金额")
+    private BigDecimal totalActualPayAmount;
+
+    @Schema(description = "总首单金额")
+    private BigDecimal totalFirstOrderAmount;
+
+    @Schema(description = "总优惠券减免")
+    private BigDecimal totalCouponAmount;
+
+    @Schema(description = "总企业专享价减")
+    private BigDecimal totalFirmDiscountAmount;
+
+    @Schema(description = "总分销佣金")
+    private BigDecimal totalCommissionAmount;
+}

+ 47 - 0
src/main/java/com/zsElectric/boot/system/model/vo/DataBoardTodayVO.java

@@ -0,0 +1,47 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 数据看板-今日实时数据VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-今日实时数据VO")
+public class DataBoardTodayVO {
+
+    @Schema(description = "今日充电度数")
+    private BigDecimal todayChargePower;
+
+    @Schema(description = "今日充电金额")
+    private BigDecimal todayChargeAmount;
+
+    @Schema(description = "今日抵扣券金额")
+    private BigDecimal todayDiscountAmount;
+
+    @Schema(description = "今日服务费金额")
+    private BigDecimal todayServiceFeeAmount;
+
+    @Schema(description = "今日退款金额")
+    private BigDecimal todayRefundAmount;
+
+    @Schema(description = "今日实付金额")
+    private BigDecimal todayActualPayAmount;
+
+    @Schema(description = "今日首单金额")
+    private BigDecimal todayFirstOrderAmount;
+
+    @Schema(description = "今日优惠券减免")
+    private BigDecimal todayCouponAmount;
+
+    @Schema(description = "今日企业专享价减")
+    private BigDecimal todayFirmDiscountAmount;
+
+    @Schema(description = "今日分销佣金")
+    private BigDecimal todayCommissionAmount;
+}

+ 30 - 0
src/main/java/com/zsElectric/boot/system/model/vo/HistoryBusinessDataVO.java

@@ -0,0 +1,30 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.util.List;
+
+/**
+ * 数据看板-历史营业数据VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-历史营业数据VO")
+public class HistoryBusinessDataVO {
+
+    @Schema(description = "日期/时间标签列表")
+    private List<String> labels;
+
+    @Schema(description = "数据值列表")
+    private List<BigDecimal> values;
+
+    @Schema(description = "数据类型:chargePower-充电度数,chargeAmount-充电金额,validOrders-有效订单,registerUsers-注册用户")
+    private String dataType;
+
+    @Schema(description = "时间维度:day-日趋势,month-月趋势")
+    private String timeDimension;
+}

+ 26 - 0
src/main/java/com/zsElectric/boot/system/model/vo/StationRankListVO.java

@@ -0,0 +1,26 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.util.List;
+
+/**
+ * 数据看板-站点排名列表VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-站点排名列表VO")
+public class StationRankListVO {
+
+    @Schema(description = "热门充电站列表")
+    private List<StationRankVO> hotStations;
+
+    @Schema(description = "波动充电站列表")
+    private List<StationRankVO> fluctuationStations;
+
+    @Schema(description = "统计时间范围")
+    private String dateRange;
+}

+ 35 - 0
src/main/java/com/zsElectric/boot/system/model/vo/StationRankVO.java

@@ -0,0 +1,35 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 数据看板-站点排名VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-站点排名VO")
+public class StationRankVO {
+
+    @Schema(description = "序号")
+    private Integer rank;
+
+    @Schema(description = "站点ID")
+    private Long stationId;
+
+    @Schema(description = "电站名称")
+    private String stationName;
+
+    @Schema(description = "充电度数")
+    private BigDecimal chargePower;
+
+    @Schema(description = "波动幅度(百分比)")
+    private BigDecimal fluctuation;
+
+    @Schema(description = "波动方向:up-上升,down-下降")
+    private String fluctuationDirection;
+}

+ 26 - 0
src/main/java/com/zsElectric/boot/system/model/vo/UserChurnRateVO.java

@@ -0,0 +1,26 @@
+package com.zsElectric.boot.system.model.vo;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * 数据看板-用户流失率VO
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Data
+@Schema(description = "数据看板-用户流失率VO")
+public class UserChurnRateVO {
+
+    @Schema(description = "近七天流失率(百分比)")
+    private BigDecimal sevenDayChurnRate;
+
+    @Schema(description = "近1个月流失率(百分比)")
+    private BigDecimal oneMonthChurnRate;
+
+    @Schema(description = "近3个月流失率(百分比)")
+    private BigDecimal threeMonthChurnRate;
+}

+ 59 - 0
src/main/java/com/zsElectric/boot/system/service/DataBoardService.java

@@ -0,0 +1,59 @@
+package com.zsElectric.boot.system.service;
+
+import com.zsElectric.boot.system.model.query.HistoryBusinessQuery;
+import com.zsElectric.boot.system.model.query.StationRankQuery;
+import com.zsElectric.boot.system.model.vo.*;
+
+/**
+ * 数据看板服务接口
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+public interface DataBoardService {
+
+    /**
+     * 获取实时数据(累计总数据)
+     *
+     * @return 实时数据VO
+     */
+    DataBoardRealTimeVO getRealTimeData();
+
+    /**
+     * 获取今日实时数据
+     *
+     * @return 今日实时数据VO
+     */
+    DataBoardTodayVO getTodayData();
+
+    /**
+     * 获取充电度数时段对比趋势
+     *
+     * @param compareDate 对比日期(格式:yyyy-MM-dd)
+     * @return 充电度数趋势VO
+     */
+    ChargePowerTrendVO getChargePowerTrend(String compareDate);
+
+    /**
+     * 获取历史营业数据
+     *
+     * @param query 查询参数
+     * @return 历史营业数据VO
+     */
+    HistoryBusinessDataVO getHistoryBusinessData(HistoryBusinessQuery query);
+
+    /**
+     * 获取站点排名数据
+     *
+     * @param query 查询参数
+     * @return 站点排名列表VO
+     */
+    StationRankListVO getStationRankData(StationRankQuery query);
+
+    /**
+     * 获取用户流失率
+     *
+     * @return 用户流失率VO
+     */
+    UserChurnRateVO getUserChurnRate();
+}

+ 410 - 0
src/main/java/com/zsElectric/boot/system/service/impl/DataBoardServiceImpl.java

@@ -0,0 +1,410 @@
+package com.zsElectric.boot.system.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.zsElectric.boot.system.mapper.DataBoardMapper;
+import com.zsElectric.boot.system.model.query.HistoryBusinessQuery;
+import com.zsElectric.boot.system.model.query.StationRankQuery;
+import com.zsElectric.boot.system.model.vo.*;
+import com.zsElectric.boot.system.service.DataBoardService;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+/**
+ * 数据看板服务实现类
+ *
+ * @author zsElectric
+ * @since 2026-03-09
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class DataBoardServiceImpl implements DataBoardService {
+
+    private final DataBoardMapper dataBoardMapper;
+
+    private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+    private static final DateTimeFormatter DATETIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    @Override
+    public DataBoardRealTimeVO getRealTimeData() {
+        DataBoardRealTimeVO realTimeVO = dataBoardMapper.selectRealTimeData();
+        if (realTimeVO == null) {
+            realTimeVO = new DataBoardRealTimeVO();
+        }
+        // 获取累计退款金额
+        BigDecimal totalRefundAmount = dataBoardMapper.selectTotalRefundAmount();
+        realTimeVO.setTotalRefundAmount(totalRefundAmount != null ? totalRefundAmount : BigDecimal.ZERO);
+
+        // 获取累计首单金额
+        BigDecimal totalFirstOrderAmount = dataBoardMapper.selectTotalFirstOrderAmount();
+        realTimeVO.setTotalFirstOrderAmount(totalFirstOrderAmount != null ? totalFirstOrderAmount : BigDecimal.ZERO);
+
+        // 确保所有字段非空
+        ensureRealTimeDataNotNull(realTimeVO);
+        return realTimeVO;
+    }
+
+    @Override
+    public DataBoardTodayVO getTodayData() {
+        LocalDate today = LocalDate.now();
+        String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
+        String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);
+
+        DataBoardTodayVO todayVO = dataBoardMapper.selectTodayData(todayStart, todayEnd);
+        if (todayVO == null) {
+            todayVO = new DataBoardTodayVO();
+        }
+
+        // 获取今日退款金额
+        BigDecimal todayRefundAmount = dataBoardMapper.selectTodayRefundAmount(todayStart, todayEnd);
+        todayVO.setTodayRefundAmount(todayRefundAmount != null ? todayRefundAmount : BigDecimal.ZERO);
+
+        // 获取今日首单金额
+        BigDecimal todayFirstOrderAmount = dataBoardMapper.selectTodayFirstOrderAmount(todayStart, todayEnd);
+        todayVO.setTodayFirstOrderAmount(todayFirstOrderAmount != null ? todayFirstOrderAmount : BigDecimal.ZERO);
+
+        // 确保所有字段非空
+        ensureTodayDataNotNull(todayVO);
+        return todayVO;
+    }
+
+    @Override
+    public ChargePowerTrendVO getChargePowerTrend(String compareDate) {
+        ChargePowerTrendVO trendVO = new ChargePowerTrendVO();
+
+        // 生成0-23小时列表
+        List<Integer> hours = IntStream.rangeClosed(0, 23).boxed().collect(Collectors.toList());
+        trendVO.setHours(hours);
+
+        // 今日数据
+        LocalDate today = LocalDate.now();
+        String todayStart = today.atStartOfDay().format(DATETIME_FORMATTER);
+        String todayEnd = today.atTime(23, 59, 59).format(DATETIME_FORMATTER);
+        List<Map<String, Object>> todayHourlyData = dataBoardMapper.selectHourlyChargePower(todayStart, todayEnd);
+        Map<Integer, BigDecimal> todayMap = convertHourlyDataToMap(todayHourlyData);
+        List<BigDecimal> todayData = hours.stream()
+                .map(hour -> todayMap.getOrDefault(hour, BigDecimal.ZERO))
+                .collect(Collectors.toList());
+        trendVO.setTodayData(todayData);
+
+        // 对比日数据
+        LocalDate compareDateParsed;
+        if (StrUtil.isNotBlank(compareDate)) {
+            compareDateParsed = LocalDate.parse(compareDate, DATE_FORMATTER);
+        } else {
+            // 默认对比昨天
+            compareDateParsed = today.minusDays(1);
+        }
+        String compareDateStart = compareDateParsed.atStartOfDay().format(DATETIME_FORMATTER);
+        String compareDateEnd = compareDateParsed.atTime(23, 59, 59).format(DATETIME_FORMATTER);
+        List<Map<String, Object>> compareHourlyData = dataBoardMapper.selectHourlyChargePower(compareDateStart, compareDateEnd);
+        Map<Integer, BigDecimal> compareMap = convertHourlyDataToMap(compareHourlyData);
+        List<BigDecimal> compareData = hours.stream()
+                .map(hour -> compareMap.getOrDefault(hour, BigDecimal.ZERO))
+                .collect(Collectors.toList());
+        trendVO.setCompareData(compareData);
+        trendVO.setCompareDate(compareDateParsed.format(DATE_FORMATTER));
+
+        return trendVO;
+    }
+
+    @Override
+    public HistoryBusinessDataVO getHistoryBusinessData(HistoryBusinessQuery query) {
+        HistoryBusinessDataVO dataVO = new HistoryBusinessDataVO();
+        dataVO.setDataType(query.getDataType());
+        dataVO.setTimeDimension(query.getTimeDimension());
+
+        // 计算日期范围
+        LocalDate startDate;
+        LocalDate endDate;
+
+        if ("month".equalsIgnoreCase(query.getTimeDimension()) && StrUtil.isNotBlank(query.getYearMonth())) {
+            // 按月趋势:查询指定月份的每日数据
+            YearMonth yearMonth = YearMonth.parse(query.getYearMonth());
+            startDate = yearMonth.atDay(1);
+            endDate = yearMonth.atEndOfMonth();
+        } else {
+            // 按日趋势:默认查询最近30天
+            endDate = LocalDate.now();
+            startDate = endDate.minusDays(29);
+        }
+
+        String startDateStr = startDate.format(DATE_FORMATTER);
+        String endDateStr = endDate.format(DATE_FORMATTER);
+
+        List<Map<String, Object>> rawData;
+        String dataType = query.getDataType();
+        if (StrUtil.isBlank(dataType)) {
+            dataType = "chargePower";
+        }
+
+        switch (dataType) {
+            case "chargeAmount":
+                rawData = dataBoardMapper.selectDailyChargeAmount(startDateStr, endDateStr);
+                break;
+            case "validOrders":
+                rawData = dataBoardMapper.selectDailyValidOrders(startDateStr, endDateStr);
+                break;
+            case "registerUsers":
+                rawData = dataBoardMapper.selectDailyRegisterUsers(startDateStr, endDateStr);
+                break;
+            case "chargePower":
+            default:
+                rawData = dataBoardMapper.selectDailyChargePower(startDateStr, endDateStr);
+                break;
+        }
+
+        // 转换数据
+        List<String> labels = new ArrayList<>();
+        List<BigDecimal> values = new ArrayList<>();
+        Map<String, BigDecimal> dataMap = new LinkedHashMap<>();
+        for (Map<String, Object> item : rawData) {
+            String label = String.valueOf(item.get("dateLabel"));
+            BigDecimal value = item.get("value") != null ?
+                    new BigDecimal(String.valueOf(item.get("value"))) : BigDecimal.ZERO;
+            dataMap.put(label, value);
+        }
+
+        // 填充所有日期
+        LocalDate current = startDate;
+        DateTimeFormatter labelFormatter = DateTimeFormatter.ofPattern("MM.dd");
+        while (!current.isAfter(endDate)) {
+            String label = current.format(labelFormatter);
+            labels.add(label);
+            values.add(dataMap.getOrDefault(label, BigDecimal.ZERO));
+            current = current.plusDays(1);
+        }
+
+        dataVO.setLabels(labels);
+        dataVO.setValues(values);
+        return dataVO;
+    }
+
+    @Override
+    public StationRankListVO getStationRankData(StationRankQuery query) {
+        StationRankListVO rankListVO = new StationRankListVO();
+
+        // 计算时间范围
+        LocalDate startDate;
+        LocalDate endDate = LocalDate.now();
+        String timeRange = query.getTimeRange();
+        if (StrUtil.isBlank(timeRange)) {
+            timeRange = "week";
+        }
+
+        switch (timeRange) {
+            case "today":
+                startDate = endDate;
+                break;
+            case "month":
+                startDate = endDate.minusDays(29);
+                break;
+            case "custom":
+                if (StrUtil.isNotBlank(query.getStartDate()) && StrUtil.isNotBlank(query.getEndDate())) {
+                    startDate = LocalDate.parse(query.getStartDate(), DATE_FORMATTER);
+                    endDate = LocalDate.parse(query.getEndDate(), DATE_FORMATTER);
+                } else {
+                    startDate = endDate.minusDays(6);
+                }
+                break;
+            case "week":
+            default:
+                startDate = endDate.minusDays(6);
+                break;
+        }
+
+        String startDateStr = startDate.format(DATE_FORMATTER);
+        String endDateStr = endDate.format(DATE_FORMATTER);
+
+        // 计算前一个周期的日期范围(用于波动计算)
+        long daysDiff = java.time.temporal.ChronoUnit.DAYS.between(startDate, endDate) + 1;
+        LocalDate prevStartDate = startDate.minusDays(daysDiff);
+        LocalDate prevEndDate = startDate.minusDays(1);
+        String prevStartDateStr = prevStartDate.format(DATE_FORMATTER);
+        String prevEndDateStr = prevEndDate.format(DATE_FORMATTER);
+
+        // 获取热门充电站
+        List<StationRankVO> hotStations = dataBoardMapper.selectHotStations(startDateStr, endDateStr, 5);
+        // 设置排名
+        for (int i = 0; i < hotStations.size(); i++) {
+            hotStations.get(i).setRank(i + 1);
+        }
+        rankListVO.setHotStations(hotStations);
+
+        // 计算波动充电站(基于充电度数波动)
+        List<StationRankVO> fluctuationStations = calculateFluctuationStations(
+                hotStations, prevStartDateStr, prevEndDateStr, startDateStr, endDateStr);
+        rankListVO.setFluctuationStations(fluctuationStations);
+
+        // 设置日期范围描述
+        rankListVO.setDateRange(startDateStr + " 至 " + endDateStr);
+
+        return rankListVO;
+    }
+
+    @Override
+    public UserChurnRateVO getUserChurnRate() {
+        UserChurnRateVO churnRateVO = new UserChurnRateVO();
+        LocalDate today = LocalDate.now();
+
+        // 计算近7天流失率
+        BigDecimal sevenDayRate = calculateChurnRate(today.minusDays(14), today.minusDays(7),
+                today.minusDays(6), today);
+        churnRateVO.setSevenDayChurnRate(sevenDayRate);
+
+        // 计算近1个月流失率
+        BigDecimal oneMonthRate = calculateChurnRate(today.minusDays(60), today.minusDays(30),
+                today.minusDays(29), today);
+        churnRateVO.setOneMonthChurnRate(oneMonthRate);
+
+        // 计算近3个月流失率
+        BigDecimal threeMonthRate = calculateChurnRate(today.minusDays(180), today.minusDays(90),
+                today.minusDays(89), today);
+        churnRateVO.setThreeMonthChurnRate(threeMonthRate);
+
+        return churnRateVO;
+    }
+
+    /**
+     * 计算流失率
+     */
+    private BigDecimal calculateChurnRate(LocalDate earlyStart, LocalDate earlyEnd,
+                                          LocalDate recentStart, LocalDate recentEnd) {
+        String earlyStartStr = earlyStart.format(DATE_FORMATTER);
+        String earlyEndStr = earlyEnd.format(DATE_FORMATTER);
+        String recentStartStr = recentStart.format(DATE_FORMATTER);
+        String recentEndStr = recentEnd.format(DATE_FORMATTER);
+
+        Long activeCount = dataBoardMapper.selectActiveUserCount(earlyStartStr, earlyEndStr);
+        if (activeCount == null || activeCount == 0) {
+            return BigDecimal.ZERO;
+        }
+
+        Long churnCount = dataBoardMapper.selectChurnUserCount(earlyStartStr, earlyEndStr, recentStartStr, recentEndStr);
+        if (churnCount == null) {
+            churnCount = 0L;
+        }
+
+        return BigDecimal.valueOf(churnCount)
+                .multiply(BigDecimal.valueOf(100))
+                .divide(BigDecimal.valueOf(activeCount), 2, RoundingMode.HALF_UP);
+    }
+
+    /**
+     * 计算波动充电站
+     */
+    private List<StationRankVO> calculateFluctuationStations(List<StationRankVO> hotStations,
+                                                              String prevStartDate, String prevEndDate,
+                                                              String currStartDate, String currEndDate) {
+        List<StationRankVO> fluctuationStations = new ArrayList<>();
+
+        for (StationRankVO station : hotStations) {
+            if (station.getStationId() == null) {
+                continue;
+            }
+            StationRankVO fluctuationStation = new StationRankVO();
+            fluctuationStation.setRank(station.getRank());
+            fluctuationStation.setStationId(station.getStationId());
+            fluctuationStation.setStationName(station.getStationName());
+            fluctuationStation.setChargePower(station.getChargePower());
+
+            // 获取前一周期的充电度数
+            BigDecimal prevPower = dataBoardMapper.selectStationChargePower(
+                    String.valueOf(station.getStationId()), prevStartDate, prevEndDate);
+            if (prevPower == null) {
+                prevPower = BigDecimal.ZERO;
+            }
+
+            BigDecimal currPower = station.getChargePower();
+            if (currPower == null) {
+                currPower = BigDecimal.ZERO;
+            }
+
+            // 计算波动百分比
+            if (prevPower.compareTo(BigDecimal.ZERO) == 0) {
+                if (currPower.compareTo(BigDecimal.ZERO) > 0) {
+                    fluctuationStation.setFluctuation(BigDecimal.valueOf(100));
+                    fluctuationStation.setFluctuationDirection("up");
+                } else {
+                    fluctuationStation.setFluctuation(BigDecimal.ZERO);
+                    fluctuationStation.setFluctuationDirection("up");
+                }
+            } else {
+                BigDecimal diff = currPower.subtract(prevPower);
+                BigDecimal fluctuation = diff.multiply(BigDecimal.valueOf(100))
+                        .divide(prevPower, 2, RoundingMode.HALF_UP).abs();
+                fluctuationStation.setFluctuation(fluctuation);
+                fluctuationStation.setFluctuationDirection(diff.compareTo(BigDecimal.ZERO) >= 0 ? "up" : "down");
+            }
+
+            fluctuationStations.add(fluctuationStation);
+        }
+
+        // 按波动幅度排序
+        fluctuationStations.sort((a, b) -> b.getFluctuation().compareTo(a.getFluctuation()));
+
+        // 重新设置排名
+        for (int i = 0; i < fluctuationStations.size(); i++) {
+            fluctuationStations.get(i).setRank(i + 1);
+        }
+
+        return fluctuationStations;
+    }
+
+    /**
+     * 将小时数据转换为Map
+     */
+    private Map<Integer, BigDecimal> convertHourlyDataToMap(List<Map<String, Object>> hourlyData) {
+        Map<Integer, BigDecimal> map = new HashMap<>();
+        for (Map<String, Object> item : hourlyData) {
+            Integer hour = ((Number) item.get("hour")).intValue();
+            BigDecimal power = item.get("chargePower") != null ?
+                    new BigDecimal(String.valueOf(item.get("chargePower"))) : BigDecimal.ZERO;
+            map.put(hour, power);
+        }
+        return map;
+    }
+
+    /**
+     * 确保实时数据所有字段非空
+     */
+    private void ensureRealTimeDataNotNull(DataBoardRealTimeVO vo) {
+        if (vo.getTotalChargePower() == null) vo.setTotalChargePower(BigDecimal.ZERO);
+        if (vo.getTotalChargeAmount() == null) vo.setTotalChargeAmount(BigDecimal.ZERO);
+        if (vo.getTotalDiscountAmount() == null) vo.setTotalDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTotalServiceFeeAmount() == null) vo.setTotalServiceFeeAmount(BigDecimal.ZERO);
+        if (vo.getTotalRefundAmount() == null) vo.setTotalRefundAmount(BigDecimal.ZERO);
+        if (vo.getTotalActualPayAmount() == null) vo.setTotalActualPayAmount(BigDecimal.ZERO);
+        if (vo.getTotalFirstOrderAmount() == null) vo.setTotalFirstOrderAmount(BigDecimal.ZERO);
+        if (vo.getTotalCouponAmount() == null) vo.setTotalCouponAmount(BigDecimal.ZERO);
+        if (vo.getTotalFirmDiscountAmount() == null) vo.setTotalFirmDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTotalCommissionAmount() == null) vo.setTotalCommissionAmount(BigDecimal.ZERO);
+    }
+
+    /**
+     * 确保今日数据所有字段非空
+     */
+    private void ensureTodayDataNotNull(DataBoardTodayVO vo) {
+        if (vo.getTodayChargePower() == null) vo.setTodayChargePower(BigDecimal.ZERO);
+        if (vo.getTodayChargeAmount() == null) vo.setTodayChargeAmount(BigDecimal.ZERO);
+        if (vo.getTodayDiscountAmount() == null) vo.setTodayDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTodayServiceFeeAmount() == null) vo.setTodayServiceFeeAmount(BigDecimal.ZERO);
+        if (vo.getTodayRefundAmount() == null) vo.setTodayRefundAmount(BigDecimal.ZERO);
+        if (vo.getTodayActualPayAmount() == null) vo.setTodayActualPayAmount(BigDecimal.ZERO);
+        if (vo.getTodayFirstOrderAmount() == null) vo.setTodayFirstOrderAmount(BigDecimal.ZERO);
+        if (vo.getTodayCouponAmount() == null) vo.setTodayCouponAmount(BigDecimal.ZERO);
+        if (vo.getTodayFirmDiscountAmount() == null) vo.setTodayFirmDiscountAmount(BigDecimal.ZERO);
+        if (vo.getTodayCommissionAmount() == null) vo.setTodayCommissionAmount(BigDecimal.ZERO);
+    }
+}

+ 10 - 2
src/main/resources/mapper/business/ChargeOrderInfoMapper.xml

@@ -85,7 +85,11 @@
                 AND coi.order_type = #{queryParams.orderType}
             </if>
             <if test="queryParams.phoneNum != null and queryParams.phoneNum != ''">
-                AND ui.phone like concat('%',#{queryParams.phoneNum},'%')
+                AND (
+                    (coi.order_type = 2 AND coi.phone_num like concat('%',#{queryParams.phoneNum},'%'))
+                    OR
+                    (coi.order_type != 1 AND ui.phone like concat('%',#{queryParams.phoneNum},'%'))
+                )
             </if>
             <if test="queryParams.chargeOrderNo != null and queryParams.chargeOrderNo != ''">
                 AND coi.charge_order_no like concat('%',#{queryParams.chargeOrderNo},'%')
@@ -318,7 +322,11 @@
                 AND a.order_type = #{queryParams.orderType}
             </if>
             <if test="queryParams.phoneNum != null and queryParams.phoneNum != ''">
-                AND b.phone like concat('%',#{queryParams.phoneNum},'%')
+                AND (
+                    (a.order_type = 2 AND a.phone_num like concat('%',#{queryParams.phoneNum},'%'))
+                    OR
+                    (a.order_type != 2 AND b.phone like concat('%',#{queryParams.phoneNum},'%'))
+                )
             </if>
             <if test="queryParams.chargeOrderNo != null and queryParams.chargeOrderNo != ''">
                 AND a.charge_order_no like concat('%',#{queryParams.chargeOrderNo},'%')

+ 192 - 0
src/main/resources/mapper/system/DataBoardMapper.xml

@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.zsElectric.boot.system.mapper.DataBoardMapper">
+
+    <!-- 获取累计实时数据 -->
+    <select id="selectRealTimeData" resultType="com.zsElectric.boot.system.model.vo.DataBoardRealTimeVO">
+        SELECT
+            COALESCE(SUM(total_charge), 0) AS totalChargePower,
+            COALESCE(SUM(third_party_total_cost), 0) AS totalChargeAmount,
+            COALESCE(SUM(discount_money), 0) AS totalDiscountAmount,
+            COALESCE(SUM(real_service_cost), 0) AS totalServiceFeeAmount,
+            COALESCE(SUM(real_cost), 0) AS totalActualPayAmount,
+            COALESCE(SUM(coupon_price), 0) AS totalCouponAmount,
+            COALESCE(SUM(firm_price), 0) AS totalFirmDiscountAmount,
+            0 AS totalCommissionAmount
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+    </select>
+
+    <!-- 获取今日实时数据 -->
+    <select id="selectTodayData" resultType="com.zsElectric.boot.system.model.vo.DataBoardTodayVO">
+        SELECT
+            COALESCE(SUM(total_charge), 0) AS todayChargePower,
+            COALESCE(SUM(third_party_total_cost), 0) AS todayChargeAmount,
+            COALESCE(SUM(discount_money), 0) AS todayDiscountAmount,
+            COALESCE(SUM(real_service_cost), 0) AS todayServiceFeeAmount,
+            COALESCE(SUM(real_cost), 0) AS todayActualPayAmount,
+            COALESCE(SUM(coupon_price), 0) AS todayCouponAmount,
+            COALESCE(SUM(firm_price), 0) AS todayFirmDiscountAmount,
+            0 AS todayCommissionAmount
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND create_time BETWEEN #{todayStart} AND #{todayEnd}
+    </select>
+
+    <!-- 获取今日退款金额 -->
+    <select id="selectTodayRefundAmount" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(amount), 0)
+        FROM c_user_refunds_order_info
+        WHERE status = 'SUCCESS' AND is_deleted = 0
+        AND success_time BETWEEN #{todayStart} AND #{todayEnd}
+    </select>
+
+    <!-- 获取累计退款金额 -->
+    <select id="selectTotalRefundAmount" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(amount), 0)
+        FROM c_user_refunds_order_info
+        WHERE status = 'SUCCESS' AND is_deleted = 0
+    </select>
+
+    <!-- 按小时统计充电度数 -->
+    <select id="selectHourlyChargePower" resultType="java.util.Map">
+        SELECT
+            HOUR(create_time) AS hour,
+            COALESCE(SUM(total_charge), 0) AS chargePower
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND create_time BETWEEN #{dateStart} AND #{dateEnd}
+        GROUP BY HOUR(create_time)
+        ORDER BY hour
+    </select>
+
+    <!-- 按日统计充电度数 -->
+    <select id="selectDailyChargePower" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(create_time, '%m.%d') AS dateLabel,
+            COALESCE(SUM(total_charge), 0) AS value
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY DATE(create_time)
+        ORDER BY DATE(create_time)
+    </select>
+
+    <!-- 按日统计充电金额 -->
+    <select id="selectDailyChargeAmount" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(create_time, '%m.%d') AS dateLabel,
+            COALESCE(SUM(real_cost), 0) AS value
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY DATE(create_time)
+        ORDER BY DATE(create_time)
+    </select>
+
+    <!-- 按日统计有效订单数 -->
+    <select id="selectDailyValidOrders" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(create_time, '%m.%d') AS dateLabel,
+            COUNT(*) AS value
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY DATE(create_time)
+        ORDER BY DATE(create_time)
+    </select>
+
+    <!-- 按日统计注册用户数 -->
+    <select id="selectDailyRegisterUsers" resultType="java.util.Map">
+        SELECT
+            DATE_FORMAT(create_time, '%m.%d') AS dateLabel,
+            COUNT(*) AS value
+        FROM c_user_info
+        WHERE is_deleted = 0
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY DATE(create_time)
+        ORDER BY DATE(create_time)
+    </select>
+
+    <!-- 获取热门充电站排名 -->
+    <select id="selectHotStations" resultType="com.zsElectric.boot.system.model.vo.StationRankVO">
+        SELECT
+            s.id AS stationId,
+            s.station_name AS stationName,
+            COALESCE(SUM(o.total_charge), 0) AS chargePower
+        FROM c_charge_order_info o
+        LEFT JOIN t_third_party_station_info s ON o.third_party_station_id = s.station_id
+        WHERE o.status = 3 AND o.is_deleted = 0
+        AND DATE(o.create_time) BETWEEN #{startDate} AND #{endDate}
+        GROUP BY o.third_party_station_id, s.id, s.station_name
+        ORDER BY chargePower DESC
+        LIMIT #{limit}
+    </select>
+
+    <!-- 获取站点在时间段内的充电度数 -->
+    <select id="selectStationChargePower" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(total_charge), 0)
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND third_party_station_id = #{stationId}
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+    </select>
+
+    <!-- 获取活跃用户数 -->
+    <select id="selectActiveUserCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT user_id)
+        FROM c_charge_order_info
+        WHERE status = 3 AND is_deleted = 0
+        AND DATE(create_time) BETWEEN #{startDate} AND #{endDate}
+    </select>
+
+    <!-- 获取流失用户数 -->
+    <select id="selectChurnUserCount" resultType="java.lang.Long">
+        SELECT COUNT(DISTINCT early.user_id)
+        FROM (
+            SELECT DISTINCT user_id
+            FROM c_charge_order_info
+            WHERE status = 3 AND is_deleted = 0
+            AND DATE(create_time) BETWEEN #{earlyStart} AND #{earlyEnd}
+        ) early
+        LEFT JOIN (
+            SELECT DISTINCT user_id
+            FROM c_charge_order_info
+            WHERE status = 3 AND is_deleted = 0
+            AND DATE(create_time) BETWEEN #{recentStart} AND #{recentEnd}
+        ) recent ON early.user_id = recent.user_id
+        WHERE recent.user_id IS NULL
+    </select>
+
+    <!-- 获取今日首单金额 -->
+    <select id="selectTodayFirstOrderAmount" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(first_order.real_cost), 0)
+        FROM (
+            SELECT user_id, MIN(create_time) AS first_time
+            FROM c_charge_order_info
+            WHERE status = 3 AND is_deleted = 0
+            GROUP BY user_id
+        ) first_order_time
+        INNER JOIN c_charge_order_info first_order
+            ON first_order.user_id = first_order_time.user_id
+            AND first_order.create_time = first_order_time.first_time
+        WHERE first_order.status = 3 AND first_order.is_deleted = 0
+        AND DATE(first_order.create_time) BETWEEN #{todayStart} AND #{todayEnd}
+    </select>
+
+    <!-- 获取累计首单金额 -->
+    <select id="selectTotalFirstOrderAmount" resultType="java.math.BigDecimal">
+        SELECT COALESCE(SUM(first_order.real_cost), 0)
+        FROM (
+            SELECT user_id, MIN(create_time) AS first_time
+            FROM c_charge_order_info
+            WHERE status = 3 AND is_deleted = 0
+            GROUP BY user_id
+        ) first_order_time
+        INNER JOIN c_charge_order_info first_order
+            ON first_order.user_id = first_order_time.user_id
+            AND first_order.create_time = first_order_time.first_time
+        WHERE first_order.status = 3 AND first_order.is_deleted = 0
+    </select>
+
+</mapper>