浏览代码

分时用电接口开发完成:支路列表、电量趋势、表格统计、导出

fuyuchuan 14 小时之前
父节点
当前提交
1013c3136c
共有 21 个文件被更改,包括 1132 次插入96 次删除
  1. 19 2
      fiveep-controller/src/main/java/com/bizmatics/controller/web/DeviceController.java
  2. 32 8
      fiveep-controller/src/main/java/com/bizmatics/controller/web/HtAnalogDataController.java
  3. 25 0
      fiveep-controller/src/main/java/com/bizmatics/controller/web/StarMarkingEquipmentController.java
  4. 2 2
      fiveep-controller/src/main/resources/application-dev.properties
  5. 6 4
      fiveep-controller/src/main/resources/application-prod.properties
  6. 3 0
      fiveep-model/src/main/java/com/bizmatics/model/DeviceList.java
  7. 30 1
      fiveep-model/src/main/java/com/bizmatics/model/SiteElectricityRecord.java
  8. 4 1
      fiveep-persistence/src/main/java/com/bizmatics/persistence/mapper/DeviceMapper.java
  9. 41 10
      fiveep-persistence/src/main/resources/mapper/mysql/DeviceMapper.xml
  10. 16 0
      fiveep-service/pom.xml
  11. 4 2
      fiveep-service/src/main/java/com/bizmatics/service/DeviceService.java
  12. 27 4
      fiveep-service/src/main/java/com/bizmatics/service/HtAnalogDataService.java
  13. 40 39
      fiveep-service/src/main/java/com/bizmatics/service/impl/DeviceServiceImpl.java
  14. 596 3
      fiveep-service/src/main/java/com/bizmatics/service/impl/HtAnalogDataServiceImpl.java
  15. 45 19
      fiveep-service/src/main/java/com/bizmatics/service/job/SiteDailyElectricityCostTask.java
  16. 44 0
      fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendRequestVO.java
  17. 27 0
      fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendResponseVO.java
  18. 41 0
      fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendVO.java
  19. 53 0
      fiveep-service/src/main/java/com/bizmatics/service/vo/TimeSharingElectricityRequestVO.java
  20. 76 0
      fiveep-service/src/main/java/com/bizmatics/service/vo/TimeSharingElectricityResponseVO.java
  21. 1 1
      pom.xml

+ 19 - 2
fiveep-controller/src/main/java/com/bizmatics/controller/web/DeviceController.java

@@ -68,6 +68,23 @@ public class DeviceController {
         return ApiResult.success(deviceService.deviceList(siteId));
     }
 
+    /**
+     * 分时用电-设备分页列表查询
+     *
+     * @param siteId     站点ID
+     * @param deviceType 设备类型 默认1、支路设备(183 用电设备), 2、分路设备(171、173用电设备)
+     * @param pageNum    页数
+     * @param pageSize   条数
+     * @return
+     */
+    @GetMapping("list")
+    public ApiResult<List<DeviceList>> branch(@RequestParam(value = "siteId") int siteId,
+                                              @RequestParam(value = "deviceType", required = false, defaultValue = "1") int deviceType,
+                                              @RequestParam(value = "pageNum", required = false, defaultValue = "1") int pageNum,
+                                              @RequestParam(value = "pageSize", required = false, defaultValue = "20") int pageSize) {
+        return ApiResult.success(deviceService.deviceBranch(siteId, deviceType, pageNum, pageSize));
+    }
+
 
     /**
      * 数据管理-同比分析报表-右侧设备查询
@@ -242,7 +259,7 @@ public class DeviceController {
      */
     @Log(title = "设备管理-通信设备", businessType = BusinessType.IMPORT)
     @PostMapping("/deviceImport")
