Selaa lähdekoodia

第一次接口代码优化

fuyuchuan 1 päivä sitten
vanhempi
commit
e8518c3839

+ 39 - 0
fiveep-model/src/main/java/com/bizmatics/model/utils/TimeRangeParams.java

@@ -0,0 +1,39 @@
+package com.bizmatics.model.utils;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/27
+ */
+@Data
+@Builder
+public class TimeRangeParams {
+    private String deviceCode;
+    private LocalDate today;
+    private LocalDate monthStart;
+    private LocalDate monthEnd;
+    private LocalDate yearStart;
+    private LocalDate yearEnd;
+    private LocalDate yesterday;
+    private LocalDate lastMonthStart;
+    private LocalDate lastMonthEnd;
+    private LocalDate lastYearStart;
+    private LocalDate lastYearEnd;
+    private LocalDateTime sameDayStart;
+    private LocalDateTime sameDayEnd;
+    private LocalDateTime sameMonthStart;
+    private LocalDateTime sameMonthEnd;
+    private LocalDateTime sameYearStart;
+    private LocalDateTime sameYearEnd;
+    private LocalDateTime lastYearSameDayStart;
+    private LocalDateTime lastYearSameDayEnd;
+    private LocalDateTime lastYearSameMonthStart;
+    private LocalDateTime lastYearSameMonthEnd;
+}

+ 131 - 0
fiveep-model/src/main/java/com/bizmatics/model/utils/TimeRangeUtils.java

@@ -0,0 +1,131 @@
+package com.bizmatics.model.utils;
+
+import lombok.Builder;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.LocalTime;
+import java.time.temporal.TemporalAdjusters;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/27
+ */
+public class TimeRangeUtils {
+    public static TimeRanges getAllTimeRanges(LocalDateTime time) {
+        LocalDate today = time.toLocalDate();
+        LocalDateTime yesterdayTime = time.minusDays(1);
+
+        return TimeRanges.builder()
+                .today(today)
+                .yesterdayTime(yesterdayTime)
+                .monthRange(getMonthRange(today))
+                .yearRange(getYearRange(today))
+                .yesterdayRange(getYesterdayRange(today))
+                .lastMonthRange(getLastMonthRange(today))
+                .lastYearRange(getLastYearRange(today))
+                .sameDayRange(getSameDayRange(time))
+                .sameMonthRange(getSameMonthRange(time))
+                .sameYearRange(getSameYearRange(time))
+                .lastYearSameDay(getLastYearSameDay(time))
+                .lastYearSameMonth(getLastYearSameMonth(time))
+                .build();
+    }
+
+    private static LocalDate[] getMonthRange(LocalDate date) {
+        return new LocalDate[]{
+                date.with(TemporalAdjusters.firstDayOfMonth()),
+                date.with(TemporalAdjusters.lastDayOfMonth())
+        };
+    }
+
+    private static LocalDate[] getYearRange(LocalDate date) {
+        return new LocalDate[]{
+                date.with(TemporalAdjusters.firstDayOfYear()),
+                date.with(TemporalAdjusters.lastDayOfYear())
+        };
+    }
+
+    private static LocalDate[] getYesterdayRange(LocalDate today) {
+        return new LocalDate[]{
+                today.minusDays(1)
+        };
+    }
+
+    private static LocalDate[] getLastMonthRange(LocalDate today) {
+        LocalDate lastMonth = today.minusMonths(1);
+        return new LocalDate[]{
+                lastMonth.with(TemporalAdjusters.firstDayOfMonth()),
+                lastMonth.with(TemporalAdjusters.lastDayOfMonth())
+        };
+    }
+
+    private static LocalDate[] getLastYearRange(LocalDate today) {
+        LocalDate lastYear = today.minusYears(1);
+        return new LocalDate[]{
+                lastYear.with(TemporalAdjusters.firstDayOfYear()),
+                lastYear.with(TemporalAdjusters.lastDayOfYear()),
+        };
+    }
+
+    private static LocalDateTime[] getSameDayRange(LocalDateTime dateTime) {
+        LocalDateTime sameDay = dateTime.minusDays(1);
+        return new LocalDateTime[]{
+                sameDay.with(LocalTime.MIN),
+                sameDay
+        };
+    }
+
+    private static LocalDateTime[] getSameMonthRange(LocalDateTime dateTime) {
+        LocalDateTime sameMonth = dateTime.minusMonths(1);
+        return new LocalDateTime[]{
+                sameMonth.with(TemporalAdjusters.firstDayOfMonth()),
+                sameMonth
+        };
+    }
+
+    private static LocalDateTime[] getSameYearRange(LocalDateTime dateTime) {
+        LocalDateTime sameYear = dateTime.minusYears(1);
+        return new LocalDateTime[]{
+                sameYear.with(TemporalAdjusters.firstDayOfYear()),
+                sameYear
+        };
+    }
+
+    private static LocalDateTime[] getLastYearSameDay(LocalDateTime dateTime) {
+        LocalDateTime sameDay = dateTime.minusYears(1);
+        return new LocalDateTime[]{
+                sameDay.with(LocalTime.MIN),
+                sameDay
+        };
+    }
+
+    private static LocalDateTime[] getLastYearSameMonth(LocalDateTime dateTime) {
+        LocalDateTime sameMonth = dateTime.minusYears(1);
+        return new LocalDateTime[]{
+                sameMonth.with(TemporalAdjusters.firstDayOfMonth()),
+                sameMonth
+        };
+    }
+
+    @Data
+    @Builder
+    public static class TimeRanges {
+        private LocalDate today;
+        private LocalDateTime yesterdayTime;
+        private LocalDate[] monthRange;
+        private LocalDate[] yearRange;
+        private LocalDate[] yesterdayRange;
+        private LocalDate[] lastMonthRange;
+        private LocalDate[] lastYearRange;
+        private LocalDateTime[] sameDayRange;
+        private LocalDateTime[] sameMonthRange;
+        private LocalDateTime[] sameYearRange;
+        private LocalDateTime[] lastYearSameDay;
+        private LocalDateTime[] lastYearSameMonth;
+        private LocalDateTime[] lastYearSameYear;
+    }
+}

+ 6 - 0
fiveep-persistence/src/main/java/com/bizmatics/persistence/mapper/HtAnalogDataMapper.java

@@ -3,12 +3,16 @@ package com.bizmatics.persistence.mapper;
 import com.bizmatics.common.mvc.base.CrudMapper;
 import com.bizmatics.model.Device;
 import com.bizmatics.model.HtAnalogData;
+import com.bizmatics.model.utils.TimeRangeParams;
 import com.bizmatics.model.vo.*;
 import org.apache.ibatis.annotations.MapKey;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.springframework.stereotype.Repository;
 
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
@@ -178,4 +182,6 @@ public interface HtAnalogDataMapper extends CrudMapper<HtAnalogData> {
                                            @Param("table") String table);
 
     List<HtAnalogData> getP(@Param("deviceCodes") List<String> deviceCodes);
+
+    List<Map<String, Object>> aggregateAll(TimeRangeParams params);
 }

+ 231 - 0
fiveep-persistence/src/main/resources/mapper/mysql/HtAnalogDataMapper.xml

@@ -1561,4 +1561,235 @@
         ) latest ON t.deviceName = latest.deviceName AND t.id = latest.max_id
     </select>
 