-    public ApiResult<Void> deviceImport(@RequestParam("file") MultipartFile file){
+    public ApiResult<Void> deviceImport(@RequestParam("file") MultipartFile file) {
         deviceService.deviceImport(file);
         return ApiResult.success();
     }
@@ -253,7 +270,7 @@ public class DeviceController {
      * @return
      */
     @GetMapping("deviceBoxList")
-    public ApiResult<List<DeviceOneVo>> deviceBoxList(@RequestParam (required = false) Integer siteId) {
+    public ApiResult<List<DeviceOneVo>> deviceBoxList(@RequestParam(required = false) Integer siteId) {
         return ApiResult.success(deviceService.deviceBoxList(siteId));
     }
 

+ 32 - 8
fiveep-controller/src/main/java/com/bizmatics/controller/web/HtAnalogDataController.java

@@ -1,7 +1,7 @@
 package com.bizmatics.controller.web;
 
-
 import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
 import com.bizmatics.model.vo.DataManagementOneVO;
 import com.bizmatics.model.vo.HtAnalogEnergyConsumptionVo;
 import com.bizmatics.model.vo.HtAnalogEnergySegmentedVo;
@@ -9,19 +9,16 @@ import com.bizmatics.model.vo.SingleLoopReportOneVo;
 import com.bizmatics.service.HtAnalogDataService;
 import com.bizmatics.service.aop.BusinessType;
 import com.bizmatics.service.aop.Log;
-import com.bizmatics.service.vo.CommonIcoVO;
-import com.bizmatics.service.vo.HadCountVO;
-import com.bizmatics.service.vo.RealScoreVO;
-import com.bizmatics.service.vo.TimeShareVO;
+import com.bizmatics.service.vo.*;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.format.annotation.DateTimeFormat;
 import org.springframework.web.bind.annotation.*;
 
-import java.time.LocalDate;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.net.URLEncoder;
 import java.time.LocalDateTime;
-import java.time.format.DateTimeFormatter;
-import java.time.temporal.ChronoUnit;
 import java.util.Date;
 import java.util.List;
 
@@ -259,5 +256,32 @@ public class HtAnalogDataController {
         }
         return ApiResult.success(htAnalogDataService.getSegmentedData(siteId, queryPeriod, queryTime, queryType));
     }
+
+    /**
+     * 时间分时电量、电费-表格统计
+     * @param request 请求参数
+     * @return 响应
+     */
+    @PostMapping("page")
+    public ApiResult<CommonPage<TimeSharingElectricityResponseVO>> timeSharingElectricity(@RequestBody TimeSharingElectricityRequestVO request) {
+        return ApiResult.success(htAnalogDataService.timeSharingElectricity(request));
+    }
+
+    /**
+     * 分时用电-电量趋势
+     * @param trendVO 请求参数
+     * @return 响应
+     */
+    @PostMapping("trend")
+    public ApiResult<List<ElectricityTrendResponseVO>> trend(@RequestBody ElectricityTrendRequestVO trendVO) {
+        return ApiResult.success(htAnalogDataService.trend(trendVO));
+    }
+
+    @PostMapping("export")
+    public void export(@RequestBody TimeSharingElectricityRequestVO request, HttpServletResponse response) throws IOException {
+        htAnalogDataService.export(request, response);
+
+    }
+
 }
 

+ 25 - 0
fiveep-controller/src/main/java/com/bizmatics/controller/web/StarMarkingEquipmentController.java

@@ -0,0 +1,25 @@
+package com.bizmatics.controller.web;
+
+import com.bizmatics.persistence.mapper.HtAnalogDataMapper;
+import com.bizmatics.service.HtAnalogDataService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/4
+ */
+@RestController
+@RequestMapping("/starMarkingEquipment")
+public class StarMarkingEquipmentController {
+    @Autowired
+    private HtAnalogDataService htAnalogDataService;
+    @Autowired
+    private HtAnalogDataMapper htAnalogDataMapper;
+
+    // @RequestMapping("/powerLevel")
+    // public
+}

+ 2 - 2
fiveep-controller/src/main/resources/application-dev.properties

@@ -19,10 +19,10 @@ spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDa
 spring.datasource.dynamic.primary=usky-power
 #spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://usky-cloud-mysql:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
 #spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://dianli.usky.cn:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
-#spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://101.133.214.75:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
+spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://101.133.214.75:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
 spring.datasource.dynamic.datasource.usky-power.username=usky
 spring.datasource.dynamic.datasource.usky-power.password=Yt#75Usky
-spring.datasource.dynamic.druid.initial-size=5                                                                       
+spring.datasource.dynamic.druid.initial-size=5
 spring.datasource.dynamic.druid.min-idle=5
 spring.datasource.dynamic.druid.max-active=30
 spring.datasource.dynamic.druid.max-wait=60000

+ 6 - 4
fiveep-controller/src/main/resources/application-prod.properties

@@ -14,14 +14,16 @@ mybatis-plus.configuration.defaultStatementTimeout=3
 mybatis.refresh.enabled=true
 mybatis.refresh.delay-seconds=10
 mybatis.refresh.sleep-seconds=20
+# SQL日志打印
+mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
 # datasource
 spring.autoconfigure.exclude=com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure
 spring.datasource.dynamic.primary=usky-power
 #spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://usky-cloud-mysql:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
 #spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://101.133.214.75:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&characterEncoding=UTF-8
-spring.datasource.dynamic.datasource.mast.url=jdbc:mysql://101.133.214.75:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
-spring.datasource.dynamic.datasource.mast.username=usky
-spring.datasource.dynamic.datasource.mast.password=Yt#75Usky
+spring.datasource.dynamic.datasource.usky-power.url=jdbc:mysql://101.133.214.75:3306/usky-electricity?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
+spring.datasource.dynamic.datasource.usky-power.username=usky
+spring.datasource.dynamic.datasource.usky-power.password=Yt#75Usky
 spring.datasource.dynamic.druid.initial-size=5
 spring.datasource.dynamic.druid.min-idle=5
 spring.datasource.dynamic.druid.max-active=30
@@ -74,7 +76,7 @@ spring.cache.ehcache.config=classpath:ehcache.xml
 # redis
 spring.cache.redis.enabled=true
 spring.redis.database=0
-spring.redis.host=usky-cloud-redist 
+spring.redis.host=usky-cloud-redis
 spring.redis.port=6379
 spring.redis.password=123456
 spring.redis.timeout=10000

+ 3 - 0
fiveep-model/src/main/java/com/bizmatics/model/DeviceList.java

@@ -72,6 +72,9 @@ public class DeviceList implements Serializable {
      */
     private Integer enable;
 
+    /**
+     * 设备状态(0 正常,1 离线,77 告警,4 故障)
+     **/
     @TableField(exist = false)
     private Integer deviceStatus;
 

+ 30 - 1
fiveep-model/src/main/java/com/bizmatics/model/SiteElectricityRecord.java

@@ -1,10 +1,15 @@
 package com.bizmatics.model;
 
 import java.math.BigDecimal;
+
 import com.baomidou.mybatisplus.annotation.IdType;
+
 import java.time.LocalDate;
+
 import com.baomidou.mybatisplus.annotation.TableId;
+
 import java.io.Serializable;
+
 import lombok.Data;
 import lombok.EqualsAndHashCode;
 import lombok.experimental.Accessors;
@@ -22,7 +27,7 @@ import lombok.experimental.Accessors;
 @Accessors(chain = true)
 public class SiteElectricityRecord implements Serializable {
 
-    private static final long serialVersionUID=1L;
+    private static final long serialVersionUID = 1L;
 
     /**
      * 站点日电费记录表ID
@@ -70,5 +75,29 @@ public class SiteElectricityRecord implements Serializable {
      */
     private BigDecimal totalCost;
 
+    /**
+     * 尖 用量(Kwh)
+     */
+    private BigDecimal sharpPeak;
+
+    /**
+     * 峰 用量(Kwh)
+     */
+    private BigDecimal peak;
+
+    /**
+     * 平 用量(Kwh)
+     */
+    private BigDecimal flat;
+
+    /**
+     * 谷 用量(Kwh)
+     */
+    private BigDecimal valley;
+
+    /**
+     * 总电量(Kwh)
+     */
+    private BigDecimal totalElectricity;
 
 }

+ 4 - 1
fiveep-persistence/src/main/java/com/bizmatics/persistence/mapper/DeviceMapper.java

@@ -59,5 +59,8 @@ public interface DeviceMapper extends CrudMapper<Device> {
 
     List<DeviceOneVo> deviceBoxList(@Param("siteId") Integer siteId);
 
-
+    List<DeviceList> deviceBranch(@Param("siteId") int siteId,
+                                  @Param("deviceType") int deviceType,
+                                  @Param("pageNum") int pageNum,
+                                  @Param("pageSize") int pageSize);
 }

+ 41 - 10
fiveep-persistence/src/main/resources/mapper/mysql/DeviceMapper.xml

@@ -4,15 +4,15 @@
 
     <!-- 通用查询映射结果 -->
     <resultMap id="BaseResultMap" type="com.bizmatics.model.Device">
-        <id column="id" property="id" />
-        <result column="device_code" property="deviceCode" />
-        <result column="device_name" property="deviceName" />
-        <result column="site_id" property="siteId" />
-        <result column="device_address" property="deviceAddress" />
-        <result column="device_type" property="deviceType" />
-        <result column="install_time" property="installTime" />
-        <result column="creator" property="creator" />
-        <result column="enable" property="enable" />
+        <id column="id" property="id"/>
+        <result column="device_code" property="deviceCode"/>
+        <result column="device_name" property="deviceName"/>
+        <result column="site_id" property="siteId"/>
+        <result column="device_address" property="deviceAddress"/>
+        <result column="device_type" property="deviceType"/>
+        <result column="install_time" property="installTime"/>
+        <result column="creator" property="creator"/>
+        <result column="enable" property="enable"/>
     </resultMap>
     <select id="selectCount" resultType="java.lang.Integer">
         select count(1)
@@ -43,7 +43,8 @@
         </where>
     </select>
     <select id="list" resultType="com.bizmatics.model.Device">
-        select d.id,d.device_code,d.device_name,d.site_id,d.device_address,d.device_type,d.install_time,ds.device_status as deviceStatus,
+        select d.id,d.device_code,d.device_name,d.site_id,d.device_address,d.device_type,d.install_time,ds.device_status
+        as deviceStatus,
         s.installed_capacity as installedCapacity
         from user_site as us
         inner join device_status as ds
@@ -89,6 +90,36 @@
         </where>
     </select>
 
+    <select id="deviceBranch" resultType="com.bizmatics.model.DeviceList">
+        SELECT
+        a.*, c.rated_voltage, c.rated_current
+        FROM
+        device AS a
+        JOIN device_attribute AS c ON a.device_code = c.monitor_device_code
+        <where>
+            a.enable = 1
+            <if test="siteId != null and siteId != 0">
+                AND a.site_id = #{siteId}
+            </if>
+
+            <choose>
+                <!-- deviceType == 1 -->
+                <when test="deviceType == 1">
+                    AND a.device_type = 1
+                </when>
+                <!-- deviceType == 2 -->
+                <when test="deviceType == 2">
+                    AND a.device_type IN (3,4)
+                </when>
+            </choose>
+        </where>
+
+        <!-- 分页参数 -->
+        <if test="pageNum != null and pageSize != null">
+            LIMIT #{pageSize} OFFSET ${(pageNum - 1) * pageSize}
+        </if>
+    </select>
+
     <select id="CorrespondDeviceList" resultType="com.bizmatics.model.vo.CorrespondDeviceVO">
         SELECT
         a.id,

+ 16 - 0
fiveep-service/pom.xml

@@ -112,6 +112,22 @@
             <groupId>com.google.protobuf</groupId>
             <artifactId>protobuf-java</artifactId>
         </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+
+        <!-- Apache POI -->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi</artifactId>-->
+<!--            <version>5.2.2</version>-->
+<!--        </dependency>-->
+<!--        <dependency>-->
+<!--            <groupId>org.apache.poi</groupId>-->
+<!--            <artifactId>poi-ooxml</artifactId>-->
+<!--            <version>5.4.0</version>-->
+<!--        </dependency>-->
     </dependencies>
 
 

+ 4 - 2
fiveep-service/src/main/java/com/bizmatics/service/DeviceService.java

@@ -37,7 +37,7 @@ public interface DeviceService extends CrudService<Device> {
      */
     DeviceCountVO selectDeviceCountByType(Integer site);
 
-    List<Device> list(Integer userId,Integer siteId,Integer deviceStatus,Date startTime,Date endTime,String type);
+    List<Device> list(Integer userId, Integer siteId, Integer deviceStatus, Date startTime, Date endTime, String type);
 
     List<DeviceList> deviceList(String siteId);
 
@@ -57,7 +57,7 @@ public interface DeviceService extends CrudService<Device> {
 
     void variableCloning(Integer type, String newDeviceCode, String oldDeviceCode, String deviceName);
 
-    List<Device> deviceListOne(Integer siteId,Integer deviceType);
+    List<Device> deviceListOne(Integer siteId, Integer deviceType);
 
     String deviceExport(String deviceName, Integer deviceType, Integer siteId);
 
@@ -67,4 +67,6 @@ public interface DeviceService extends CrudService<Device> {
 
 
     List<DeviceOneVo> deviceBoxList(Integer siteId);
+
+    List<DeviceList> deviceBranch(int siteId, int deviceType, int pageNum, int pageSize);
 }

+ 27 - 4
fiveep-service/src/main/java/com/bizmatics/service/HtAnalogDataService.java

@@ -1,14 +1,14 @@
 package com.bizmatics.service;
 
 import com.bizmatics.common.core.bean.ApiResult;
+import com.bizmatics.common.core.bean.CommonPage;
 import com.bizmatics.common.mvc.base.CrudService;
 import com.bizmatics.model.HtAnalogData;
 import com.bizmatics.model.vo.*;
-import com.bizmatics.service.vo.CommonIcoVO;
-import com.bizmatics.service.vo.HadCountVO;
-import com.bizmatics.service.vo.RealScoreVO;
-import com.bizmatics.service.vo.TimeShareVO;
+import com.bizmatics.service.vo.*;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.util.Date;
@@ -131,4 +131,27 @@ public interface HtAnalogDataService extends CrudService<HtAnalogData> {
      * @return HtAnalogEnergySegmentedVo
      */
     HtAnalogEnergySegmentedVo getSegmentedData(Integer siteId, String queryPeriod, LocalDateTime queryTime, String queryType);
+
+    /**
+     * 分时电量-表格统计
+     *
+     * @param request
+     * @return
+     */
+    CommonPage<TimeSharingElectricityResponseVO> timeSharingElectricity(TimeSharingElectricityRequestVO request);
+
+    /**
+     * 分时电量-趋势统计
+     *
+     * @param trendVO
+     * @return
+     */
+    List<ElectricityTrendResponseVO> trend(ElectricityTrendRequestVO trendVO);
+
+    /**
+     * 分时电量-导出
+     *
+     * @param request
+     */
+    void export(TimeSharingElectricityRequestVO request, HttpServletResponse response) throws IOException;
 }

+ 40 - 39
fiveep-service/src/main/java/com/bizmatics/service/impl/DeviceServiceImpl.java

@@ -43,10 +43,7 @@ import java.io.FileOutputStream;
 import java.io.IOException;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
 
 /**
  * 设备
@@ -143,7 +140,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                     for (int j = 0; j < deviceAnalogVariableList.size(); j++) {
                         if (deviceList.get(i).getId().equals(deviceAnalogVariableList.get(j).getMonitoringEquipment())) {
                             DeviceAnalogVariableList deviceAnalogVariableListThree = new DeviceAnalogVariableList();
-                            deviceAnalogVariableListThree.setVariableCoding(deviceAnalogVariableList.get(j).getVariableCoding()+"_"+deviceList.get(i).getId());
+                            deviceAnalogVariableListThree.setVariableCoding(deviceAnalogVariableList.get(j).getVariableCoding() + "_" + deviceList.get(i).getId());
                             deviceAnalogVariableListThree.setVariableName(deviceAnalogVariableList.get(j).getVariableName());
                             deviceAnalogVariableListThree.setId(deviceAnalogVariableList.get(j).getId());
                             deviceAnalogVariableListTwo.add(deviceAnalogVariableListThree);
@@ -159,7 +156,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
     }
 
     @Override
-    public List<DeviceOneVo> deviceBoxList(Integer siteId){
+    public List<DeviceOneVo> deviceBoxList(Integer siteId) {
         List<DeviceOneVo> deviceOneVo = baseMapper.deviceBoxList(siteId);
         return deviceOneVo;
     }
@@ -198,16 +195,16 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
     @Override
     public CommonPage<CorrespondDeviceVO> correspondDeviceList(String deviceName, int size, int current) {
         List<SysUser> tenantDaya = userMapper.getTenantId(SecurityUtils.getLoginUser().getUser().getUserId());
-        if (tenantDaya.size()<0) {
+        if (tenantDaya.size() < 0) {
             throw new BusinessException("无此租户,请联系管理员");
         }
-        List<CorrespondDeviceVO> correspondDeviceListOne = baseMapper.CorrespondDeviceList(deviceName, null, null,tenantDaya.get(0).getTenantId());
+        List<CorrespondDeviceVO> correspondDeviceListOne = baseMapper.CorrespondDeviceList(deviceName, null, null, tenantDaya.get(0).getTenantId());
         int total = 0;
         if (correspondDeviceListOne.size() > 0) {
             total = correspondDeviceListOne.size();
         }
         int startCurrent = (current - 1) * size;
-        List<CorrespondDeviceVO> correspondDeviceList = baseMapper.CorrespondDeviceList(deviceName, startCurrent, size,tenantDaya.get(0).getTenantId());
+        List<CorrespondDeviceVO> correspondDeviceList = baseMapper.CorrespondDeviceList(deviceName, startCurrent, size, tenantDaya.get(0).getTenantId());
         return new CommonPage<>(correspondDeviceList, total, size, current);
     }
 
@@ -231,7 +228,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
 
     @Override
     public CommonPage<Device> videoMonitoringDeviceList(String deviceName, Integer deviceType, Integer siteId, Integer size, Integer current) {
-        IPage<Device> page = new Page<Device>(current,size);
+        IPage<Device> page = new Page<Device>(current, size);
         LambdaQueryWrapper<Device> queryWrapper = Wrappers.lambdaQuery();
         queryWrapper.eq(Device::getSiteId, siteId).eq(Device::getEnable, 1);
         if (deviceType != null && deviceType != 0) {
@@ -243,13 +240,13 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
 
         page = this.page(page, queryWrapper);
         this.ToCommonPage(page);
-        return new CommonPage<>(page.getRecords(), page.getTotal(), page.getSize(),page.getCurrent() );
+        return new CommonPage<>(page.getRecords(), page.getTotal(), page.getSize(), page.getCurrent());
     }
 
     @Override
     public void variableCloning(Integer type, String newDeviceCode, String oldDeviceCode, String deviceName) {
         SysUser user = SecurityUtils.getLoginUser().getUser();
-        //查询出旧设备配置
+        // 查询出旧设备配置
         LambdaQueryWrapper<Device> queryWrapper = Wrappers.lambdaQuery();
         queryWrapper.eq(Device::getEnable, 1).eq(Device::getDeviceCode, oldDeviceCode);
         List<Device> deviceList = this.list(queryWrapper);
@@ -258,10 +255,10 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
             LambdaQueryWrapper<Device> queryWrapperFour = Wrappers.lambdaQuery();
             queryWrapperFour.eq(Device::getEnable, 1).eq(Device::getDeviceCode, newDeviceCode);
             List<Device> deviceListrFour = this.list(queryWrapperFour);
-            if (deviceListrFour.size()>0){
-                throw new BusinessException(newDeviceCode+"设备编号重复");
-            }else {
-                //设备表新增
+            if (deviceListrFour.size() > 0) {
+                throw new BusinessException(newDeviceCode + "设备编号重复");
+            } else {
+                // 设备表新增
                 Device device = new Device();
                 device.setEnable(1);
                 device.setInstallTime(new Date());
@@ -277,14 +274,14 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                 Integer deviceId = device.getId();
                 String deviceCode = device.getDeviceCode();
                 Integer siteId = device.getSiteId();
-                //设备状态表新增
+                // 设备状态表新增
                 DeviceStatus deviceStatus = new DeviceStatus();
                 deviceStatus.setDeviceStatus(1);
                 deviceStatus.setDeviceCode(deviceCode);
                 deviceStatus.setStatusTime(new Date());
                 deviceStatus.setSiteId(siteId);
                 deviceStatusService.save(deviceStatus);
-                //变量配置查询
+                // 变量配置查询
                 LambdaQueryWrapper<DeviceAnalogVariableList> queryWrapperOne = Wrappers.lambdaQuery();
                 queryWrapperOne.eq(DeviceAnalogVariableList::getStatus, 1).eq(DeviceAnalogVariableList::getCommunicationEquipment, deviceList.get(0).getId());
                 List<DeviceAnalogVariableList> deviceAnalogVariableList = deviceAnalogVariableListService.list(queryWrapperOne);
@@ -293,7 +290,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                         DeviceAnalogVariableList deviceAnalogVariableListOne = new DeviceAnalogVariableList();
                         deviceAnalogVariableListOne.setDeviceCode(newDeviceCode);
                         deviceAnalogVariableListOne.setVariableName(deviceAnalogVariableList.get(i).getVariableName());
-                        deviceAnalogVariableListOne.setVariableCoding(newDeviceCode+"_"+deviceAnalogVariableList.get(i).getVariableCoding().split("_")[1]);
+                        deviceAnalogVariableListOne.setVariableCoding(newDeviceCode + "_" + deviceAnalogVariableList.get(i).getVariableCoding().split("_")[1]);
                         deviceAnalogVariableListOne.setMonitoringEquipment(0);
                         deviceAnalogVariableListOne.setCommunicationEquipment(deviceId);
                         deviceAnalogVariableListOne.setDataAddress(deviceAnalogVariableList.get(i).getDataAddress());
@@ -313,7 +310,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
             queryWrapperOneA.eq(DeviceAnalogVariableList::getStatus, 1).eq(DeviceAnalogVariableList::getCommunicationEquipment, deviceList.get(0).getId());
             List<DeviceAnalogVariableList> deviceAnalogVariableListOne = deviceAnalogVariableListService.list(queryWrapperOneA);
 
-            //克隆设备查询是否存在
+            // 克隆设备查询是否存在
             LambdaQueryWrapper<Device> queryWrapperTwo = Wrappers.lambdaQuery();
             queryWrapperTwo.eq(Device::getEnable, 1).eq(Device::getDeviceCode, newDeviceCode);
             List<Device> deviceListTwo = this.list(queryWrapperTwo);
@@ -335,7 +332,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                     DeviceAnalogVariableList deviceAnalogVariableList = new DeviceAnalogVariableList();
                     deviceAnalogVariableList.setDeviceCode(newDeviceCode);
                     deviceAnalogVariableList.setVariableName(deviceAnalogVariableListOne.get(i).getVariableName());
-                    deviceAnalogVariableList.setVariableCoding(newDeviceCode+"_"+deviceAnalogVariableListOne.get(i).getVariableCoding().split("_")[1]);
+                    deviceAnalogVariableList.setVariableCoding(newDeviceCode + "_" + deviceAnalogVariableListOne.get(i).getVariableCoding().split("_")[1]);
                     deviceAnalogVariableList.setMonitoringEquipment(0);
                     deviceAnalogVariableList.setCommunicationEquipment(deviceListTwo.get(0).getId());
                     deviceAnalogVariableList.setDataType(deviceAnalogVariableListOne.get(i).getDataType());
@@ -375,7 +372,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                         Page<Device> page = new Page<>(i, 30);
                         LambdaQueryWrapper<Device> queryWrapper = Wrappers.lambdaQuery();
                         queryWrapper.eq(Device::getEnable, 1);
-                        if (siteId!=0){
+                        if (siteId != 0) {
                             queryWrapper.eq(Device::getSiteId, siteId);
                         }
 
@@ -388,7 +385,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
 
                         page = this.page(page, queryWrapper);
 
-                        for (int j = 0; j < page.getRecords().size(); j++){
+                        for (int j = 0; j < page.getRecords().size(); j++) {
                             DeviceOne deviceOne = new DeviceOne();
                             deviceOne.setDeviceCode(page.getRecords().get(j).getDeviceCode());
                             deviceOne.setDeviceName(page.getRecords().get(j).getDeviceName());
@@ -438,7 +435,7 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
         Workbook workbook = null;
         File file = null;
         List<SysUser> tenantDaya = userMapper.getTenantId(SecurityUtils.getLoginUser().getUser().getUserId());
-        if (tenantDaya.size()<0) {
+        if (tenantDaya.size() < 0) {
             throw new BusinessException("无此租户,请联系管理员");
         }
         try {
@@ -447,8 +444,8 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                     (o, i) -> {
                         List<CorrespondDeviceTwoVO> Active = new ArrayList<CorrespondDeviceTwoVO>();
                         int startCurrent = (i - 1) * 30;
-                        List<CorrespondDeviceVO> correspondDeviceList = baseMapper.CorrespondDeviceList(deviceName, startCurrent, 30,tenantDaya.get(0).getTenantId());
-                        for (int j = 0; j <correspondDeviceList.size(); j++){
+                        List<CorrespondDeviceVO> correspondDeviceList = baseMapper.CorrespondDeviceList(deviceName, startCurrent, 30, tenantDaya.get(0).getTenantId());
+                        for (int j = 0; j < correspondDeviceList.size(); j++) {
                             CorrespondDeviceTwoVO correspondDeviceTwoVO = new CorrespondDeviceTwoVO();
                             correspondDeviceTwoVO.setDeviceCode(correspondDeviceList.get(j).getDeviceCode());
                             correspondDeviceTwoVO.setDeviceName(correspondDeviceList.get(j).getDeviceName());
@@ -497,19 +494,19 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
         SysUser user = SecurityUtils.getLoginUser().getUser();
         ImportParams params = new ImportParams();
         params.setHeadRows(1);
-        String err="文件导入失败";
+        String err = "文件导入失败";
         try {
             List<DeviceImportVo> deviceImportVos = ExcelImportUtil.importExcel(multipartFile.getInputStream(),
                     DeviceImportVo.class, params);
-            if (CollectionUtils.isNotEmpty(deviceImportVos)){
-                int rot=0;
-                for (DeviceImportVo deviceImportVo:deviceImportVos) {
+            if (CollectionUtils.isNotEmpty(deviceImportVos)) {
+                int rot = 0;
+                for (DeviceImportVo deviceImportVo : deviceImportVos) {
                     DeviceStatus deviceStatus = new DeviceStatus();
                     Device device = BeanMapperUtils.map(deviceImportVo, Device.class);
                     device.setEnable(1);
                     device.setCreator(user.getUserName());
                     device.setInstallTime(new Date());
-                    try{
+                    try {
                         this.save(device);
                         String deviceCode = device.getDeviceCode();
                         Integer siteId = device.getSiteId();
@@ -518,21 +515,25 @@ public class DeviceServiceImpl extends AbstractCrudService<DeviceMapper, Device>
                         deviceStatus.setStatusTime(new Date());
                         deviceStatus.setSiteId(siteId);
                         deviceStatusService.save(deviceStatus);
-                    }catch (Exception e){
-                        int h=rot+2;
-                        err="文件导入失败,第"+h+"行数据导入失败";
-                        throw  new BusinessException(err);
+                    } catch (Exception e) {
+                        int h = rot + 2;
+                        err = "文件导入失败,第" + h + "行数据导入失败";
+                        throw new BusinessException(err);
                     }
                     rot++;
                 }
-            }else {
-                err="文件不能为空";
+            } else {
+                err = "文件不能为空";
                 throw new BusinessException(err);
             }
-        }catch (Exception e){
+        } catch (Exception e) {
             TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
-            throw  new BusinessException(err);
+            throw new BusinessException(err);
         }
     }
 
+    @Override
+    public List<DeviceList> deviceBranch(int siteId, int deviceType, int pageNum, int pageSize) {
+        return baseMapper.deviceBranch(siteId, deviceType, pageNum, pageSize);
+    }
 }

+ 596 - 3
fiveep-service/src/main/java/com/bizmatics/service/impl/HtAnalogDataServiceImpl.java

@@ -5,13 +5,13 @@ import cn.afterturn.easypoi.excel.entity.ExportParams;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.bizmatics.common.core.bean.CommonPage;
 import com.bizmatics.common.core.exception.BusinessException;
 import com.bizmatics.common.core.util.*;
 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.*;
@@ -22,25 +22,34 @@ 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 com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import lombok.extern.slf4j.Slf4j;
-import org.apache.poi.ss.usermodel.Workbook;
-import org.checkerframework.checker.units.qual.A;
+import org.apache.poi.ss.usermodel.*;
+import org.apache.poi.ss.util.CellRangeAddress;
+import org.apache.poi.xssf.usermodel.XSSFWorkbook;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
+import javax.servlet.http.HttpServletResponse;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.OutputStream;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.text.DateFormat;
 import java.text.DecimalFormat;
 import java.text.SimpleDateFormat;
 import java.time.*;
+import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.time.temporal.TemporalAdjusters;
 import java.util.*;
 import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
@@ -88,6 +97,8 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
 
     private static final BigDecimal ZERO = BigDecimal.ZERO;
 
+    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+
     @Override
     public HadCountVO selectCount() {
         HadCountVO hadCountVO = new HadCountVO();
@@ -3028,5 +3039,587 @@ public class HtAnalogDataServiceImpl extends AbstractCrudService<HtAnalogDataMap
                 .reduce(ZERO, BigDecimal::add);
     }
 
+    @Override
+    public CommonPage<TimeSharingElectricityResponseVO> timeSharingElectricity(TimeSharingElectricityRequestVO request) {
+        /* ---------- 1. 参数兜底 ---------- */
+        Integer pageNum = request.getPageNum() == null ? 1 : request.getPageNum();
+        Integer pageSize = request.getPageSize() == null ? 20 : request.getPageSize();
+        if (request.getSiteId() == null) {
+            throw new BusinessException("站点ID不能为空");
+        }
+        if (CollectionUtils.isEmpty(request.getDeviceCodes())) {
+            throw new BusinessException("设备代码列表不能为空");
+        }
+        if (request.getStartTime() == null || request.getEndTime() == null) {
+            throw new BusinessException("起止时间不能为空");
+        }
+
+        /* ---------- 2. 分页查询 ---------- */
+        PageHelper.startPage(pageNum, pageSize);
+        LambdaQueryWrapper<SiteElectricityRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SiteElectricityRecord::getSiteId, request.getSiteId())
+                .in(SiteElectricityRecord::getDeviceCode, request.getDeviceCodes())
+                .between(SiteElectricityRecord::getDate, request.getStartTime(), request.getEndTime())
+                .orderByAsc(SiteElectricityRecord::getDate, SiteElectricityRecord::getDeviceCode);
+
+        List<SiteElectricityRecord> records = siteElectricityRecordMapper.selectList(wrapper);
+
+        // 3. 按 deviceCode 分组汇总
+        List<TimeSharingElectricityResponseVO> list = records.stream()
+                .collect(Collectors.groupingBy(
+                        SiteElectricityRecord::getDeviceCode,
+                        LinkedHashMap::new,
+                        Collectors.reducing((a, b) -> {
+                            // 累加电量
+                            a.setSharpPeak(a.getSharpPeak().add(b.getSharpPeak()));
+                            a.setPeak(a.getPeak().add(b.getPeak()));
+                            a.setFlat(a.getFlat().add(b.getFlat()));
+                            a.setValley(a.getValley().add(b.getValley()));
+                            a.setTotalElectricity(a.getTotalElectricity().add(b.getTotalElectricity()));
+
+                            // 累加电费
+                            a.setSharpPeakCost(a.getSharpPeakCost().add(b.getSharpPeakCost()));
+                            a.setPeakCost(a.getPeakCost().add(b.getPeakCost()));
+                            a.setFlatCost(a.getFlatCost().add(b.getFlatCost()));
+                            a.setValleyCost(a.getValleyCost().add(b.getValleyCost()));
+                            a.setTotalCost(a.getTotalCost().add(b.getTotalCost()));
+                            return a;
+                        })
+                ))
+                .values().stream()
+                .map(opt -> opt.orElseThrow(() -> new RuntimeException("汇总数据不能为空")))
+                .map(r -> {
+                    TimeSharingElectricityResponseVO vo = new TimeSharingElectricityResponseVO();
+                    vo.setDeviceName(r.getDeviceCode());
+                    vo.setSharpNum(r.getSharpPeak());
+                    vo.setPeakNum(r.getPeak());
+                    vo.setFlatNum(r.getFlat());
+                    vo.setValleyNum(r.getValley());
+                    vo.setTotalNum(r.getTotalElectricity());
+                    vo.setSharpPrice(r.getSharpPeakCost());
+                    vo.setPeakPrice(r.getPeakCost());
+                    vo.setFlatPrice(r.getFlatCost());
+                    vo.setValleyPrice(r.getValleyCost());
+                    vo.setTotalPrice(r.getTotalCost());
+                    vo.setTime(request.getStartTime().toString() +
+                            " - " + request.getEndTime().toString());
+                    return vo;
+                })
+                .collect(Collectors.toList());
+
+        /* ---------- 4. 封装分页结果 ---------- */
+        PageInfo<TimeSharingElectricityResponseVO> pageInfo = new PageInfo<>(list);
+        return new CommonPage<>(list, pageInfo.getTotal(), pageNum, pageSize);
+    }
+
+    @Override
+    public List<ElectricityTrendResponseVO> trend(ElectricityTrendRequestVO request) {
+        // 1. 参数校验
+        if (request.getSiteId() == null) {
+            throw new BusinessException("站点ID不能为空");
+        }
+        if (CollectionUtils.isEmpty(request.getDeviceCodes())) {
+            throw new BusinessException("设备代码列表不能为空");
+        }
+        if (request.getStartTime() == null || request.getEndTime() == null || request.getStartTime().isAfter(request.getEndTime())) {
+            throw new BusinessException("起止时间异常");
+        }
+        if (request.getType() == null || !Arrays.asList(1, 2).contains(request.getType())) {
+            throw new BusinessException("查询类型必须为1(电量)或2(电费)");
+        }
+
+        LocalDate startDate = request.getStartTime();
+        LocalDate endDate = request.getEndTime();
+
+        // 2. 查询数据
+        LambdaQueryWrapper<SiteElectricityRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SiteElectricityRecord::getSiteId, request.getSiteId())
+                .in(SiteElectricityRecord::getDeviceCode, request.getDeviceCodes())
+                .between(SiteElectricityRecord::getDate, startDate, endDate)
+                .orderByAsc(SiteElectricityRecord::getDeviceCode, SiteElectricityRecord::getDate);
+
+        List<SiteElectricityRecord> records = siteElectricityRecordMapper.selectList(wrapper);
+
+        // 3. 判断维度
+        long days = ChronoUnit.DAYS.between(startDate, endDate) + 1;
+        boolean monthMode = days > 31;
+
+        // 4. 选择字段函数(电量 or 电费)
+        boolean isElectricity = request.getType() == 1;
+        Function<SiteElectricityRecord, BigDecimal> sharpFn = isElectricity ?
+                SiteElectricityRecord::getSharpPeak : SiteElectricityRecord::getSharpPeakCost;
+        Function<SiteElectricityRecord, BigDecimal> peakFn = isElectricity ?
+                SiteElectricityRecord::getPeak : SiteElectricityRecord::getPeakCost;
+        Function<SiteElectricityRecord, BigDecimal> flatFn = isElectricity ?
+                SiteElectricityRecord::getFlat : SiteElectricityRecord::getFlatCost;
+        Function<SiteElectricityRecord, BigDecimal> valleyFn = isElectricity ?
+                SiteElectricityRecord::getValley : SiteElectricityRecord::getValleyCost;
+
+        records.forEach(r -> log.info("id={}, date={}, toString={}", r.getId(), r.getDate(), r.getDate().toString()));
+
+        // 5. 分组汇总
+        Map<String, Map<String, List<SiteElectricityRecord>>> group =
+                records.stream()
+                        .collect(Collectors.groupingBy(
+                                SiteElectricityRecord::getDeviceCode,
+                                LinkedHashMap::new,
+                                Collectors.groupingBy(r -> monthMode ?
+                                                YearMonth.from(r.getDate()).toString() : r.getDate().toString(),
+                                        LinkedHashMap::new,
+                                        Collectors.toList())
+                        ));
+
+
+        // 6. 转 VO
+        List<ElectricityTrendResponseVO> result = new ArrayList<>();
+        group.forEach((deviceCode, timeMap) -> {
+            ElectricityTrendResponseVO dev = new ElectricityTrendResponseVO();
+            dev.setDeviceCode(deviceCode);
+
+            List<ElectricityTrendVO> trendList = new ArrayList<>();
+            timeMap.forEach((timeKey, list) -> {
+
+                if (timeKey.contains("031") || timeKey.contains("-0031")) {
+                    throw new IllegalArgumentException("脏 timeKey 出现:" + timeKey);
+                }
+
+                BigDecimal sharp = list.stream().map(sharpFn).reduce(ZERO, BigDecimal::add);
+                BigDecimal peak = list.stream().map(peakFn).reduce(ZERO, BigDecimal::add);
+                BigDecimal flat = list.stream().map(flatFn).reduce(ZERO, BigDecimal::add);
+                BigDecimal valley = list.stream().map(valleyFn).reduce(ZERO, BigDecimal::add);
+
+                ElectricityTrendVO vo = new ElectricityTrendVO();
+                vo.setTime(timeKey);
+                vo.setSharp(sharp);
+                vo.setPeak(peak);
+                vo.setFlat(flat);
+                vo.setValley(valley);
+                trendList.add(vo);
+            });
+            dev.setData(trendList);
+            result.add(dev);
+        });
+
+        return result;
+    }
+
+    @Override
+    public void export(TimeSharingElectricityRequestVO request, HttpServletResponse response) throws IOException {
+        // 参数校验
+        if (request.getSiteId() == null) {
+            throw new BusinessException("站点ID不能为空");
+        }
+        if (CollectionUtils.isEmpty(request.getDeviceCodes())) {
+            throw new BusinessException("设备代码列表不能为空");
+        }
+        if (request.getStartTime() == null || request.getEndTime() == null) {
+            throw new BusinessException("起止时间不能为空");
+        }
+
+        // 格式化时间(解决之前的时间格式问题)
+        String start = formatter.format(request.getStartTime());
+        String end = formatter.format(request.getEndTime());
+
+        // 查询数据
+        LambdaQueryWrapper<SiteElectricityRecord> wrapper = new LambdaQueryWrapper<>();
+        wrapper.eq(SiteElectricityRecord::getSiteId, request.getSiteId())
+                .in(SiteElectricityRecord::getDeviceCode, request.getDeviceCodes())
+                .between(SiteElectricityRecord::getDate, request.getStartTime(), request.getEndTime())
+                .orderByAsc(SiteElectricityRecord::getDate, SiteElectricityRecord::getDeviceCode);
+
+        List<SiteElectricityRecord> records = siteElectricityRecordMapper.selectList(wrapper);
+        log.info("查询到的记录数: {}", records.size());
+        if (records.isEmpty()) {
+            throw new BusinessException("没有找到符合条件的数据");
+        }
+
+        // 按设备分组汇总数据
+        List<TimeSharingElectricityResponseVO> dataList = aggregateData(records, start, end);
+
+        // 处理分页
+        List<TimeSharingElectricityResponseVO> pageList = handlePagination(dataList, request);
+
+        // 准备导出信息
+        String deviceTypePrefix = request.getDeviceType() == 1 ? "支路" : "分项";
+        String sheetName = deviceTypePrefix + "分时用电";
+        String fileName = String.format("%s分时用电(%s-%s).xlsx", deviceTypePrefix, start, end);
+        String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.name());
+
+        // 导出Excel
+        try (Workbook workbook = new XSSFWorkbook()) {
+            Sheet sheet = workbook.createSheet(sheetName);
+
+            // 创建样式
+            CellStyle titleStyle = createTitleStyle(workbook);    // 新增标题样式
+            CellStyle headerStyle = createHeaderStyle(workbook);
+            CellStyle dataStyle = createDataStyle(workbook);
+            CellStyle percentageStyle = createPercentageStyle(workbook);
+
+            // 构建表格结构
+            int currentRow = 0;
+            currentRow = createTitleRow(sheet, titleStyle, sheetName, currentRow);  // 使用标题样式
+            currentRow = createHeaderRows(sheet, headerStyle, currentRow, deviceTypePrefix);
+
+            // 设置列宽
+            setColumnWidths(sheet);
+
+            // 插入数据
+            currentRow = insertDataIntoSheet(sheet, pageList, dataStyle, percentageStyle, currentRow);
+
+            // 响应处理
+            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+            response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFileName);
+
+            try (OutputStream outputStream = response.getOutputStream()) {
+                workbook.write(outputStream);
+            }
+        } catch (Exception e) {
+            log.error("导出文件失败", e);
+            throw new BusinessException("导出文件失败: " + e.getMessage());
+        }
+    }
+
+    /**
+     * 创建标题行
+     */
+    private int createTitleRow(Sheet sheet, CellStyle titleStyle, String title, int rowIndex) {
+        Row titleRow = sheet.createRow(rowIndex++);
+        Cell titleCell = titleRow.createCell(0);
+        titleCell.setCellValue(title);
+        titleCell.setCellStyle(titleStyle);
+        // 合并标题行(0行0列到0行11列)
+        sheet.addMergedRegion(new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), 0, 11));
+        return rowIndex;
+    }
+
+    /**
+     * 创建表头行(包含单元格合并逻辑)
+     */
+    /**
+     * 创建表头行(包含单元格合并逻辑,确保所有单元格应用带边框的样式)
+     */
+    private int createHeaderRows(Sheet sheet, CellStyle headerStyle, int rowIndex, String deviceTypePrefix) {
+        // 第一级表头(rowIndex行)
+        Row headerRow = sheet.createRow(rowIndex);
+
+        // 1. 支路名称 - 合并两行(rowIndex和rowIndex+1行,0列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex + 1, 0, 0));
+        // 为合并区域的两个行(rowIndex 和 rowIndex+1)的第0列单元格设置样式
+        for (int r = rowIndex; r <= rowIndex + 1; r++) {
+            Row row = sheet.getRow(r);
+            if (row == null) {
+                row = sheet.createRow(r);
+            }
+            Cell cell = row.getCell(0);
+            if (cell == null) {
+                cell = row.createCell(0);
+            }
+            cell.setCellValue(deviceTypePrefix + "名称");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 2. 尖峰 - 合并两列(rowIndex行,1-2列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 1, 2));
+        // 为合并区域的两个列(1 和 2)的单元格设置样式
+        for (int c = 1; c <= 2; c++) {
+            Cell cell = headerRow.getCell(c);
+            if (cell == null) {
+                cell = headerRow.createCell(c);
+            }
+            cell.setCellValue("尖峰");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 3. 峰 - 合并两列(rowIndex行,3-4列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 3, 4));
+        for (int c = 3; c <= 4; c++) {
+            Cell cell = headerRow.getCell(c);
+            if (cell == null) {
+                cell = headerRow.createCell(c);
+            }
+            cell.setCellValue("峰");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 4. 平 - 合并两列(rowIndex行,5-6列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 5, 6));
+        for (int c = 5; c <= 6; c++) {
+            Cell cell = headerRow.getCell(c);
+            if (cell == null) {
+                cell = headerRow.createCell(c);
+            }
+            cell.setCellValue("平");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 5. 谷 - 合并两列(rowIndex行,7-8列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 7, 8));
+        for (int c = 7; c <= 8; c++) {
+            Cell cell = headerRow.getCell(c);
+            if (cell == null) {
+                cell = headerRow.createCell(c);
+            }
+            cell.setCellValue("谷");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 6. 总 - 合并两列(rowIndex行,9-10列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex, 9, 10));
+        for (int c = 9; c <= 10; c++) {
+            Cell cell = headerRow.getCell(c);
+            if (cell == null) {
+                cell = headerRow.createCell(c);
+            }
+            cell.setCellValue("总");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 7. 谷电量占比 - 合并两行(rowIndex和rowIndex+1行,11列)
+        sheet.addMergedRegion(new CellRangeAddress(rowIndex, rowIndex + 1, 11, 11));
+        for (int r = rowIndex; r <= rowIndex + 1; r++) {
+            Row row = sheet.getRow(r);
+            if (row == null) {
+                row = sheet.createRow(r);
+            }
+            Cell cell = row.getCell(11);
+            if (cell == null) {
+                cell = row.createCell(11);
+            }
+            cell.setCellValue("谷电量占比");
+            cell.setCellStyle(headerStyle);
+        }
+
+        // 第二级表头(rowIndex+1行)
+        rowIndex++;
+        Row subHeaderRow = sheet.createRow(rowIndex);
+        String[] subHeaders = {"", "电量(kWh)", "电费(元)", "电量(kWh)", "电费(元)",
+                "电量(kWh)", "电费(元)", "电量(kWh)", "电费(元)",
+                "电量(kWh)", "电费(元)", ""};
+        for (int i = 0; i < subHeaders.length; i++) {
+            Cell cell = subHeaderRow.createCell(i);
+            cell.setCellValue(subHeaders[i]);
+            cell.setCellStyle(headerStyle);
+        }
+
+        return rowIndex + 1;
+    }
+
+    /**
+     * 设置列宽(增加标题行宽度)
+     */
+    private void setColumnWidths(Sheet sheet) {
+        // 为不同列设置更合理的宽度,第0列(支路名称)宽度增加
+        int[] columnWidths = {25, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 18};
+        for (int i = 0; i < columnWidths.length; i++) {
+            sheet.setColumnWidth(i, columnWidths[i] * 256);
+        }
+    }
+
+    // 创建标题样式(带边框、宋体,24号)
+    private CellStyle createTitleStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        // 设置边框
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short) 24);
+        font.setBold(true);
+        style.setFont(font);
+        return style;
+    }
+
+    // 创建表头样式(带边框、宋体,12号,加粗)
+    private CellStyle createHeaderStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        // 设置边框
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short) 12);
+        font.setBold(true);
+        style.setFont(font);
+        return style;
+    }
+
+    // 创建数据行样式(带边框、宋体,12号)
+    private CellStyle createDataStyle(Workbook workbook) {
+        CellStyle style = workbook.createCellStyle();
+        // 设置边框
+        style.setBorderTop(BorderStyle.THIN);
+        style.setBorderBottom(BorderStyle.THIN);
+        style.setBorderLeft(BorderStyle.THIN);
+        style.setBorderRight(BorderStyle.THIN);
+        style.setAlignment(HorizontalAlignment.CENTER);
+        style.setVerticalAlignment(VerticalAlignment.CENTER);
+
+        Font font = workbook.createFont();
+        font.setFontName("宋体");
+        font.setFontHeightInPoints((short) 12);
+        style.setFont(font);
+        return style;
+    }
+
+    // 创建百分比样式(带边框、宋体,12号,百分比格式)
+    private CellStyle createPercentageStyle(Workbook workbook) {
+        CellStyle style = createDataStyle(workbook);
+        DataFormat format = workbook.createDataFormat();
+        style.setDataFormat(format.getFormat("0.00%"));
+        return style;
+    }
+
+    /**
+     * 汇总数据
+     */
+    private List<TimeSharingElectricityResponseVO> aggregateData(List<SiteElectricityRecord> records, String start, String end) {
+        return records.stream()
+                .collect(Collectors.groupingBy(
+                        SiteElectricityRecord::getDeviceCode,
+                        LinkedHashMap::new,
+                        Collectors.reducing((a, b) -> {
+                            // 累加电量
+                            a.setSharpPeak(a.getSharpPeak().add(b.getSharpPeak()));
+                            a.setPeak(a.getPeak().add(b.getPeak()));
+                            a.setFlat(a.getFlat().add(b.getFlat()));
+                            a.setValley(a.getValley().add(b.getValley()));
+                            a.setTotalElectricity(a.getTotalElectricity().add(b.getTotalElectricity()));
+
+                            // 累加电费
+                            a.setSharpPeakCost(a.getSharpPeakCost().add(b.getSharpPeakCost()));
+                            a.setPeakCost(a.getPeakCost().add(b.getPeakCost()));
+                            a.setFlatCost(a.getFlatCost().add(b.getFlatCost()));
+                            a.setValleyCost(a.getValleyCost().add(b.getValleyCost()));
+                            a.setTotalCost(a.getTotalCost().add(b.getTotalCost()));
+                            return a;
+                        })
+                ))
+                .values().stream()
+                .map(opt -> opt.orElseThrow(() -> new RuntimeException("汇总数据不能为空")))
+                .map(r -> {
+                    TimeSharingElectricityResponseVO vo = new TimeSharingElectricityResponseVO();
+                    vo.setDeviceName(r.getDeviceCode());
+                    vo.setSharpNum(r.getSharpPeak());
+                    vo.setPeakNum(r.getPeak());
+                    vo.setFlatNum(r.getFlat());
+                    vo.setValleyNum(r.getValley());
+                    vo.setTotalNum(r.getTotalElectricity());
+                    vo.setSharpPrice(r.getSharpPeakCost());
+                    vo.setPeakPrice(r.getPeakCost());
+                    vo.setFlatPrice(r.getFlatCost());
+                    vo.setValleyPrice(r.getValleyCost());
+                    vo.setTotalPrice(r.getTotalCost());
+                    vo.setTime(start + " - " + end);
+                    return vo;
+                })
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 处理分页
+     */
+    private List<TimeSharingElectricityResponseVO> handlePagination(List<TimeSharingElectricityResponseVO> dataList,
+                                                                    TimeSharingElectricityRequestVO request) {
+        int total = dataList.size();
+        int startIndex = (request.getPageNum() - 1) * request.getPageSize();
+        int endIndex = Math.min(startIndex + request.getPageSize(), total);
+
+        log.info("总记录数: {}, 起始索引: {}, 结束索引: {}", total, startIndex, endIndex);
+
+        if (startIndex > total) {
+            throw new BusinessException("起始索引无效");
+        }
+
+        return dataList.subList(startIndex, endIndex);
+    }
+
+    /**
+     * 插入数据到工作表
+     */
+    private int insertDataIntoSheet(Sheet sheet, List<TimeSharingElectricityResponseVO> dataList,
+                                    CellStyle dataStyle, CellStyle percentageStyle, int startRow) {
+        int currentRow = startRow;
+
+        for (TimeSharingElectricityResponseVO vo : dataList) {
+            Row row = sheet.createRow(currentRow++);
+            int cellIndex = 0;
+
+            // 支路名称
+            createCell(row, cellIndex++, vo.getDeviceName(), dataStyle);
+
+            // 尖峰数据
+            createCell(row, cellIndex++, vo.getSharpNum(), dataStyle);
+            createCell(row, cellIndex++, vo.getSharpPrice(), dataStyle);
+
+            // 峰数据
+            createCell(row, cellIndex++, vo.getPeakNum(), dataStyle);
+            createCell(row, cellIndex++, vo.getPeakPrice(), dataStyle);
+
+            // 平数据
+            createCell(row, cellIndex++, vo.getFlatNum(), dataStyle);
+            createCell(row, cellIndex++, vo.getFlatPrice(), dataStyle);
+
+            // 谷数据
+            createCell(row, cellIndex++, vo.getValleyNum(), dataStyle);
+            createCell(row, cellIndex++, vo.getValleyPrice(), dataStyle);
+
+            // 总数据
+            createCell(row, cellIndex++, vo.getTotalNum(), dataStyle);
+            createCell(row, cellIndex++, vo.getTotalPrice(), dataStyle);
+
+            // 谷电量占比(处理除零情况)
+            BigDecimal valleyRatio = calculateValleyRatio(vo);
+            createCell(row, cellIndex++, valleyRatio, percentageStyle);
+        }
+
+        return currentRow;
+    }
+
+    /**
+     * 创建单元格并设置值和样式
+     */
+    private void createCell(Row row, int cellIndex, Object value, CellStyle style) {
+        Cell cell = row.createCell(cellIndex);
+        cell.setCellStyle(style);
+
+        if (value == null) {
+            cell.setCellValue("");
+            return;
+        }
+
+        // 根据值类型设置单元格内容
+        if (value instanceof BigDecimal) {
+            cell.setCellValue(((BigDecimal) value).doubleValue());
+        } else if (value instanceof Number) {
+            cell.setCellValue(((Number) value).doubleValue());
+        } else {
+            cell.setCellValue(value.toString());
+        }
+    }
+
+    /**
+     * 计算谷电量占比(处理除零情况)
+     */
+    private BigDecimal calculateValleyRatio(TimeSharingElectricityResponseVO vo) {
+        try {
+            if (vo.getTotalNum() == null || vo.getTotalNum().compareTo(BigDecimal.ZERO) == 0) {
+                return BigDecimal.ZERO;
+            }
+            return vo.getValleyNum().divide(vo.getTotalNum(), 4, RoundingMode.HALF_UP);
+        } catch (Exception e) {
+            log.warn("计算谷电量占比失败", e);
+            return BigDecimal.ZERO;
+        }
+    }
 
 }