+    <select id="aggregateAll" resultType="map">
+        <!-- 1. today -->
+        SELECT 'today' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime = #{today}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime = #{today}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 2. yesterday -->
+        SELECT 'yesterday' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime = #{yesterday}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime = #{yesterday}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 3. month -->
+        SELECT 'month' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{monthStart} AND #{monthEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{monthStart} AND #{monthEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 4. last_month -->
+        SELECT 'last_month' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{lastMonthStart} AND #{lastMonthEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{lastMonthStart} AND #{lastMonthEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 5. year -->
+        SELECT 'year' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{yearStart} AND #{yearEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{yearStart} AND #{yearEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 6. last_year -->
+        SELECT 'last_year' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{lastYearStart} AND #{lastYearEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND freezingTime BETWEEN #{lastYearStart} AND #{lastYearEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 7. same_day (按 dataTime 区间) -->
+        SELECT 'same_day' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameDayStart} AND #{sameDayEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameDayStart} AND #{sameDayEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 8. same_month -->
+        SELECT 'same_month' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameMonthStart} AND #{sameMonthEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameMonthStart} AND #{sameMonthEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 9. same_year -->
+        SELECT 'same_year' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameYearStart} AND #{sameYearEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{sameYearStart} AND #{sameYearEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 10. last_year_same_day -->
+        SELECT 'last_year_same_day' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{lastYearSameDayStart} AND #{lastYearSameDayEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{lastYearSameDayStart} AND #{lastYearSameDayEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+
+        UNION ALL
+        <!-- 11. last_year_same_month -->
+        SELECT 'last_year_same_month' AS type,
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{lastYearSameMonthStart} AND #{lastYearSameMonthEnd}
+        ORDER BY dataTime DESC
+        LIMIT 1)
+        ,0)
+        -
+        IFNULL(
+        (SELECT epp
+        FROM ht_analog_data
+        WHERE deviceName = #{deviceCode}
+        AND dataTime BETWEEN #{lastYearSameMonthStart} AND #{lastYearSameMonthEnd}
+        ORDER BY dataTime ASC
+        LIMIT 1)
+        ,0) AS value
+    </select>
 </mapper>

+ 12 - 1
fiveep-service/pom.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <parent>
         <artifactId>usky-power</artifactId>
         <groupId>com.bizmatics</groupId>
@@ -101,6 +102,16 @@
             <artifactId>fastjson</artifactId>
         </dependency>
 
+        <!-- 缓存配置 -->
+        <dependency>
+            <groupId>com.github.ben-manes.caffeine</groupId>
+            <artifactId>caffeine</artifactId>
+            <version>2.9.3</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.protobuf</groupId>
+            <artifactId>protobuf-java</artifactId>
+        </dependency>
     </dependencies>
 
 

+ 200 - 192
fiveep-service/src/main/java/com/bizmatics/service/impl/HtAnalogDataServiceImpl.java

@@ -11,15 +11,20 @@ import com.bizmatics.common.mvc.base.AbstractCrudService;
 import com.bizmatics.common.spring.util.GlobalUtils;
 import com.bizmatics.common.spring.util.JsonUtils;
 import com.bizmatics.model.*;
+import com.bizmatics.model.utils.TimeRangeParams;
+import com.bizmatics.model.utils.TimeRangeUtils;
 import com.bizmatics.model.vo.*;
 import com.bizmatics.persistence.mapper.*;
 import com.bizmatics.service.*;
+import com.bizmatics.service.util.AnalogCache;
 import com.bizmatics.service.util.FieldEscapeUtils;
 import com.bizmatics.service.util.SecurityUtils;
+import com.bizmatics.service.util.SiteFeeCacheService;
 import com.bizmatics.service.vo.*;
 import com.fasterxml.jackson.core.type.TypeReference;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.poi.ss.usermodel.Workbook;
+import org.checkerframework.checker.units.qual.A;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
@@ -76,6 +81,12 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
     private SiteElectricityRecordMapper siteElectricityRecordMapper;
     @Autowired
     private ElectricityTimePriceMapper electricityTimePriceMapper;
+    @Autowired
+    private AnalogCache analogCache;
+    @Autowired
+    private SiteFeeCacheService siteFeeCacheService;
+
+    private static final BigDecimal ZERO = BigDecimal.ZERO;
 
     @Override
     public HadCountVO selectCount() {
@@ -1514,9 +1525,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
             return new HtAnalogEnergyConsumptionVo();
         }
 
-        // LocalDate time = localDateTime.toLocalDate();
         // 上个月的同一天
-        // LocalDate sameDayLastMonth = time.minusMonths(1);
         LocalDate today = time.toLocalDate();
         LocalDate yesterday1 = today.minusDays(1);
         LocalDate lastMonth = today.minusMonths(1);
@@ -1558,76 +1567,75 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         BigDecimal coefficient = new BigDecimal(siteDynamicProperties.getDemolitionStandardCoal1())
                 .divide(new BigDecimal(demolitionStandardCoal), 10, RoundingMode.DOWN);
 
+        // 时间范围
+        TimeRangeUtils.TimeRanges ranges = TimeRangeUtils.getAllTimeRanges(time);
+
         switch (timeType) {
             // 概览
             case "overview":
                 if (!data183.isEmpty()) {
-                    dayList = getAnalogData(data183.get(0), time, 1);
-                    monthList = getAnalogData(data183.get(0), time, 2);
-                    yearList = getAnalogData(data183.get(0), time, 3);
-
-                    yesterday = getAnalogData(data183.get(0), time.minusDays(1), 1);
-                    // 上月同期
-                    sameDayLastMonthList = getAnalogData(data183.get(0), time, 22);
-                    // 上年同期
-                    sameDayLastYearList = getAnalogData(data183.get(0), time, 33);
-
+                    String deviceCode183 = data183.get(0);
                     if (consume == 1) {
-                        // 昨日同期
-                        List<HtAnalogData> yesterdatSameTimeList = getAnalogData(data183.get(0), time, 11);
-                        List<HtAnalogData> sameDayLastMonth = getAnalogData(data183.get(0), time.minusMonths(1), 2);
-                        List<HtAnalogData> sameDayLastYear = getAnalogData(data183.get(0), time.minusYears(1), 3);
+                        Map<String, BigDecimal> result = analogCache.get(deviceCode183, ranges);
+                        BigDecimal today1 = result.get("today");
+                        BigDecimal month1 = result.get("month");
+                        BigDecimal year1 = result.get("year");
+                        BigDecimal yesterday2 = result.get("yesterday");
+                        BigDecimal lastMonth1 = result.get("last_month");
+                        BigDecimal lastYear1 = result.get("last_year");
+                        BigDecimal sameDay = result.get("same_day");
+                        BigDecimal sameMonth = result.get("same_month");
+                        BigDecimal sameYear = result.get("same_year");
+                        BigDecimal lastYearSameDay = result.get("last_year_same_day");
+                        BigDecimal lastYearSameMonth = result.get("last_year_same_month");
+
+                        // 电
+                        vo.setTodayElectricity(today1);
+                        vo.setMonthElectricity(month1);
+                        vo.setYearElectricity(year1);
+                        vo.setYesterdayElectricity(yesterday2);
+                        vo.setLastMonthElectricity(lastMonth1);
+                        vo.setLastYearElectricity(lastYear1);
 
-                        vo.setTodayElectricity(getTotal(dayList));
-                        vo.setMonthElectricity(getTotal(monthList));
-                        vo.setYearElectricity(getTotal(yearList));
-                        vo.setYesterdayElectricity(getTotal(yesterday));
-                        vo.setLastMonthElectricity(getTotal(sameDayLastMonth));
-                        vo.setLastYearElectricity(getTotal(sameDayLastYear));
-
-                        vo.setTodayRingRatio(getRingRatio(dayList, yesterdatSameTimeList));
-                        vo.setMonthRingRatio(getRingRatio(monthList, sameDayLastMonthList));
-                        vo.setYearRingRatio(getRingRatio(yearList, sameDayLastYearList));
-
-                        vo.setToday(getTotal(dayList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setYesterday(getTotal(yesterday).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setMonth(getTotal(monthList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setLastMonth(getTotal(sameDayLastMonth).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setYear(getTotal(yearList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setLastYear(getTotal(sameDayLastYear).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-
-                        // 调试输出
-                        System.out.println("昨日同期:" + getTotal(yesterdatSameTimeList));
-                        System.out.println("上月同期:" + getTotal(sameDayLastMonthList));
-                        System.out.println("去年同期:" + getTotal(sameDayLastYearList));
+                        // 环比
+                        vo.setTodayRingRatio(ringRatio(today1, sameDay));
+                        vo.setMonthRingRatio(ringRatio(month1, sameMonth));
+                        vo.setYearRingRatio(ringRatio(year1, sameYear));
 
+                        // 能耗
+                        vo.setToday(scale(today1, coefficient));
+                        vo.setMonth(scale(month1, coefficient));
+                        vo.setYear(scale(year1, coefficient));
+                        vo.setYesterday(scale(yesterday2, coefficient));
+                        vo.setLastMonth(scale(lastMonth1, coefficient));
+                        vo.setLastYear(scale(lastYear1, coefficient));
                     } else if (consume == 2) {
                         // 1. 计算电量 & 基础费用
-                        ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
+                        // ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
 
-                        BigDecimal dayElectricity = getTotal(dayList);
-                        BigDecimal monthElectricity = getTotal(monthList);
-                        BigDecimal yearElectricity = getTotal(yearList);
-                        BigDecimal sameDayElectricity = getTotal(yesterday);
-                        BigDecimal sameMonthElectricity = getTotal(sameDayLastMonthList);
-                        BigDecimal sameYearElectricity = getTotal(sameDayLastYearList);
+                        // BigDecimal dayElectricity = getTotal(dayList);
+                        // BigDecimal monthElectricity = getTotal(monthList);
+                        // BigDecimal yearElectricity = getTotal(yearList);
+                        // BigDecimal sameDayElectricity = getTotal(yesterday);
+                        // BigDecimal sameMonthElectricity = getTotal(sameDayLastMonthList);
+                        // BigDecimal sameYearElectricity = getTotal(sameDayLastYearList);
 
                         // 基本费用
-                        BigDecimal dayBasic = getBasicExpenses(dayList, cfg, siteId, dayElectricity);
-                        BigDecimal monthBasic = getBasicExpenses(monthList, cfg, siteId, monthElectricity);
-                        BigDecimal yearBasic = getBasicExpenses(yearList, cfg, siteId, yearElectricity);
-                        BigDecimal sameDayBasic = getBasicExpenses(yesterday, cfg, siteId, sameDayElectricity);
-                        BigDecimal sameMonthBasic = getBasicExpenses(sameDayLastMonthList, cfg, siteId, sameMonthElectricity);
-                        BigDecimal sameYearBasic = getBasicExpenses(sameDayLastYearList, cfg, siteId, sameYearElectricity);
+                        // BigDecimal dayBasic = getBasicExpenses(dayList, cfg, siteId, dayElectricity);
+                        // BigDecimal monthBasic = getBasicExpenses(monthList, cfg, siteId, monthElectricity);
+                        // BigDecimal yearBasic = getBasicExpenses(yearList, cfg, siteId, yearElectricity);
+                        // BigDecimal sameDayBasic = getBasicExpenses(yesterday, cfg, siteId, sameDayElectricity);
+                        // BigDecimal sameMonthBasic = getBasicExpenses(sameDayLastMonthList, cfg, siteId, sameMonthElectricity);
+                        // BigDecimal sameYearBasic = getBasicExpenses(sameDayLastYearList, cfg, siteId, sameYearElectricity);
 
                         // 3. 费用汇总
-                        BigDecimal dayCost = calculateTotalCost(calculateTimeFee(siteId, today, 1));//.add(dayBasic);
-                        BigDecimal monthCost = calculateTotalCost(calculateTimeFee(siteId, today, 2));
-                        BigDecimal yearCost = calculateTotalCost(calculateTimeFee(siteId, today, 3));
+                        BigDecimal dayCost = siteFeeCacheService.getTotalCost(siteId, ranges.getToday(), 1);//.add(dayBasic);
+                        BigDecimal monthCost = siteFeeCacheService.getTotalCost(siteId, ranges.getToday(), 2);
+                        BigDecimal yearCost = siteFeeCacheService.getTotalCost(siteId, ranges.getToday(), 3);
 
-                        BigDecimal yesterdayCost = calculateTotalCost(calculateTimeFee(siteId, yesterday1, 1));//.add(sameDayBasic);
-                        BigDecimal sameMonthCost = calculateTotalCost(calculateTimeFee(siteId, lastMonth, 22));
-                        BigDecimal sameYearCost = calculateTotalCost(calculateTimeFee(siteId, lastYear, 33));
+                        BigDecimal yesterdayCost = siteFeeCacheService.getTotalCost(siteId, ranges.getYesterdayRange()[0], 1);//.add(sameDayBasic);
+                        BigDecimal sameMonthCost = siteFeeCacheService.getTotalCost(siteId, ranges.getSameMonthRange()[1].toLocalDate(), 22);
+                        BigDecimal sameYearCost = siteFeeCacheService.getTotalCost(siteId, ranges.getSameYearRange()[1].toLocalDate(), 33);
 
                         // 4. 设置值
                         vo.setTodayCost(dayCost);
@@ -1638,20 +1646,11 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         vo.setLastYearCost(sameYearCost);
 
                         // 日环比
-                        vo.setDayCostRingRatio(yesterdayCost.compareTo(BigDecimal.ZERO) != 0
-                                ? dayCost.subtract(yesterdayCost)
-                                .divide(yesterdayCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setDayCostRingRatio(ringRatio(dayCost, yesterdayCost));
                         // 月环比
-                        vo.setMonthCostRingRatio(sameMonthCost.compareTo(BigDecimal.ZERO) != 0
-                                ? monthCost.subtract(sameMonthCost)
-                                .divide(sameMonthCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setMonthCostRingRatio(ringRatio(monthCost, sameMonthCost));
                         // 年环比
-                        vo.setYearCostRingRatio(sameYearCost.compareTo(BigDecimal.ZERO) != 0
-                                ? yearCost.subtract(sameYearCost)
-                                .divide(sameYearCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setYearCostRingRatio(ringRatio(yearCost, sameYearCost));
                     }
                 } else if (!data173.isEmpty()) {
                     dayList173 = get173Data(data173, time, 1);
@@ -1701,24 +1700,20 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         // 电耗
                         vo.setTodayElectricity(getTotal(dayList));
                         // 昨日同期电耗
-                        vo.setYesterdayElectricity(getTotal(yesterday));
+                        // vo.setYesterdayElectricity(getTotal(yesterday));
+                        BigDecimal yesterdaySameTimeElectricity = getTotal(yesterday);
                         // 能耗
-                        vo.setToday(getTotal(dayList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setYesterday(getTotal(yesterday).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
+                        vo.setToday(scale(vo.getTodayElectricity(), coefficient));
+                        vo.setYesterday(scale(yesterdaySameTimeElectricity, coefficient));
                         // 日同期环比
-                        vo.setTodayRingRatio(getRingRatio(dayList, yesterday));
+                        vo.setTodayRingRatio(ringRatio(vo.getTodayElectricity(), yesterdaySameTimeElectricity));
 
                         // 上一年同日能耗
                         List<HtAnalogData> analogData = getAnalogData(data183.get(0), time.minusYears(1), 1);
-                        BigDecimal sameDayLastYearEnergy = getTotal(analogData).multiply(coefficient).setScale(3, RoundingMode.HALF_UP);
+                        BigDecimal sameDayLastYearEnergy = scale(getTotal(analogData), coefficient);
                         vo.setYearOnYear(sameDayLastYearEnergy);
                         // 日能耗同比
-                        vo.setYearOnYearPercent(
-                                sameDayLastYearEnergy.compareTo(BigDecimal.ZERO) == 0
-                                        ? BigDecimal.ZERO
-                                        : getTotal(dayList).multiply(coefficient)
-                                        .divide(sameDayLastYearEnergy, 3, RoundingMode.HALF_UP)
-                        );
+                        vo.setYearOnYearPercent(ringRatio(vo.getToday(), sameDayLastYearEnergy));
                     } else if (!data173.isEmpty()) {
                         dayList173 = get173Data(data173, time, 1);
                         yesterday173 = get173Data(data173, time.minusDays(1), 1);
@@ -1745,7 +1740,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         vo.setTodayElectricity(getTotal(dayList));
 
                         dayList = getAnalogData(data183.get(0), time, 1);
-                        yesterday = getAnalogData(data183.get(0), time.minusDays(1), 1);
+                        yesterday = getAnalogData(data183.get(0), ranges.getYesterdayTime(), 1);
 
                         BigDecimal dayElectricity = getTotal(dayList);
                         BigDecimal sameDayElectricity = getTotal(yesterday);
@@ -1755,20 +1750,15 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         BigDecimal yesterdayCost = calculateTotalCost(calculateTimeFee(siteId, yesterday1, 1)).add(sameDayBasic);
                         vo.setTodayCost(dayCost);
                         vo.setYesterdayCost(yesterdayCost);
-                        vo.setDayCostRingRatio(yesterdayCost.compareTo(BigDecimal.ZERO) != 0
-                                ? dayCost.subtract(yesterdayCost)
-                                .divide(yesterdayCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setDayCostRingRatio(ringRatio(dayCost, yesterdayCost));
 
                         // 上一年同日能耗
-                        List<HtAnalogData> sameDayLastYear = getAnalogData(data183.get(0), time.minusYears(1), 1);
+                        List<HtAnalogData> sameDayLastYear = getAnalogData(data183.get(0), ranges.getLastYearSameDay()[0], 1);
                         BigDecimal sameDayLastYearElectricity = getTotal(sameDayLastYear);
                         BigDecimal sameDayLastYearBasic = getBasicExpenses(sameDayLastYear, cfg, siteId, sameDayLastYearElectricity);
                         BigDecimal sameDayLastYearCost = calculateTotalCost(calculateTimeFee(siteId, lastYear, 1)).add(sameDayLastYearBasic);
                         vo.setYearOnYear(sameDayLastYearCost);
-                        vo.setYearOnYearPercent(sameDayLastYearCost.compareTo(BigDecimal.ZERO) != 0
-                                ? dayCost.subtract(sameDayLastYearCost)
-                                .divide(sameDayLastYearCost, 2, RoundingMode.HALF_UP) : BigDecimal.ZERO);
+                        vo.setYearOnYearPercent(ringRatio(dayCost, sameDayLastYearCost));
 
                     } else if (!data173.isEmpty()) {
 
@@ -1778,53 +1768,53 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
             // 月
             case "month":
                 if (!data183.isEmpty()) {
-                    monthList = getAnalogData(data183.get(0), time, 2);
-                    sameDayLastMonthList = getAnalogData(data183.get(0), time.minusMonths(1), 22);
+                    String deviceCode183 = data183.get(0);
+                    Map<String, BigDecimal> result = analogCache.get(deviceCode183, ranges);
+                    BigDecimal month1 = result.get("month");
+                    // 电耗
+                    vo.setMonthElectricity(month1);
+                    // monthList = getAnalogData(deviceCode183, time, 2);
+                    // sameDayLastMonthList = getAnalogData(deviceCode183, time.minusMonths(1), 22);
                     // 上一年同月能耗
-                    List<HtAnalogData> analogData = getAnalogData(data183.get(0), time.minusYears(1), 2);
+                    // List<HtAnalogData> analogData = getAnalogData(deviceCode183, time.minusYears(1), 2);
                     if (consume == 1) {
-                        // 电耗
-                        vo.setMonthElectricity(getTotal(monthList));
-                        vo.setLastMonthElectricity(getTotal(sameDayLastMonthList));
+                        BigDecimal sameMonth = result.get("same_month");
+                        BigDecimal lastYearSameMonth = result.get("last_year_same_month");
+                        // 上月同期电耗
+                        vo.setLastMonthElectricity(sameMonth);
                         // 能耗
-                        vo.setMonth(getTotal(monthList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setLastMonth(getTotal(sameDayLastMonthList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
+                        vo.setMonth(scale(month1, coefficient));
+                        vo.setLastMonth(scale(sameMonth, coefficient));
                         // 同期环比
-                        vo.setMonthRingRatio(getRingRatio(monthList, sameDayLastMonthList));
+                        vo.setMonthRingRatio(ringRatio(month1, sameMonth));
 
-                        BigDecimal sameMonthLastYearEnergy = getTotal(analogData).multiply(coefficient).setScale(3, RoundingMode.HALF_UP);
-                        vo.setYearOnYear(sameMonthLastYearEnergy);
+                        // BigDecimal sameMonthLastYearEnergy = getTotal(analogData).multiply(coefficient).setScale(3, RoundingMode.HALF_UP);
+                        vo.setYearOnYear(lastYearSameMonth);
                         // 月能耗同比
-                        vo.setYearOnYearPercent(
-                                sameMonthLastYearEnergy.compareTo(BigDecimal.ZERO) == 0
-                                        ? BigDecimal.ZERO
-                                        : getTotal(monthList).multiply(coefficient)
-                                        .divide(sameMonthLastYearEnergy, 3, RoundingMode.HALF_UP)
-                        );
+                        vo.setYearOnYearPercent(ringRatio(month1, lastYearSameMonth));
                     } else if (consume == 2) {
-                        vo.setMonthElectricity(getTotal(monthList));
+                        // vo.setMonthElectricity(getTotal(monthList));
 
-                        ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
-                        BigDecimal monthBasic = getBasicExpenses(monthList, cfg, siteId, getTotal(monthList));
-                        BigDecimal sameMonthBasic = getBasicExpenses(sameDayLastMonthList, cfg, siteId, getTotal(sameDayLastMonthList));
-                        BigDecimal monthCost = calculateTotalCost(calculateTimeFee(siteId, today, 2)).add(monthBasic);
-                        BigDecimal sameMonthCost = calculateTotalCost(calculateTimeFee(siteId, lastMonth, 22)).add(sameMonthBasic);
+                        BigDecimal monthCost = siteFeeCacheService.getTotalCost(siteId, ranges.getToday(), 2);
+                        BigDecimal sameMonthCost = siteFeeCacheService.getTotalCost(siteId, ranges.getSameMonthRange()[0].toLocalDate(), 22);
+                        BigDecimal sameMonthLastYearCost = siteFeeCacheService.getTotalCost(siteId, ranges.getLastYearSameMonth()[0].toLocalDate(), 4);
+
+                        // ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
+                        // BigDecimal monthBasic = getBasicExpenses(monthList, cfg, siteId, getTotal(monthList));
+                        // BigDecimal sameMonthBasic = getBasicExpenses(sameDayLastMonthList, cfg, siteId, getTotal(sameDayLastMonthList));
+                        // BigDecimal monthCost = calculateTotalCost(calculateTimeFee(siteId, today, 2)).add(monthBasic);
+                        // BigDecimal sameMonthCost = calculateTotalCost(calculateTimeFee(siteId, lastMonth, 22)).add(sameMonthBasic);
                         vo.setMonthCost(monthCost);
+                        // 上月同期费用
                         vo.setLastMonthCost(sameMonthCost);
-                        vo.setMonthCostRingRatio(sameMonthCost.compareTo(BigDecimal.ZERO) != 0
-                                ? monthCost.subtract(sameMonthCost)
-                                .divide(sameMonthCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setMonthCostRingRatio(ringRatio(monthCost, sameMonthCost));
                         // 上一年同月能耗
-                        List<HtAnalogData> sameMonthLastYear = getAnalogData(data183.get(0), time.minusYears(1), 2);
-                        BigDecimal sameMonthLastYearElectricity = getTotal(sameMonthLastYear);
-                        BigDecimal sameMonthLastYearBasic = getBasicExpenses(sameMonthLastYear, cfg, siteId, sameMonthLastYearElectricity);
-                        BigDecimal sameMonthLastYearCost = calculateTotalCost(calculateTimeFee(siteId, lastMonth, 2)).add(sameMonthLastYearBasic);
+                        // List<HtAnalogData> sameMonthLastYear = getAnalogData(deviceCode183, time.minusYears(1), 2);
+                        // BigDecimal sameMonthLastYearElectricity = getTotal(sameMonthLastYear);
+                        // BigDecimal sameMonthLastYearBasic = getBasicExpenses(sameMonthLastYear, cfg, siteId, sameMonthLastYearElectricity);
+                        // BigDecimal sameMonthLastYearCost = calculateTotalCost(calculateTimeFee(siteId, lastMonth, 2)).add(sameMonthLastYearBasic);
                         vo.setYearOnYear(sameMonthLastYearCost);
-                        vo.setYearOnYearPercent(sameMonthLastYearCost.compareTo(BigDecimal.ZERO) != 0
-                                ? monthCost.subtract(sameMonthLastYearCost)
-                                .divide(sameMonthLastYearCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setYearOnYearPercent(ringRatio(monthCost, sameMonthLastYearCost));
                     }
 
                 } else if (!data173.isEmpty()) {
@@ -1851,39 +1841,44 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
             // 年
             case "year":
                 if (!data183.isEmpty()) {
-                    yearList = getAnalogData(data183.get(0), time, 3);
-                    sameDayLastYearList = getAnalogData(data183.get(0), time.minusYears(1), 33);
+                    String deviceCode183 = data183.get(0);
+                    Map<String, BigDecimal> result = analogCache.get(deviceCode183, ranges);
+                    BigDecimal year1 = result.get("year");
+                    // yearList = getAnalogData(deviceCode183, time, 3);
+                    // sameDayLastYearList = getAnalogData(deviceCode183, time.minusYears(1), 33);
 
                     if (consume == 1) {
+                        BigDecimal sameYear = result.get("same_year");
                         // 电耗
-                        vo.setYearElectricity(getTotal(yearList));
+                        vo.setYearElectricity(year1);
                         // 去年同期电耗
-                        vo.setLastYearElectricity(getTotal(sameDayLastYearList));
+                        vo.setLastYearElectricity(sameYear);
                         // 能耗
-                        vo.setYear(getTotal(yearList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setLastYear(getTotal(sameDayLastYearList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
+                        vo.setYear(scale(year1, coefficient));
+                        vo.setLastYear(scale(sameYear, coefficient));
                         // 环比
-                        vo.setYearRingRatio(getRingRatio(yearList, sameDayLastYearList));
+                        vo.setYearRingRatio(ringRatio(year1, sameYear));
                         // 同比
-                        vo.setYearOnYear(getTotal(sameDayLastYearList).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
-                        vo.setYearOnYearPercent(getRingRatio(yearList, sameDayLastYearList));
+                        vo.setYearOnYear(vo.getYear());
+                        vo.setYearOnYearPercent(vo.getYearCostRingRatio());
                         // 上一年能耗
-                        // List<HtAnalogData> analogData = getAnalogData(data183.get(0), time.minusYears(1), 3);
+                        // List<HtAnalogData> analogData = getAnalogData(deviceCode183, time.minusYears(1), 3);
                         // vo.setYearOnYear(getTotal(analogData).multiply(coefficient).setScale(3, RoundingMode.HALF_UP));
                     } else if (consume == 2) {
-                        vo.setYearElectricity(getTotal(yearList));
+                        // vo.setYearElectricity(getTotal(yearList));
+
+                        BigDecimal yearCost = siteFeeCacheService.getTotalCost(siteId, ranges.getToday(), 3);
+
+                        BigDecimal sameYearCost = siteFeeCacheService.getTotalCost(siteId, ranges.getSameYearRange()[1].toLocalDate(), 33);
 
-                        ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
-                        BigDecimal yearBasic = getBasicExpenses(yearList, cfg, siteId, getTotal(yearList));
-                        BigDecimal sameYearBasic = getBasicExpenses(sameDayLastYearList, cfg, siteId, getTotal(sameDayLastYearList));
-                        BigDecimal yearCost = calculateTotalCost(calculateTimeFee(siteId, today, 3)).add(yearBasic);
-                        BigDecimal sameYearCost = calculateTotalCost(calculateTimeFee(siteId, lastYear, 33)).add(sameYearBasic);
+                        // ElectricityRateConfig cfg = getElectricityRateConfig(siteId);
+                        // BigDecimal yearBasic = getBasicExpenses(yearList, cfg, siteId, getTotal(yearList));
+                        // BigDecimal sameYearBasic = getBasicExpenses(sameDayLastYearList, cfg, siteId, getTotal(sameDayLastYearList));
+                        // BigDecimal yearCost = calculateTotalCost(calculateTimeFee(siteId, today, 3)).add(yearBasic);
+                        // BigDecimal sameYearCost = calculateTotalCost(calculateTimeFee(siteId, lastYear, 33)).add(sameYearBasic);
                         vo.setYearCost(yearCost);
                         vo.setLastYearCost(sameYearCost);
-                        vo.setYearCostRingRatio(sameYearCost.compareTo(BigDecimal.ZERO) != 0
-                                ? yearCost.subtract(sameYearCost)
-                                .divide(sameYearCost, 2, RoundingMode.HALF_UP)
-                                : BigDecimal.ZERO);
+                        vo.setYearCostRingRatio(ringRatio(yearCost, sameYearCost));
                         vo.setYearOnYear(sameYearCost);
                         vo.setYearOnYearPercent(vo.getYearCostRingRatio());
                     }
@@ -1912,9 +1907,18 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         return vo;
     }
 
+    // 环比计算
+    public static BigDecimal ringRatio(BigDecimal value, BigDecimal same) {
+        return same.compareTo(ZERO) == 0 ? ZERO : value.subtract(same).divide(same, 2, RoundingMode.HALF_UP);
+    }
+
+    // 能耗转换
+    public static BigDecimal scale(BigDecimal value, BigDecimal coefficient) {
+        return value == null ? ZERO : value.multiply(coefficient).setScale(3, RoundingMode.HALF_UP);
+    }
+
     // 获取173数据
     public List<HtAnalog173Data> get173Data(List<String> deviceCodeList, LocalDateTime localDateTime, int timeType) {
-
         LocalDate data = localDateTime.toLocalDate();
         LocalDate monthFirstDay = data.with(TemporalAdjusters.firstDayOfMonth());
         LocalDate monthLastDay = data.with(TemporalAdjusters.lastDayOfMonth());
@@ -1968,7 +1972,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      */
     public BigDecimal get173Total(List<HtAnalog173Data> dataList) {
         if (CollectionUtils.isEmpty(dataList)) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
         return dataList.stream()
                 .collect(Collectors.groupingBy(HtAnalog173Data::getDeviceName))
@@ -1978,8 +1982,8 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         .sorted(Comparator.comparing(HtAnalog173Data::getDataTime))
                         .map(HtAnalog173Data::getEpp)
                         .reduce((first, last) -> last.subtract(first))
-                        .orElse(BigDecimal.ZERO))
-                .reduce(BigDecimal.ZERO, BigDecimal::add);
+                        .orElse(ZERO))
+                .reduce(ZERO, BigDecimal::add);
     }
 
     /**
@@ -1996,8 +2000,8 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         BigDecimal daySum = calcDeviceDiffSum(dayList173, localDateTime);
         BigDecimal yearSum = calcDeviceDiffSum(yearList173, localDateTime);
 
-        if (yearSum.compareTo(BigDecimal.ZERO) == 0) {
-            return BigDecimal.ZERO;
+        if (yearSum.compareTo(ZERO) == 0) {
+            return ZERO;
         }
 
         return daySum
@@ -2014,7 +2018,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                                          LocalDateTime localDateTime) {
 
         if (CollectionUtils.isEmpty(list)) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
 
         return list.stream()
@@ -2026,8 +2030,8 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                         .sorted(Comparator.comparing(HtAnalog173Data::getDataTime))
                         .map(HtAnalog173Data::getEpp)
                         .reduce((first, last) -> last.subtract(first))
-                        .orElse(BigDecimal.ZERO))
-                .reduce(BigDecimal.ZERO, BigDecimal::add);
+                        .orElse(ZERO))
+                .reduce(ZERO, BigDecimal::add);
     }
 
     /**
@@ -2035,9 +2039,10 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      */
     public BigDecimal getTotal(List<HtAnalogData> dataList) {
         if (dataList == null || dataList.isEmpty()) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
 
+        // 获取最大最小时间,已按照时间顺序倒序
         HtAnalogData max = dataList.get(0), min = dataList.get(dataList.size() - 1);
         // for (int i = 1, size = dataList.size(); i < size; i++) {
         //     HtAnalogData d = dataList.get(i);
@@ -2048,6 +2053,10 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         //         min = d;
         //     }
         // }
+        if (max.getEpp() == null || min.getEpp() == null ||
+                max.getEpp().compareTo(min.getEpp()) == 0 || min.getEpp().compareTo(ZERO) == 0) {
+            return ZERO;
+        }
         return max.getEpp().subtract(min.getEpp());
     }
 
@@ -2071,14 +2080,6 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      */
     public List<HtAnalogData> getAnalogData(String deviceCode, LocalDateTime localDateTime, int timeType) {
         LocalDate data = localDateTime.toLocalDate();
-        LocalDate monthFirstDay = data.with(TemporalAdjusters.firstDayOfMonth());
-        LocalDateTime monthFirstTime = LocalDateTime.of(monthFirstDay, LocalTime.MIN);
-        LocalDate monthMonthLastDay = data.with(TemporalAdjusters.lastDayOfMonth());
-        LocalDate yearFirstDay = data.with(TemporalAdjusters.firstDayOfYear());
-        LocalDate yearLastDay = data.with(TemporalAdjusters.lastDayOfYear());
-        LocalDateTime yearFirstTime = LocalDateTime.of(yearFirstDay, LocalTime.MIN);
-        LocalDateTime dayFirstTime = LocalDateTime.of(data, LocalTime.MIN);
-
         LambdaQueryWrapper<HtAnalogData> queryWrapper = Wrappers.lambdaQuery();
         queryWrapper.select(HtAnalogData::getDeviceName,
                 HtAnalogData::getFreezingTime,
@@ -2092,23 +2093,30 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                 break;
             // 月
             case 2:
+                LocalDate monthFirstDay = data.with(TemporalAdjusters.firstDayOfMonth());
+                LocalDate monthMonthLastDay = data.with(TemporalAdjusters.lastDayOfMonth());
                 queryWrapper.between(HtAnalogData::getFreezingTime, monthFirstDay, monthMonthLastDay);
                 break;
             // 年
             case 3:
+                LocalDate yearFirstDay = data.with(TemporalAdjusters.firstDayOfYear());
+                LocalDate yearLastDay = data.with(TemporalAdjusters.lastDayOfYear());
                 queryWrapper.between(HtAnalogData::getFreezingTime, yearFirstDay, yearLastDay);
                 break;
 
             // 昨日同期
             case 11:
+                LocalDateTime dayFirstTime = LocalDateTime.of(data, LocalTime.MIN);
                 queryWrapper.between(HtAnalogData::getDataTime, dayFirstTime.minusDays(1), localDateTime.minusDays(1));
                 break;
             // 上月同期
             case 22:
+                LocalDateTime monthFirstTime = localDateTime.with(TemporalAdjusters.firstDayOfMonth());
                 queryWrapper.between(HtAnalogData::getDataTime, monthFirstTime.minusMonths(1), localDateTime.minusMonths(1));
                 break;
             // 上年同期
             case 33:
+                LocalDateTime yearFirstTime = localDateTime.with(TemporalAdjusters.firstDayOfYear());
                 queryWrapper.between(HtAnalogData::getDataTime, yearFirstTime.minusYears(1), localDateTime.minusYears(1));
                 break;
             default:
@@ -2131,7 +2139,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
 //                                       List<String> timeList) {
 //
 //     if (source == null || source.isEmpty() || timeList == null || timeList.isEmpty()) {
-//         return Collections.nCopies(timeList == null ? 0 : timeList.size(), BigDecimal.ZERO);
+//         return Collections.nCopies(timeList == null ? 0 : timeList.size(), ZERO);
 //     }
 //
 //     // 提取时间“槽位”的映射函数
@@ -2160,7 +2168,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
 //             .collect(Collectors.groupingBy(slotExtractor));
 //
 //     // 构造结果,顺序与 timeList 保持一致
-//     List<BigDecimal> result = new ArrayList<>(Collections.nCopies(timeList.size(), BigDecimal.ZERO));
+//     List<BigDecimal> result = new ArrayList<>(Collections.nCopies(timeList.size(), ZERO));
 //
 //     grouped.forEach((slot, list) -> {
 //         if (slot >= 0 && slot < timeList.size() && list.size() >= 2) {
@@ -2183,7 +2191,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
     public BigDecimal getRingRatio(List<HtAnalogData> curList,
                                    List<HtAnalogData> preList) {
         if (curList.isEmpty() || preList.isEmpty()) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
 
         // 已倒序:0 是最新,size-1 是最早
@@ -2192,8 +2200,8 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         BigDecimal preDiff = preList.get(0).getEpp()
                 .subtract(preList.get(preList.size() - 1).getEpp());
 
-        if (preDiff.compareTo(BigDecimal.ZERO) == 0) {
-            return BigDecimal.ZERO;
+        if (preDiff.compareTo(ZERO) == 0) {
+            return ZERO;
         }
 
         // 计算环比:(cur - pre) / pre
@@ -2308,9 +2316,9 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         List<String> devices183 = new ArrayList<>();
         List<String> devices173 = new ArrayList<>();
 
-        BigDecimal current = BigDecimal.ZERO;
-        BigDecimal previous = BigDecimal.ZERO;
-        BigDecimal ratio = BigDecimal.ZERO;
+        BigDecimal current = ZERO;
+        BigDecimal previous = ZERO;
+        BigDecimal ratio = ZERO;
 
         List<Device> devices = getDevices(siteId);
         if (devices.isEmpty()) {
@@ -2371,10 +2379,10 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         SiteDynamicProperties siteDynamicProperties = siteDynamicPropertiesService.selectOne(siteId);
         if (siteDynamicProperties == null) {
             log.warn("站点{}未配置能耗属性等数据", siteId);
-            vo.setRatio(BigDecimal.ZERO);
+            vo.setRatio(ZERO);
             vo.setUnit("energy".equals(queryType) ? "吨标准煤" : "electric".equals(queryType) ? "kwh" : "cost".equals(queryType) ? "元" : "kWh");
-            vo.setCurrent(BigDecimal.ZERO);
-            vo.setPrevious(BigDecimal.ZERO);
+            vo.setCurrent(ZERO);
+            vo.setPrevious(ZERO);
             vo.setTimeKeys(timeKeys);
             vo.setAmountList(zeroList(timeKeys.size()));
             return vo;
@@ -2404,14 +2412,14 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                     vo.setAmountList(dayCostList);
                     vo.setCurrent(dayCostList.stream()
                             .filter(Objects::nonNull)
-                            .reduce(BigDecimal.ZERO, BigDecimal::add));
+                            .reduce(ZERO, BigDecimal::add));
                     break;
                 case "month": {
                     List<SiteElectricityRecord> siteElectricityRecords = calculateTimeFee(siteId, localDate, 2);
                     vo.setCurrent(siteElectricityRecords.stream()
                             .filter(Objects::nonNull)
                             .map(SiteElectricityRecord::getTotalCost)
-                            .reduce(BigDecimal.ZERO, BigDecimal::add));
+                            .reduce(ZERO, BigDecimal::add));
 
                     List<SiteElectricityRecord> monthRecords = calculateTimeFee(siteId, localDate, 2);
                     vo.setAmountList(getMonthYearList(monthRecords, timeKeys, "month"));
@@ -2422,7 +2430,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                     vo.setCurrent(siteElectricityRecords.stream()
                             .filter(Objects::nonNull)
                             .map(SiteElectricityRecord::getTotalCost)
-                            .reduce(BigDecimal.ZERO, BigDecimal::add));
+                            .reduce(ZERO, BigDecimal::add));
 
                     List<SiteElectricityRecord> monthRecords = calculateTimeFee(siteId, localDate, 3);
                     vo.setAmountList(getMonthYearList(monthRecords, timeKeys, "year"));
@@ -2454,7 +2462,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
             Map<YearMonth, BigDecimal> monthMap = records.stream()
                     .collect(Collectors.groupingBy(
                             r -> YearMonth.from(r.getDate()),
-                            Collectors.reducing(BigDecimal.ZERO,
+                            Collectors.reducing(ZERO,
                                     SiteElectricityRecord::getTotalCost,
                                     BigDecimal::add)));
 
@@ -2584,7 +2592,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         for (LocalDateTime hourStart : timeKeys) {
             List<HtAnalogData> oneHour = groupByHour.get(hourStart);
             if (oneHour == null || oneHour.isEmpty()) {
-                result.add(BigDecimal.ZERO);
+                result.add(ZERO);
             } else {
                 // 升序排序
                 oneHour.sort(Comparator.comparing(HtAnalogData::getDataTime));
@@ -2640,7 +2648,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
 
             List<HtAnalogData> oneGroup = groupByPeriod.get(periodStart);
             if (oneGroup == null || oneGroup.isEmpty()) {
-                result.add(BigDecimal.ZERO);
+                result.add(ZERO);
             } else {
                 oneGroup.sort(Comparator.comparing(HtAnalogData::getDataTime));
                 BigDecimal first = oneGroup.get(0).getEpp();
@@ -2661,7 +2669,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                                             List<LocalDateTime> timeKeys) {
         if (periodPastOrPresent == 1) {
             // 全部补 0
-            return Collections.nCopies(size, BigDecimal.ZERO);
+            return Collections.nCopies(size, ZERO);
         }
 
         LocalDateTime nowPeriodStart = alignToPeriod(LocalDateTime.now(), period);
@@ -2670,7 +2678,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
             if (periodStart.isAfter(nowPeriodStart)) {
                 list.add(null);
             } else {
-                list.add(BigDecimal.ZERO);
+                list.add(ZERO);
             }
         }
         return list;
@@ -2694,7 +2702,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         for (LocalDateTime hourStart : timeKeys) {
             List<HtAnalog173Data> oneHour = groupByHour.get(hourStart);
             if (oneHour == null || oneHour.isEmpty()) {
-                result.add(BigDecimal.ZERO);
+                result.add(ZERO);
             } else {
                 oneHour.sort(Comparator.comparing(HtAnalog173Data::getDataTime));
                 BigDecimal first = oneHour.get(0).getEpp();
@@ -2734,7 +2742,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         for (LocalDateTime hourStart : timeKeys) {
             List<HtAnalog173Data> oneHour = groupByHour.get(hourStart);
             if (oneHour == null || oneHour.isEmpty()) {
-                result.add(BigDecimal.ZERO);
+                result.add(ZERO);
             } else {
                 oneHour.sort(Comparator.comparing(HtAnalog173Data::getDataTime));
                 BigDecimal first = oneHour.get(0).getEpp();
@@ -2762,7 +2770,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
     /** 用“最后一条 - 第一条”计算周期总量 */
     private BigDecimal calcTotal(List<HtAnalogData> list) {
         if (list.size() < 2) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
         return list.get(0).getEpp()
                 .subtract(list.get(list.size() - 1).getEpp());
@@ -2783,7 +2791,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
 
     /** 返回一个固定长度的 0 列表,避免空数组 */
     private List<BigDecimal> zeroList(int size) {
-        return new ArrayList<>(Collections.nCopies(size, BigDecimal.ZERO));
+        return new ArrayList<>(Collections.nCopies(size, ZERO));
     }
 
     /** 获取一个站点的费率配置-基本电价配置 */
@@ -2810,7 +2818,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      */
     public BigDecimal getBasicExpenses(List<HtAnalogData> list, ElectricityRateConfig config, Integer siteId, BigDecimal electricity) {
 
-        BigDecimal basicExpenses = BigDecimal.ZERO;
+        BigDecimal basicExpenses = ZERO;
         // 超过的百分比
         BigDecimal excessPercentage = config.getExcessPercentage();
         // 超过部分的计费倍数
@@ -2842,7 +2850,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         }
 
         BigDecimal additionalElectricityPrice = additionalElectricityPrice(siteId, electricity);
-        if (additionalElectricityPrice.compareTo(BigDecimal.ZERO) > 0) {
+        if (additionalElectricityPrice.compareTo(ZERO) > 0) {
             basicExpenses = basicExpenses.add(additionalElectricityPrice);
         }
 
@@ -2858,7 +2866,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         return getSiteInformation(siteId).stream()
                 .map(SiteInformation::getAdditionalElectricityPrice)
                 .filter(Objects::nonNull)
-                .reduce(BigDecimal.ZERO, BigDecimal::add)
+                .reduce(ZERO, BigDecimal::add)
                 .multiply(electricity)
                 .setScale(2, RoundingMode.HALF_UP);
     }
@@ -2904,7 +2912,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      * @return 实际最大需量电费
      */
     private BigDecimal calculateActualMaxDemand(List<HtAnalogData> dataList) {
-        BigDecimal maxDemand = BigDecimal.ZERO;
+        BigDecimal maxDemand = ZERO;
 
         for (int i = 0; i < dataList.size() - 1; i++) {
             HtAnalogData start = dataList.get(i);
@@ -2937,7 +2945,7 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
         // 允许n%浮动
         BigDecimal overDemand = actualDemand.subtract(contractDemand.multiply(new BigDecimal(1).add(excessPercentage)));
 
-        if (overDemand.compareTo(BigDecimal.ZERO) <= 0) {
+        if (overDemand.compareTo(ZERO) <= 0) {
             // 未超105%,按合同计费
             return contractDemand.multiply(demandPrice);
         } else {
@@ -3012,12 +3020,12 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
      */
     private BigDecimal calculateTotalCost(List<SiteElectricityRecord> records) {
         if (records == null || records.isEmpty()) {
-            return BigDecimal.ZERO;
+            return ZERO;
         }
 
         return records.stream()
                 .map(SiteElectricityRecord::getTotalCost)
-                .reduce(BigDecimal.ZERO, BigDecimal::add);
+                .reduce(ZERO, BigDecimal::add);
     }
 
 

+ 29 - 0
fiveep-service/src/main/java/com/bizmatics/service/util/AggCacheKey.java

@@ -0,0 +1,29 @@
+package com.bizmatics.service.util;
+
+import com.bizmatics.model.utils.TimeRangeUtils;
+import lombok.Value;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/28
+ */
+@Value(staticConstructor = "of")
+public final class AggCacheKey {
+    String deviceCode;
+    TimeRangeUtils.TimeRanges ranges;
+
+    public String signature() {
+        return deviceCode + "|"
+                + ranges.getToday() + "|"
+                + ranges.getMonthRange()[0] + "|" + ranges.getMonthRange()[1] + "|"
+                + ranges.getYearRange()[0] + "|" + ranges.getYearRange()[1] + "|"
+                + ranges.getYesterdayRange()[0] + "|"
+                + ranges.getLastMonthRange()[0] + "|" + ranges.getLastMonthRange()[1] + "|"
+                + ranges.getLastYearRange()[0] + "|" + ranges.getLastYearRange()[1] + "|"
+                + ranges.getSameDayRange()[0] + "|"
+                + ranges.getSameMonthRange()[0] + "|" + ranges.getSameMonthRange()[1] + "|"
+                + ranges.getSameYearRange()[0] + "|" + ranges.getSameYearRange()[1];
+    }
+}

+ 111 - 0
fiveep-service/src/main/java/com/bizmatics/service/util/AnalogCache.java

@@ -0,0 +1,111 @@
+package com.bizmatics.service.util;
+
+import com.bizmatics.model.utils.TimeRangeParams;
+import com.bizmatics.model.utils.TimeRangeUtils;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.bizmatics.persistence.mapper.HtAnalogDataMapper;
+import com.github.benmanes.caffeine.cache.stats.CacheStats;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import lombok.var;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Component;
+
+import java.math.BigDecimal;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/28
+ */
+@Slf4j
+@Component
+@RequiredArgsConstructor
+public class AnalogCache {
+
+    private final HtAnalogDataMapper htAnalogDataMapper;
+
+    /**
+     * 本地缓存:expireAfterWrite = 60 秒
+     */
+    private final Cache<String, Map<String, BigDecimal>> cache =
+            Caffeine.newBuilder()
+                    // 根据设备量自行调节
+                    .maximumSize(5_000)
+                    .expireAfterAccess(2, TimeUnit.MINUTES)
+                    .expireAfterWrite(5, TimeUnit.MINUTES)
+                    // 命中率监控
+                    .recordStats()
+                    .build();
+
+    /**
+     * 对外唯一入口
+     */
+    public Map<String, BigDecimal> get(String deviceCode,
+                                       TimeRangeUtils.TimeRanges ranges) {
+        String key = AggCacheKey.of(deviceCode, ranges).signature();
+        // 如果 key 不存在,自动调用 loadFromDb
+        return cache.get(key, k -> loadFromDb(deviceCode, ranges));
+    }
+
+    /**
+     * 回源:真正查数据库
+     */
+    private Map<String, BigDecimal> loadFromDb(String deviceCode,
+                                               TimeRangeUtils.TimeRanges ranges) {
+        TimeRangeParams params = TimeRangeParams.builder()
+                .deviceCode(deviceCode)
+                .today(ranges.getToday())
+                .monthStart(ranges.getMonthRange()[0])
+                .monthEnd(ranges.getMonthRange()[1])
+                .yearStart(ranges.getYearRange()[0])
+                .yearEnd(ranges.getYearRange()[1])
+                .yesterday(ranges.getYesterdayRange()[0])
+                .lastMonthStart(ranges.getLastMonthRange()[0])
+                .lastMonthEnd(ranges.getLastMonthRange()[1])
+                .lastYearStart(ranges.getLastYearRange()[0])
+                .lastYearEnd(ranges.getLastYearRange()[1])
+                .sameDayStart(ranges.getSameDayRange()[0])
+                .sameDayEnd(ranges.getSameDayRange()[1])
+                .sameMonthStart(ranges.getSameMonthRange()[0])
+                .sameMonthEnd(ranges.getSameMonthRange()[1])
+                .sameYearStart(ranges.getSameYearRange()[0])
+                .sameYearEnd(ranges.getSameYearRange()[1])
+                .lastYearSameDayStart(ranges.getLastYearSameDay()[0])
+                .lastYearSameDayEnd(ranges.getLastYearSameDay()[1])
+                .lastYearSameMonthStart(ranges.getLastYearSameMonth()[0])
+                .lastYearSameMonthEnd(ranges.getLastYearSameMonth()[1])
+                .build();
+
+        List<Map<String, Object>> rows = htAnalogDataMapper.aggregateAll(params);
+        return rows.stream()
+                .collect(Collectors.toMap(
+                        r -> (String) r.get("type"),
+                        r -> new BigDecimal(r.get("value").toString())
+                ));
+    }
+
+    /* ---------------- 命中率日志(可选) ---------------- */
+    @Scheduled(fixedDelay = 30_000)
+    public void logCacheStats() {
+        CacheStats stats = cache.stats();
+        long size = cache.estimatedSize();
+
+        // 预先格式化数字
+        String hitRateFormatted = String.format("%.2f", stats.hitRate() * 100);
+        String avgLoadTimeFormatted = String.format("%.2f", stats.averageLoadPenalty() / 1_000_000.0);
+
+        log.info("缓存统计 - 大小: {}, 命中率: {}%, 加载次数: {}, 加载失败: {}, 平均加载时间: {}ms",
+                size,
+                hitRateFormatted,
+                stats.loadCount(),
+                stats.loadFailureCount(),
+                avgLoadTimeFormatted);
+    }
+}

+ 18 - 0
fiveep-service/src/main/java/com/bizmatics/service/util/FeeCacheKey.java

@@ -0,0 +1,18 @@
+package com.bizmatics.service.util;
+
+import lombok.Value;
+
+import java.time.LocalDate;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/28
+ */
+@Value(staticConstructor = "of")
+public class FeeCacheKey {
+    Integer siteId;
+    LocalDate queryDate;
+    Integer queryType;
+}

+ 106 - 0
fiveep-service/src/main/java/com/bizmatics/service/util/SiteFeeCacheService.java

@@ -0,0 +1,106 @@
+package com.bizmatics.service.util;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.bizmatics.model.SiteElectricityRecord;
+import com.bizmatics.persistence.mapper.SiteElectricityRecordMapper;
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import lombok.var;
+import org.springframework.scheduling.annotation.Scheduled;
+import org.springframework.stereotype.Service;
+
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.temporal.TemporalAdjusters;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/8/28
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class SiteFeeCacheService {
+
+    private final SiteElectricityRecordMapper siteElectricityRecordMapper;
+    private static final BigDecimal ZERO = BigDecimal.ZERO;
+
+    /**
+     * 本地缓存:key = siteId + queryDate + queryType
+     * 过期时间 30 秒,可根据业务改成 60 秒或 5 分钟
+     */
+    private final Cache<FeeCacheKey, BigDecimal> cache =
+            Caffeine.newBuilder()
+                    // 按设备量调整
+                    .maximumSize(2_000)
+                    .expireAfterWrite(30, TimeUnit.SECONDS)
+                    .build();
+
+    /** 对外唯一入口:直接返回金额 */
+    public BigDecimal getTotalCost(Integer siteId,
+                                   LocalDate queryDate,
+                                   Integer queryType) {
+        return cache.get(FeeCacheKey.of(siteId, queryDate, queryType),
+                k -> loadAndSum(k.getSiteId(), k.getQueryDate(), k.getQueryType()));
+    }
+
+    /**
+     * 原 calculateTimeFee 逻辑原封不动搬进来
+     */
+    private BigDecimal loadAndSum(Integer siteId, LocalDate queryDate, Integer queryType) {
+        LocalDate monthFirstDay = queryDate.with(TemporalAdjusters.firstDayOfMonth());
+        LocalDate monthLastDay = queryDate.with(TemporalAdjusters.lastDayOfMonth());
+        LocalDate yearFirstDay = queryDate.with(TemporalAdjusters.firstDayOfYear());
+        LocalDate yearLastDay = queryDate.with(TemporalAdjusters.lastDayOfYear());
+        LocalDate lastYearSameMonthFirstDay = queryDate.minusYears(1).with(TemporalAdjusters.firstDayOfMonth());
+
+        LambdaQueryWrapper<SiteElectricityRecord> qw = new LambdaQueryWrapper<>();
+        qw.select(SiteElectricityRecord::getTotalCost, SiteElectricityRecord::getDate)
+                .eq(SiteElectricityRecord::getSiteId, siteId);
+
+        switch (queryType) {
+            case 1:
+                qw.eq(SiteElectricityRecord::getDate, queryDate);
+                break;
+            case 2:
+                qw.between(SiteElectricityRecord::getDate, monthFirstDay, monthLastDay);
+                break;
+            case 3:
+                qw.between(SiteElectricityRecord::getDate, yearFirstDay, yearLastDay);
+                break;
+            case 22:
+                qw.between(SiteElectricityRecord::getDate, monthFirstDay, queryDate);
+                break;
+            case 33:
+                qw.between(SiteElectricityRecord::getDate, yearFirstDay, queryDate);
+                break;
+            case 4:
+                qw.between(SiteElectricityRecord::getDate, lastYearSameMonthFirstDay, queryDate);
+                break;
+        }
+
+        List<SiteElectricityRecord> rows = siteElectricityRecordMapper.selectList(qw);
+
+        if (rows == null || rows.isEmpty()) {
+            return ZERO;
+        }
+        return rows.stream()
+                .map(SiteElectricityRecord::getTotalCost)
+                .reduce(ZERO, BigDecimal::add);
+    }
+
+    /**
+     * 定时任务,用于打印本地缓存的命中率
+     */
+    @Scheduled(fixedDelay = 30_000)
+    public void siteLogStats() {
+        var stats = cache.stats();
+        log.info("本地缓存命中率: {}%", String.format("%.2f", stats.hitRate() * 100));
+    }
+}