+ 45 - 19
fiveep-service/src/main/java/com/bizmatics/service/job/SiteDailyElectricityCostTask.java

@@ -34,17 +34,20 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
     /** 应用启动后立即执行一次 */
     @Override
     public void run(ApplicationArguments args) {
-        log.info("立即执行一次日电费记录");
+        log.info("立即执行一次日电量电费记录");
         // calculateDailyElectricityCost();
     }
 
     // 每天凌晨0点过5分执行,计算前一天的用电费用
-    @Scheduled(cron = "0 5 0 * * ?")
+    //@Scheduled(cron = "0 5 0 * * ?")
+
+    // 每小时第5分钟执行一次
+    @Scheduled(cron = "0 5 * * * ?")
     public void calculateDailyElectricityCost() {
-        LocalDate yesterday = LocalDate.now().minusDays(1);
-        // LocalDate yesterday = LocalDate.now();
+        // LocalDate yesterday = LocalDate.now().minusDays(1);
+        LocalDate yesterday = LocalDate.now();
         // LocalDate yesterday = LocalDate.parse("2025-07-19");
-        log.info("开始计算{}的用电费用", yesterday);
+        // log.info("开始计算{}的用电费用", yesterday);
 
         try {
             calculateElectricityCostForDate(yesterday);
@@ -121,6 +124,13 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
         BigDecimal valleyCost = BigDecimal.ZERO;
         BigDecimal totalCost = BigDecimal.ZERO;
 
+        // 初始化用电量
+        BigDecimal sharpPeak = BigDecimal.ZERO;
+        BigDecimal peak = BigDecimal.ZERO;
+        BigDecimal flat = BigDecimal.ZERO;
+        BigDecimal valley = BigDecimal.ZERO;
+        BigDecimal totalElectricity = BigDecimal.ZERO;
+
         // 计算每个时间段的电费
         for (int i = 1; i < electricityDataList.size(); i++) {
             ElectricityData prev = electricityDataList.get(i - 1);
@@ -154,19 +164,24 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
             // 计算电费:用电量 × 电价
             BigDecimal cost = electricityUsage.multiply(config.getElectricityPrice());
             totalCost = totalCost.add(cost).setScale(2, RoundingMode.HALF_UP);
+            totalElectricity = totalElectricity.add(electricityUsage);
 
             switch (config.getPeakValleyAttribute()) {
                 case 1: // 尖
                     sharpPeakCost = sharpPeakCost.add(cost).setScale(2, RoundingMode.HALF_UP);
+                    sharpPeak = sharpPeak.add(electricityUsage);
                     break;
                 case 2: // 峰
                     peakCost = peakCost.add(cost).setScale(2, RoundingMode.HALF_UP);
+                    peak = peak.add(electricityUsage);
                     break;
                 case 3: // 平
                     flatCost = flatCost.add(cost).setScale(2, RoundingMode.HALF_UP);
+                    flat = flat.add(electricityUsage);
                     break;
                 case 4: // 谷
                     valleyCost = valleyCost.add(cost).setScale(2, RoundingMode.HALF_UP);
+                    valley = valley.add(electricityUsage);
                     break;
                 default:
                     log.warn("未知的峰谷属性: {}", config.getPeakValleyAttribute());
@@ -175,10 +190,15 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
 
         // 保存计算结果
         saveElectricityRecord(siteId, deviceCode, date, sharpPeakCost, peakCost,
-                flatCost, valleyCost, totalCost);
-
-        log.info("站点{}设备{}在{}的电费计算完成:尖峰{}元,峰{}元,平{}元,谷{}元,总计{}元",
-                siteId, deviceCode, date, sharpPeakCost, peakCost, flatCost, valleyCost, totalCost);
+                flatCost, valleyCost, totalCost, sharpPeak, peak, flat, valley, totalElectricity);
+
+        log.info("站点{}设备{}在{}的电费计算完成:尖峰{}元({}kWh),峰{}元({}kWh),平{}元({}kWh),谷{}元({}kWh),总计{}元({}kwh)",
+                siteId, deviceCode, date,
+                sharpPeakCost, sharpPeak,
+                peakCost, peak,
+                flatCost, flat,
+                valleyCost, valley,
+                totalCost, totalElectricity);
     }
 
     private List<TimePriceConfig> getTimePriceConfigs(Integer siteId, int month) {
@@ -250,17 +270,18 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
     private void saveElectricityRecord(Integer siteId, String deviceCode, LocalDate date,
                                        BigDecimal sharpPeakCost, BigDecimal peakCost,
                                        BigDecimal flatCost, BigDecimal valleyCost,
-                                       BigDecimal totalCost) {
+                                       BigDecimal totalCost, BigDecimal sharpPeak,
+                                       BigDecimal peak, BigDecimal flat, BigDecimal valley,
+                                       BigDecimal totalElectricity) {
         String sql = "INSERT INTO site_electricity_record " +
-                "(site_id, device_code, date, peak_cost, flat_cost, " +
-                "valley_cost, sharp_peak_cost, total_cost) " +
-                "VALUES (?, ?, ?, ?, ?, ?, ?, ?) " +
+                "(site_id, device_code, date, peak_cost, flat_cost, valley_cost, sharp_peak_cost, total_cost, " +
+                "sharp_peak, peak, flat, valley, total_electricity) " +
+                "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) " +
                 "ON DUPLICATE KEY UPDATE " +
-                "peak_cost = VALUES(peak_cost), " +
-                "flat_cost = VALUES(flat_cost), " +
-                "valley_cost = VALUES(valley_cost), " +
-                "sharp_peak_cost = VALUES(sharp_peak_cost), " +
-                "total_cost = VALUES(total_cost)";
+                "peak_cost = VALUES(peak_cost), flat_cost = VALUES(flat_cost), valley_cost = VALUES(valley_cost), " +
+                "sharp_peak_cost = VALUES(sharp_peak_cost), total_cost = VALUES(total_cost), " +
+                "sharp_peak = VALUES(sharp_peak), peak = VALUES(peak), " +
+                "flat = VALUES(flat), valley = VALUES(valley), total_electricity = VALUES(total_electricity)";
 
         try {
             jdbcTemplate.update(sql,
@@ -271,7 +292,12 @@ public class SiteDailyElectricityCostTask implements ApplicationRunner {
                     flatCost,
                     valleyCost,
                     sharpPeakCost,
-                    totalCost
+                    totalCost,
+                    sharpPeak,
+                    peak,
+                    flat,
+                    valley,
+                    totalElectricity
             );
             log.debug("成功保存站点{}设备{}的电费记录", siteId, deviceCode);
         } catch (Exception e) {

+ 44 - 0
fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendRequestVO.java

@@ -0,0 +1,44 @@
+package com.bizmatics.service.vo;
+
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/5
+ */
+@Data
+public class ElectricityTrendRequestVO {
+
+    /**
+     * 站点id
+     */
+    private Integer siteId;
+
+    /**
+     * 查询类型 1:电量,2:费用
+     */
+    private Integer type;
+
+    /**
+     * 开始时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startTime;
+
+    /**
+     * 结束时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endTime;
+
+    /**
+     * 设备编码列表
+     */
+    private List<String> deviceCodes;
+}

+ 27 - 0
fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendResponseVO.java

@@ -0,0 +1,27 @@
+package com.bizmatics.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+import java.time.LocalDateTime;
+import java.util.List;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/5
+ */
+@Data
+public class ElectricityTrendResponseVO {
+
+    /**
+     * 设备名称
+     */
+    private String deviceCode;
+
+    /**
+     * 设备数据
+     */
+    private List<ElectricityTrendVO> data;
+}

+ 41 - 0
fiveep-service/src/main/java/com/bizmatics/service/vo/ElectricityTrendVO.java

@@ -0,0 +1,41 @@
+package com.bizmatics.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/5
+ */
+@Data
+public class ElectricityTrendVO {
+
+    /**
+     * 时间
+     */
+    private String time;
+
+    /**
+     * 尖
+     */
+    private BigDecimal sharp;
+
+    /**
+     * 峰
+     */
+    private BigDecimal peak;
+
+    /**
+     * 平
+     */
+    private BigDecimal flat;
+
+    /**
+     * 谷
+     */
+    private BigDecimal valley;
+
+}

+ 53 - 0
fiveep-service/src/main/java/com/bizmatics/service/vo/TimeSharingElectricityRequestVO.java

@@ -0,0 +1,53 @@
+package com.bizmatics.service.vo;
+
+import lombok.Data;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.time.LocalDate;
+import java.util.List;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/4
+ */
+@Data
+public class TimeSharingElectricityRequestVO {
+    /**
+     * 站点id
+     */
+    private Integer siteId;
+
+    /**
+     * 设备类型 1:支路 2:分项
+     */
+    private Integer deviceType;
+
+    /**
+     * 开始时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate startTime;
+
+    /**
+     * 结束时间
+     */
+    @DateTimeFormat(pattern = "yyyy-MM-dd")
+    private LocalDate endTime;
+
+    /**
+     * 页数
+     */
+    private Integer pageNum;
+
+    /**
+     * 每页数量
+     */
+    private Integer pageSize;
+
+    /**
+     * 设备编码
+     */
+    private List<String> deviceCodes;
+}

+ 76 - 0
fiveep-service/src/main/java/com/bizmatics/service/vo/TimeSharingElectricityResponseVO.java

@@ -0,0 +1,76 @@
+package com.bizmatics.service.vo;
+
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ *
+ * @author fyc
+ * @email yuchuan.fu@chinausky.com
+ * @date 2025/9/4
+ */
+@Data
+public class TimeSharingElectricityResponseVO {
+
+    /**
+     * 设备名称
+     */
+    private String deviceName;
+
+    /**
+     * 尖 电量
+     */
+    private BigDecimal sharpNum;
+
+    /**
+     * 峰 电量
+     */
+    private BigDecimal peakNum;
+
+    /**
+     * 平 电量
+     */
+    private BigDecimal flatNum;
+
+    /**
+     * 谷 电量
+     */
+    private BigDecimal valleyNum;
+
+    /**
+     * 总 电量
+     */
+    private BigDecimal totalNum;
+
+    /**
+     * 尖 电费
+     */
+    private BigDecimal sharpPrice;
+
+    /**
+     * 峰 电费
+     */
+    private BigDecimal peakPrice;
+
+    /**
+     * 平 电费
+     */
+    private BigDecimal flatPrice;
+
+    /**
+     * 谷 电费
+     */
+    private BigDecimal valleyPrice;
+
+    /**
+     * 总 电费
+     */
+    private BigDecimal totalPrice;
+
+    /**
+     * 数据时间区间
+     * 也用于导出文件名
+     */
+    private String time;
+}

+ 1 - 1
pom.xml

@@ -50,7 +50,7 @@
         <!--apache-->
         <commons-lang3.version>3.11</commons-lang3.version>
         <commons-beanutils.version>1.9.4</commons-beanutils.version>
-        <commons-io.version>2.6</commons-io.version>
+        <commons-io.version>2.11.0</commons-io.version>
         <okhttp3.version>4.8.1</okhttp3.version>
         <openCsv.version>4.6</openCsv.version>