feat: 添加电表和采集器同步服务及相关数据模型
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.config; |
| | | |
| | | import lombok.Data; |
| | | import org.springframework.boot.context.properties.ConfigurationProperties; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * 天å¯çµè¡¨å¹³å°é
ç½® |
| | | */ |
| | | @Data |
| | | @Component |
| | | @ConfigurationProperties(prefix = "tqdianbiao") |
| | | public class TqdianbiaoConfig { |
| | | |
| | | /** API åºç¡å°å */ |
| | | private String baseUrl = "https://168.tqdianbiao.com"; |
| | | |
| | | /** è®¤è¯ token */ |
| | | private String auth = "b6229401590539d5def7f2bf897b33de"; |
| | | |
| | | /** æ¯å¦å¿½ç¥äºæå¨åæ¯ï¼0-å¦ï¼1-æ¯ */ |
| | | private Integer ignoreRadio = 1; |
| | | |
| | | /** 忥é
ç½® */ |
| | | private Sync sync = new Sync(); |
| | | |
| | | @Data |
| | | public static class Sync { |
| | | /** æ¯å¦å¯ç¨å®æ¶åæ¥ */ |
| | | private Boolean enabled = true; |
| | | /** å°æ¶åæ¥åççªå£ï¼å°æ¶æ°ï¼ */ |
| | | private Integer hourWindow = 2; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.controller; |
| | | |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.http.service.StatisticEleService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | @RestController |
| | | @Tag(name = "è½èçµè¡¨ç»è®¡") |
| | | @RequestMapping("/statisticEle") |
| | | @RequiredArgsConstructor |
| | | public class StatisticEleController extends BaseController { |
| | | |
| | | private final StatisticEleService statisticEleService; |
| | | |
| | | @GetMapping("/list") |
| | | @Operation(summary = "è½èæ°æ®-å表æ¥è¯¢(æ¬å°åº)") |
| | | @Log(title = "è½èæ°æ®-å表æ¥è¯¢", businessType = BusinessType.OTHER) |
| | | public AjaxResult list( |
| | | @RequestParam(defaultValue = "hour") String dimension, |
| | | @RequestParam String startTime, |
| | | @RequestParam String endTime, |
| | | @RequestParam(defaultValue = "1") Integer ignoreRadio) { |
| | | return AjaxResult.success(statisticEleService.listRecords(dimension, startTime, endTime, ignoreRadio)); |
| | | } |
| | | |
| | | @GetMapping("/summary") |
| | | @Operation(summary = "è½èæ°æ®-æ±æ»ç»è®¡(æ¬å°åº)") |
| | | @Log(title = "è½èæ°æ®-æ±æ»ç»è®¡", businessType = BusinessType.OTHER) |
| | | public AjaxResult summary( |
| | | @RequestParam(defaultValue = "hour") String dimension, |
| | | @RequestParam String startTime, |
| | | @RequestParam String endTime) { |
| | | return AjaxResult.success(statisticEleService.getSummary(dimension, startTime, endTime)); |
| | | } |
| | | |
| | | @GetMapping("/yesterday") |
| | | @Operation(summary = "æ¨æ¥ç¨çµéæ±æ»") |
| | | public AjaxResult yesterday() { |
| | | return AjaxResult.success(statisticEleService.getYesterdaySummary()); |
| | | } |
| | | |
| | | @GetMapping("/syncStatus") |
| | | @Operation(summary = "è½èæ°æ®-åæ¥ç¶æ") |
| | | public AjaxResult syncStatus() { |
| | | return AjaxResult.success(statisticEleService.getSyncStatus()); |
| | | } |
| | | |
| | | @GetMapping("/raw") |
| | | @Operation(summary = "è½èæ°æ®-åå§æ¥å£(è°è¯ç¨ï¼æ
ç¨)") |
| | | @Log(title = "è½èæ°æ®-åå§æ¥å£", businessType = BusinessType.OTHER) |
| | | public AjaxResult raw( |
| | | @RequestParam(defaultValue = "hour") String dimension, |
| | | @RequestParam String startTime, |
| | | @RequestParam String endTime) { |
| | | return AjaxResult.success(statisticEleService.fetchRawData(dimension, startTime, endTime)); |
| | | } |
| | | |
| | | @PostMapping("/export") |
| | | @Operation(summary = "è½èæ°æ®-导åº") |
| | | @Log(title = "è½èæ°æ®-导åº", businessType = BusinessType.EXPORT) |
| | | public void export( |
| | | @RequestParam(defaultValue = "hour") String dimension, |
| | | @RequestParam String startTime, |
| | | @RequestParam String endTime, |
| | | HttpServletResponse response) { |
| | | statisticEleService.exportRecords(dimension, startTime, endTime, response); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | import com.ruoyi.http.service.TqdianbiaoCollectorManageService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | | @Tag(name = "éé卿¡£æ¡") |
| | | @RequestMapping("/tqdianbiao/collector") |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoCollectorController extends BaseController { |
| | | |
| | | private final TqdianbiaoCollectorManageService collectorManageService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "éé卿¡£æ¡-å页æ¥è¯¢") |
| | | public AjaxResult listPage(Page page, TqdianbiaoCollector query) { |
| | | return AjaxResult.success(collectorManageService.listPage(page, query)); |
| | | } |
| | | |
| | | @GetMapping("/listAll") |
| | | @Operation(summary = "éé卿¡£æ¡-å
¨é¨å表") |
| | | public AjaxResult listAll() { |
| | | return AjaxResult.success(collectorManageService.list()); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Log(title = "éé卿¡£æ¡-æ°å¢", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody TqdianbiaoCollector collector) { |
| | | return collectorManageService.addCollector(collector) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | @Log(title = "éé卿¡£æ¡-ä¿®æ¹", businessType = BusinessType.UPDATE) |
| | | public AjaxResult update(@RequestBody TqdianbiaoCollector collector) { |
| | | return collectorManageService.updateCollector(collector) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Log(title = "éé卿¡£æ¡-å é¤", businessType = BusinessType.DELETE) |
| | | public AjaxResult delete(@RequestBody List<Long> ids) { |
| | | return collectorManageService.deleteByIds(ids) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @PostMapping("/sync") |
| | | @Log(title = "éé卿¡£æ¡-è¿ç¨åæ¥", businessType = BusinessType.OTHER) |
| | | public AjaxResult sync() { |
| | | int count = collectorManageService.syncFromRemote(); |
| | | return AjaxResult.success("忥æåï¼å
±åæ¥ " + count + " æ¡"); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.service.TqdianbiaoEleRecordManageService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | | @Tag(name = "çµéè®°å½ç®¡ç") |
| | | @RequestMapping("/tqdianbiao/eleRecord") |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoEleRecordController extends BaseController { |
| | | |
| | | private final TqdianbiaoEleRecordManageService eleRecordManageService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "çµéè®°å½-å页æ¥è¯¢") |
| | | public AjaxResult listPage(Page page, TqdianbiaoEleRecord query) { |
| | | return AjaxResult.success(eleRecordManageService.listPage(page, query)); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Log(title = "çµéè®°å½-æ°å¢", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody TqdianbiaoEleRecord record) { |
| | | return eleRecordManageService.addRecord(record) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | @Log(title = "çµéè®°å½-ä¿®æ¹", businessType = BusinessType.UPDATE) |
| | | public AjaxResult update(@RequestBody TqdianbiaoEleRecord record) { |
| | | return eleRecordManageService.updateRecord(record) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Log(title = "çµéè®°å½-å é¤", businessType = BusinessType.DELETE) |
| | | public AjaxResult delete(@RequestBody List<Long> ids) { |
| | | return eleRecordManageService.deleteByIds(ids) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @GetMapping("/prevReading") |
| | | @Operation(summary = "è·å䏿¬¡çµé读æ°") |
| | | public AjaxResult prevReading(Long meterId, String timeKey) { |
| | | return AjaxResult.success(eleRecordManageService.getPrevReading(meterId, timeKey)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | import com.ruoyi.http.service.TqdianbiaoMeterManageService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | | @Tag(name = "çµè¡¨æ¡£æ¡") |
| | | @RequestMapping("/tqdianbiao/meter") |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoMeterController extends BaseController { |
| | | |
| | | private final TqdianbiaoMeterManageService meterManageService; |
| | | |
| | | @GetMapping("/listPage") |
| | | @Operation(summary = "çµè¡¨æ¡£æ¡-å页æ¥è¯¢") |
| | | public AjaxResult listPage(Page page, TqdianbiaoMeter query) { |
| | | return AjaxResult.success(meterManageService.listPage(page, query)); |
| | | } |
| | | |
| | | @GetMapping("/listAll") |
| | | @Operation(summary = "çµè¡¨æ¡£æ¡-å
¨é¨å表") |
| | | public AjaxResult listAll() { |
| | | return AjaxResult.success(meterManageService.list()); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Log(title = "çµè¡¨æ¡£æ¡-æ°å¢", businessType = BusinessType.INSERT) |
| | | public AjaxResult add(@RequestBody TqdianbiaoMeter meter) { |
| | | return meterManageService.addMeter(meter) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @PostMapping("/update") |
| | | @Log(title = "çµè¡¨æ¡£æ¡-ä¿®æ¹", businessType = BusinessType.UPDATE) |
| | | public AjaxResult update(@RequestBody TqdianbiaoMeter meter) { |
| | | return meterManageService.updateMeter(meter) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Log(title = "çµè¡¨æ¡£æ¡-å é¤", businessType = BusinessType.DELETE) |
| | | public AjaxResult delete(@RequestBody List<Long> ids) { |
| | | return meterManageService.deleteByIds(ids) ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | | @PostMapping("/sync") |
| | | @Log(title = "çµè¡¨æ¡£æ¡-è¿ç¨åæ¥", businessType = BusinessType.OTHER) |
| | | public AjaxResult sync() { |
| | | int count = meterManageService.syncFromRemote(); |
| | | return AjaxResult.success("忥æåï¼å
±åæ¥ " + count + " æ¡"); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | |
| | | public interface TqdianbiaoCollectorMapper extends BaseMapper<TqdianbiaoCollector> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.vo.StatisticEleRecordVo; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TqdianbiaoEleRecordMapper extends BaseMapper<TqdianbiaoEleRecord> { |
| | | |
| | | List<StatisticEleRecordVo> selectRecordList( |
| | | @Param("dimensions") List<String> dimensions, |
| | | @Param("startTime") String startTime, |
| | | @Param("endTime") String endTime); |
| | | |
| | | TqdianbiaoEleRecord selectPrevReading( |
| | | @Param("meterId") Long meterId, |
| | | @Param("timeKey") String timeKey); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | |
| | | public interface TqdianbiaoMeterMapper extends BaseMapper<TqdianbiaoMeter> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoSyncLog; |
| | | |
| | | public interface TqdianbiaoSyncLogMapper extends BaseMapper<TqdianbiaoSyncLog> { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("tqdianbiao_collector") |
| | | public class TqdianbiaoCollector { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | private String collectorId; |
| | | |
| | | private String collectorNo; |
| | | |
| | | private Boolean online; |
| | | |
| | | private Integer csq; |
| | | |
| | | private String connectTime; |
| | | |
| | | private String disconnectTime; |
| | | |
| | | private String description; |
| | | |
| | | private LocalDateTime syncTime; |
| | | |
| | | private LocalDateTime createTime; |
| | | |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(exist = false) |
| | | private String keyword; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("tqdianbiao_ele_record") |
| | | public class TqdianbiaoEleRecord { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | private Long meterId; |
| | | |
| | | /** çµè¡¨åç§°(åä½) */ |
| | | private String meterName; |
| | | |
| | | private String dimension; |
| | | |
| | | /** æè¡¨æ¹å¼ sync/manual */ |
| | | private String readingMethod; |
| | | |
| | | private String timeKey; |
| | | |
| | | private String startTime; |
| | | |
| | | private String endTime; |
| | | |
| | | private BigDecimal totalConsumption; |
| | | |
| | | private BigDecimal sharpConsumption; |
| | | |
| | | private BigDecimal peakConsumption; |
| | | |
| | | private BigDecimal flatConsumption; |
| | | |
| | | private BigDecimal valleyConsumption; |
| | | |
| | | private BigDecimal deepValleyConsumption; |
| | | |
| | | private String startReading; |
| | | |
| | | private String endReading; |
| | | |
| | | /** 䏿¬¡çµé(æ»è¯»æ°) */ |
| | | private BigDecimal prevReading; |
| | | |
| | | /** æ¬æ¬¡çµé(æ»è¯»æ°) */ |
| | | private BigDecimal currReading; |
| | | |
| | | private Integer ratio; |
| | | |
| | | private LocalDateTime syncTime; |
| | | |
| | | private LocalDateTime createTime; |
| | | |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(exist = false) |
| | | private String startTimeKey; |
| | | |
| | | @TableField(exist = false) |
| | | private String endTimeKey; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("tqdianbiao_meter") |
| | | public class TqdianbiaoMeter { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | private Long meterId; |
| | | |
| | | /** çµè¡¨åç§° */ |
| | | private String meterName; |
| | | |
| | | private String collectorId; |
| | | |
| | | private String collectorNo; |
| | | |
| | | private String address; |
| | | |
| | | private Integer rate; |
| | | |
| | | private String relayState; |
| | | |
| | | private String meterType; |
| | | |
| | | private Integer csq; |
| | | |
| | | private String description; |
| | | |
| | | /** æ¥æº sync-å¹³å°åæ¥ manual-æå¨æ·»å */ |
| | | private String source; |
| | | |
| | | private LocalDateTime syncTime; |
| | | |
| | | private LocalDateTime createTime; |
| | | |
| | | private LocalDateTime updateTime; |
| | | |
| | | @TableField(exist = false) |
| | | private String keyword; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @TableName("tqdianbiao_sync_log") |
| | | public class TqdianbiaoSyncLog { |
| | | |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | private String syncType; |
| | | |
| | | private String windowStart; |
| | | |
| | | private String windowEnd; |
| | | |
| | | private String status; |
| | | |
| | | private Integer recordCount; |
| | | |
| | | private Integer apiCallCount; |
| | | |
| | | private String errorMsg; |
| | | |
| | | private LocalDateTime createTime; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | import com.ruoyi.http.vo.StatisticEleRecordVo; |
| | | import com.ruoyi.http.vo.StatisticEleSummaryVo; |
| | | import com.ruoyi.http.vo.StatisticEleSyncStatusVo; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 天å¯çµè¡¨ç»è®¡æ¥å£ |
| | | */ |
| | | public interface StatisticEleService { |
| | | |
| | | String fetchRawData(String dimension, String startTime, String endTime); |
| | | |
| | | List<StatisticEleRecordVo> listRecords(String dimension, String startTime, String endTime, Integer ignoreRadio); |
| | | |
| | | StatisticEleSummaryVo getSummary(String dimension, String startTime, String endTime); |
| | | |
| | | StatisticEleSummaryVo getYesterdaySummary(); |
| | | |
| | | void exportRecords(String dimension, String startTime, String endTime, HttpServletResponse response); |
| | | |
| | | StatisticEleSyncStatusVo getSyncStatus(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TqdianbiaoCollectorManageService extends IService<TqdianbiaoCollector> { |
| | | |
| | | IPage<TqdianbiaoCollector> listPage(Page page, TqdianbiaoCollector query); |
| | | |
| | | boolean addCollector(TqdianbiaoCollector collector); |
| | | |
| | | boolean updateCollector(TqdianbiaoCollector collector); |
| | | |
| | | boolean deleteByIds(List<Long> ids); |
| | | |
| | | int syncFromRemote(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | public interface TqdianbiaoCollectorSyncService { |
| | | |
| | | int syncCollectors(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | public interface TqdianbiaoEleRecordManageService extends IService<TqdianbiaoEleRecord> { |
| | | |
| | | IPage<TqdianbiaoEleRecord> listPage(Page page, TqdianbiaoEleRecord query); |
| | | |
| | | boolean addRecord(TqdianbiaoEleRecord record); |
| | | |
| | | boolean updateRecord(TqdianbiaoEleRecord record); |
| | | |
| | | boolean deleteByIds(List<Long> ids); |
| | | |
| | | BigDecimal getPrevReading(Long meterId, String timeKey); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | public interface TqdianbiaoEleSyncService { |
| | | |
| | | int syncHourData(); |
| | | |
| | | /** 䏿¬¡æ§è¡¥æ°ï¼åæ¥åä¸å¤© 00:00 è³å½åå°æ¶ï¼ç¨å®å¯å */ |
| | | int syncLast3DaysHourData(); |
| | | |
| | | int syncDayData(); |
| | | |
| | | int syncMonthData(); |
| | | |
| | | int syncYearData(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | |
| | | import java.util.List; |
| | | |
| | | public interface TqdianbiaoMeterManageService extends IService<TqdianbiaoMeter> { |
| | | |
| | | IPage<TqdianbiaoMeter> listPage(Page page, TqdianbiaoMeter query); |
| | | |
| | | boolean addMeter(TqdianbiaoMeter meter); |
| | | |
| | | boolean updateMeter(TqdianbiaoMeter meter); |
| | | |
| | | boolean deleteByIds(List<Long> ids); |
| | | |
| | | int syncFromRemote(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | public interface TqdianbiaoMeterSyncService { |
| | | |
| | | int syncMeters(); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service; |
| | | |
| | | public interface TqdianbiaoSyncLogService { |
| | | |
| | | void logSuccess(String syncType, String windowStart, String windowEnd, int recordCount); |
| | | |
| | | void logFailure(String syncType, String windowStart, String windowEnd, String errorMsg); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.http.config.TqdianbiaoConfig; |
| | | import com.ruoyi.http.mapper.TqdianbiaoCollectorMapper; |
| | | import com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper; |
| | | import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; |
| | | import com.ruoyi.http.mapper.TqdianbiaoSyncLogMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.pojo.TqdianbiaoSyncLog; |
| | | import com.ruoyi.http.service.StatisticEleService; |
| | | import com.ruoyi.http.util.StatisticEleAggregateUtil; |
| | | import com.ruoyi.http.util.StatisticEleAggregateUtil.HourRange; |
| | | import com.ruoyi.http.vo.StatisticEleRecordVo; |
| | | import com.ruoyi.http.vo.StatisticEleSummaryVo; |
| | | import com.ruoyi.http.vo.StatisticEleSyncStatusVo; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.HashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public class StatisticEleServiceImpl implements StatisticEleService { |
| | | |
| | | private static final Set<String> STAT_DIMENSIONS = Set.of("day", "month", "quarter", "year"); |
| | | private static final List<String> DATA_DIMENSIONS = List.of("hour", "manual"); |
| | | private static final DateTimeFormatter LOG_TIME_FMT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); |
| | | |
| | | private final TqdianbiaoConfig config; |
| | | private final TqdianbiaoEleRecordMapper eleRecordMapper; |
| | | private final TqdianbiaoMeterMapper meterMapper; |
| | | private final TqdianbiaoCollectorMapper collectorMapper; |
| | | private final TqdianbiaoSyncLogMapper syncLogMapper; |
| | | |
| | | @Override |
| | | public String fetchRawData(String dimension, String startTime, String endTime) { |
| | | if (!"hour".equals(dimension)) { |
| | | throw new ServiceException("ä»
æ¯ææåå°æ¶åå§æ°æ®"); |
| | | } |
| | | String url = config.getBaseUrl() + "/Api/StatisticEle/hour"; |
| | | String param = String.format( |
| | | "auth=%s&start_time=%s&end_time=%s&ignore_radio=%d", |
| | | config.getAuth(), startTime, endTime, config.getIgnoreRadio() |
| | | ); |
| | | log.warn("è°ç¨è¿ç¨çµè¡¨æ¥å£(è°è¯): {}?{}", url, param); |
| | | return HttpUtils.sendGet(url, param); |
| | | } |
| | | |
| | | @Override |
| | | public List<StatisticEleRecordVo> listRecords(String dimension, String startTime, String endTime, Integer ignoreRadio) { |
| | | if ("hour".equals(dimension) || "collection".equals(dimension)) { |
| | | return queryHourRecords(startTime, endTime); |
| | | } |
| | | return aggregateFromHour(dimension, startTime, endTime, true); |
| | | } |
| | | |
| | | @Override |
| | | public StatisticEleSummaryVo getSummary(String dimension, String startTime, String endTime) { |
| | | if (!StringUtils.hasText(startTime) || !StringUtils.hasText(endTime)) { |
| | | throw new ServiceException("å¼å§æ¶é´åç»ææ¶é´ä¸è½ä¸ºç©º"); |
| | | } |
| | | if ("hour".equals(dimension)) { |
| | | List<StatisticEleRecordVo> detailRecords = queryHourRecords(startTime, endTime); |
| | | List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets( |
| | | detailRecords, StatisticEleAggregateUtil.HOUR_TO_HOUR); |
| | | return buildSummary(detailRecords, chartRecords); |
| | | } |
| | | if (!STAT_DIMENSIONS.contains(dimension)) { |
| | | throw new ServiceException("ç»è®¡ç»´åº¦æ æï¼æ¯æ hour/day/month/quarter/year"); |
| | | } |
| | | |
| | | List<StatisticEleRecordVo> detailRecords = aggregateFromHour(dimension, startTime, endTime, true); |
| | | List<StatisticEleRecordVo> chartRecords = aggregateFromHour(dimension, startTime, endTime, false); |
| | | return buildSummary(detailRecords, chartRecords); |
| | | } |
| | | |
| | | private StatisticEleSummaryVo buildSummary( |
| | | List<StatisticEleRecordVo> detailRecords, List<StatisticEleRecordVo> chartRecords) { |
| | | StatisticEleAggregateUtil.StatisticEleSummaryMetrics metrics = |
| | | StatisticEleAggregateUtil.calcMetrics(chartRecords); |
| | | StatisticEleSummaryVo summary = new StatisticEleSummaryVo(); |
| | | summary.setRecords(detailRecords); |
| | | summary.setChartRecords(chartRecords); |
| | | summary.setRecordCount(metrics.getRecordCount()); |
| | | summary.setTotalConsumption(metrics.getTotalConsumption()); |
| | | summary.setAvgConsumption(metrics.getAvgConsumption()); |
| | | summary.setMaxConsumption(metrics.getMaxConsumption()); |
| | | summary.setMinConsumption(metrics.getMinConsumption()); |
| | | return summary; |
| | | } |
| | | |
| | | @Override |
| | | public StatisticEleSummaryVo getYesterdaySummary() { |
| | | HourRange range = StatisticEleAggregateUtil.yesterdayHourRange(); |
| | | List<StatisticEleRecordVo> records = queryHourRecords(range.startTime(), range.endTime()); |
| | | List<StatisticEleRecordVo> chartRecords = StatisticEleAggregateUtil.aggregateHourToBuckets( |
| | | records, StatisticEleAggregateUtil.HOUR_TO_HOUR); |
| | | StatisticEleSummaryVo summary = buildSummary(records, chartRecords); |
| | | summary.setTotalConsumption(round(StatisticEleAggregateUtil.sumRecordsTotal(records))); |
| | | return summary; |
| | | } |
| | | |
| | | private static double round(double value) { |
| | | return Math.round(value * 100.0) / 100.0; |
| | | } |
| | | |
| | | @Override |
| | | public void exportRecords(String dimension, String startTime, String endTime, HttpServletResponse response) { |
| | | List<StatisticEleRecordVo> records; |
| | | if ("hour".equals(dimension)) { |
| | | records = queryHourRecords(startTime, endTime); |
| | | } else { |
| | | records = aggregateFromHour(dimension, startTime, endTime, true); |
| | | } |
| | | ExcelUtil<StatisticEleRecordVo> util = new ExcelUtil<>(StatisticEleRecordVo.class); |
| | | util.exportExcel(response, records, "è½èç»è®¡æ°æ®"); |
| | | } |
| | | |
| | | @Override |
| | | public StatisticEleSyncStatusVo getSyncStatus() { |
| | | StatisticEleSyncStatusVo status = new StatisticEleSyncStatusVo(); |
| | | status.setMeterCount(Math.toIntExact(meterMapper.selectCount(null))); |
| | | status.setCollectorCount(Math.toIntExact(collectorMapper.selectCount(null))); |
| | | status.setOnlineCollectorCount(Math.toIntExact(collectorMapper.selectCount( |
| | | Wrappers.<TqdianbiaoCollector>lambdaQuery().eq(TqdianbiaoCollector::getOnline, true)))); |
| | | |
| | | Map<String, Long> recordCountByDimension = new HashMap<>(); |
| | | recordCountByDimension.put("hour", eleRecordMapper.selectCount( |
| | | Wrappers.<TqdianbiaoEleRecord>lambdaQuery().eq(TqdianbiaoEleRecord::getDimension, "hour"))); |
| | | status.setRecordCountByDimension(recordCountByDimension); |
| | | |
| | | Map<String, String> lastSyncTimeByType = new HashMap<>(); |
| | | for (String syncType : List.of("collector", "meter", "hour")) { |
| | | TqdianbiaoSyncLog latest = syncLogMapper.selectOne( |
| | | Wrappers.<TqdianbiaoSyncLog>lambdaQuery() |
| | | .eq(TqdianbiaoSyncLog::getSyncType, syncType) |
| | | .eq(TqdianbiaoSyncLog::getStatus, "success") |
| | | .orderByDesc(TqdianbiaoSyncLog::getCreateTime) |
| | | .last("LIMIT 1")); |
| | | if (latest != null && latest.getCreateTime() != null) { |
| | | lastSyncTimeByType.put(syncType, latest.getCreateTime().format(LOG_TIME_FMT)); |
| | | } |
| | | } |
| | | status.setLastSyncTimeByType(lastSyncTimeByType); |
| | | return status; |
| | | } |
| | | |
| | | private List<StatisticEleRecordVo> queryHourRecords(String startTime, String endTime) { |
| | | String normalizedStart = StatisticEleAggregateUtil.normalizeQueryStartTimeKey(startTime); |
| | | String normalizedEnd = StatisticEleAggregateUtil.normalizeQueryEndTimeKey(endTime); |
| | | return eleRecordMapper.selectRecordList(DATA_DIMENSIONS, normalizedStart, normalizedEnd); |
| | | } |
| | | |
| | | private List<StatisticEleRecordVo> aggregateFromHour( |
| | | String dimension, String startTime, String endTime, boolean perMeter) { |
| | | HourRange range = StatisticEleAggregateUtil.toHourQueryRange(dimension, startTime, endTime); |
| | | List<StatisticEleRecordVo> hourRecords = queryHourRecords(range.startTime(), range.endTime()); |
| | | Function<String, String> bucketFn = StatisticEleAggregateUtil.bucketFn(dimension); |
| | | if (perMeter) { |
| | | return StatisticEleAggregateUtil.aggregateHourPerMeter(hourRecords, bucketFn); |
| | | } |
| | | return StatisticEleAggregateUtil.aggregateHourToBuckets(hourRecords, bucketFn); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.http.mapper.TqdianbiaoCollectorMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | import com.ruoyi.http.service.TqdianbiaoCollectorManageService; |
| | | import com.ruoyi.http.service.TqdianbiaoCollectorSyncService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoCollectorManageServiceImpl |
| | | extends ServiceImpl<TqdianbiaoCollectorMapper, TqdianbiaoCollector> |
| | | implements TqdianbiaoCollectorManageService { |
| | | |
| | | private final TqdianbiaoCollectorSyncService collectorSyncService; |
| | | |
| | | @Override |
| | | public IPage<TqdianbiaoCollector> listPage(Page page, TqdianbiaoCollector query) { |
| | | return page(page, Wrappers.<TqdianbiaoCollector>lambdaQuery() |
| | | .and(StringUtils.isNotEmpty(query.getKeyword()), w -> w |
| | | .like(TqdianbiaoCollector::getCollectorNo, query.getKeyword()) |
| | | .or() |
| | | .like(TqdianbiaoCollector::getCollectorId, query.getKeyword()) |
| | | .or() |
| | | .like(TqdianbiaoCollector::getDescription, query.getKeyword())) |
| | | .eq(StringUtils.isNotEmpty(query.getCollectorId()), TqdianbiaoCollector::getCollectorId, query.getCollectorId()) |
| | | .orderByDesc(TqdianbiaoCollector::getUpdateTime)); |
| | | } |
| | | |
| | | @Override |
| | | public boolean addCollector(TqdianbiaoCollector collector) { |
| | | validateCollector(collector); |
| | | if (existsCollectorId(collector.getCollectorId(), null)) { |
| | | throw new ServiceException("éé卿¡£æ¡IDå·²åå¨"); |
| | | } |
| | | collector.setSyncTime(LocalDateTime.now()); |
| | | return save(collector); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateCollector(TqdianbiaoCollector collector) { |
| | | if (collector.getId() == null) { |
| | | throw new ServiceException("IDä¸è½ä¸ºç©º"); |
| | | } |
| | | validateCollector(collector); |
| | | if (existsCollectorId(collector.getCollectorId(), collector.getId())) { |
| | | throw new ServiceException("éé卿¡£æ¡IDå·²åå¨"); |
| | | } |
| | | collector.setSyncTime(LocalDateTime.now()); |
| | | return updateById(collector); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteByIds(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | throw new ServiceException("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | } |
| | | return removeBatchByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public int syncFromRemote() { |
| | | return collectorSyncService.syncCollectors(); |
| | | } |
| | | |
| | | private void validateCollector(TqdianbiaoCollector collector) { |
| | | if (StringUtils.isEmpty(collector.getCollectorId())) { |
| | | throw new ServiceException("éé卿¡£æ¡IDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(collector.getCollectorNo())) { |
| | | throw new ServiceException("ééå¨å·ä¸è½ä¸ºç©º"); |
| | | } |
| | | } |
| | | |
| | | private boolean existsCollectorId(String collectorId, Long excludeId) { |
| | | return count(Wrappers.<TqdianbiaoCollector>lambdaQuery() |
| | | .eq(TqdianbiaoCollector::getCollectorId, collectorId) |
| | | .ne(excludeId != null, TqdianbiaoCollector::getId, excludeId)) > 0; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.http.config.TqdianbiaoConfig; |
| | | import com.ruoyi.http.mapper.TqdianbiaoCollectorMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoCollector; |
| | | import com.ruoyi.http.service.TqdianbiaoCollectorSyncService; |
| | | import com.ruoyi.http.service.TqdianbiaoSyncLogService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Service |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoCollectorSyncServiceImpl implements TqdianbiaoCollectorSyncService { |
| | | |
| | | private final TqdianbiaoConfig config; |
| | | private final TqdianbiaoCollectorMapper collectorMapper; |
| | | private final TqdianbiaoSyncLogService syncLogService; |
| | | |
| | | @Override |
| | | public int syncCollectors() { |
| | | String syncType = "collector"; |
| | | try { |
| | | String url = config.getBaseUrl() + "/Api/Collector"; |
| | | String param = "auth=" + config.getAuth(); |
| | | String raw = HttpUtils.sendGet(url, param); |
| | | JSONArray list = parseList(raw); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int count = 0; |
| | | for (int i = 0; i < list.size(); i++) { |
| | | JSONObject item = list.getJSONObject(i); |
| | | String collectorId = item.getString("id"); |
| | | if (collectorId == null) { |
| | | continue; |
| | | } |
| | | TqdianbiaoCollector entity = collectorMapper.selectOne( |
| | | Wrappers.<TqdianbiaoCollector>lambdaQuery() |
| | | .eq(TqdianbiaoCollector::getCollectorId, collectorId) |
| | | .last("LIMIT 1")); |
| | | if (entity == null) { |
| | | entity = new TqdianbiaoCollector(); |
| | | entity.setCollectorId(collectorId); |
| | | } |
| | | entity.setCollectorNo(item.getString("collectorid")); |
| | | entity.setOnline(item.getBoolean("online")); |
| | | entity.setCsq(item.getInteger("csq")); |
| | | entity.setConnectTime(item.getString("connect_time")); |
| | | entity.setDisconnectTime(item.getString("disconnect_time")); |
| | | entity.setDescription(item.getString("description")); |
| | | entity.setSyncTime(now); |
| | | if (entity.getId() == null) { |
| | | collectorMapper.insert(entity); |
| | | } else { |
| | | collectorMapper.updateById(entity); |
| | | } |
| | | count++; |
| | | } |
| | | syncLogService.logSuccess(syncType, null, null, count); |
| | | return count; |
| | | } catch (Exception e) { |
| | | log.error("ééå¨åæ¥å¤±è´¥", e); |
| | | syncLogService.logFailure(syncType, null, null, e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private JSONArray parseList(String raw) { |
| | | Object parsed = JSON.parse(raw); |
| | | if (parsed instanceof JSONArray) { |
| | | return (JSONArray) parsed; |
| | | } |
| | | JSONObject root = (JSONObject) parsed; |
| | | if (root.getIntValue("status") != 1) { |
| | | throw new IllegalStateException("éé卿¥å£è¿åå¼å¸¸: " + raw); |
| | | } |
| | | Object data = root.get("data"); |
| | | if (data instanceof JSONArray) { |
| | | return (JSONArray) data; |
| | | } |
| | | return new JSONArray(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper; |
| | | import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | import com.ruoyi.http.service.TqdianbiaoEleRecordManageService; |
| | | import com.ruoyi.http.util.StatisticEleReadingUtil; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | @Service |
| | | public class TqdianbiaoEleRecordManageServiceImpl |
| | | extends ServiceImpl<TqdianbiaoEleRecordMapper, TqdianbiaoEleRecord> |
| | | implements TqdianbiaoEleRecordManageService { |
| | | |
| | | private static final Set<String> MANUAL_DIMENSIONS = Set.of("manual"); |
| | | |
| | | private final TqdianbiaoMeterMapper meterMapper; |
| | | |
| | | public TqdianbiaoEleRecordManageServiceImpl(TqdianbiaoMeterMapper meterMapper) { |
| | | this.meterMapper = meterMapper; |
| | | } |
| | | |
| | | @Override |
| | | public IPage<TqdianbiaoEleRecord> listPage(Page page, TqdianbiaoEleRecord query) { |
| | | return page(page, Wrappers.<TqdianbiaoEleRecord>lambdaQuery() |
| | | .eq(StringUtils.isNotEmpty(query.getDimension()), TqdianbiaoEleRecord::getDimension, query.getDimension()) |
| | | .eq(query.getMeterId() != null, TqdianbiaoEleRecord::getMeterId, query.getMeterId()) |
| | | .ge(StringUtils.isNotEmpty(query.getStartTimeKey()), TqdianbiaoEleRecord::getTimeKey, query.getStartTimeKey()) |
| | | .le(StringUtils.isNotEmpty(query.getEndTimeKey()), TqdianbiaoEleRecord::getTimeKey, query.getEndTimeKey()) |
| | | .orderByDesc(TqdianbiaoEleRecord::getTimeKey)); |
| | | } |
| | | |
| | | @Override |
| | | public boolean addRecord(TqdianbiaoEleRecord record) { |
| | | validateRecord(record); |
| | | enrichFromMeter(record); |
| | | if (existsUnique(record, null)) { |
| | | throw new ServiceException("该çµè¡¨å¨è¯¥æ¶é´ç¹å·²åå¨è®°å½"); |
| | | } |
| | | record.setSyncTime(LocalDateTime.now()); |
| | | return save(record); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateRecord(TqdianbiaoEleRecord record) { |
| | | if (record.getId() == null) { |
| | | throw new ServiceException("IDä¸è½ä¸ºç©º"); |
| | | } |
| | | validateRecord(record); |
| | | enrichFromMeter(record); |
| | | if (existsUnique(record, record.getId())) { |
| | | throw new ServiceException("该çµè¡¨å¨è¯¥æ¶é´ç¹å·²åå¨è®°å½"); |
| | | } |
| | | record.setSyncTime(LocalDateTime.now()); |
| | | return updateById(record); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteByIds(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | throw new ServiceException("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | } |
| | | return removeBatchByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public BigDecimal getPrevReading(Long meterId, String timeKey) { |
| | | if (meterId == null || StringUtils.isEmpty(timeKey)) { |
| | | return null; |
| | | } |
| | | TqdianbiaoEleRecord prev = baseMapper.selectPrevReading(meterId, timeKey); |
| | | if (prev == null) { |
| | | return null; |
| | | } |
| | | if (prev.getCurrReading() != null) { |
| | | return prev.getCurrReading(); |
| | | } |
| | | return StatisticEleReadingUtil.parseFirstReading(prev.getEndReading()); |
| | | } |
| | | |
| | | private void enrichFromMeter(TqdianbiaoEleRecord record) { |
| | | TqdianbiaoMeter meter = meterMapper.selectOne( |
| | | Wrappers.<TqdianbiaoMeter>lambdaQuery() |
| | | .eq(TqdianbiaoMeter::getMeterId, record.getMeterId()) |
| | | .last("LIMIT 1")); |
| | | if (meter != null) { |
| | | record.setMeterName(meter.getMeterName()); |
| | | if (record.getRatio() == null && meter.getRate() != null) { |
| | | record.setRatio(meter.getRate()); |
| | | } |
| | | } |
| | | if (record.getPrevReading() != null && record.getCurrReading() != null) { |
| | | record.setTotalConsumption( |
| | | StatisticEleReadingUtil.calcConsumption(record.getPrevReading(), record.getCurrReading(), record.getRatio())); |
| | | } |
| | | } |
| | | |
| | | private void validateRecord(TqdianbiaoEleRecord record) { |
| | | if (record.getMeterId() == null) { |
| | | throw new ServiceException("çµè¡¨ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(record.getDimension()) || !MANUAL_DIMENSIONS.contains(record.getDimension())) { |
| | | throw new ServiceException("ä»
æ¯ææå¨å½å
¥(manual)ç»´åº¦æ°æ®"); |
| | | } |
| | | if (StringUtils.isEmpty(record.getTimeKey())) { |
| | | throw new ServiceException("æ¶é´æ è¯ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (record.getTimeKey().length() != 12) { |
| | | throw new ServiceException("æå¨å½å
¥æ¶é´æ è¯æ ¼å¼åºä¸º YYYYMMDDHHmm"); |
| | | } |
| | | if (record.getCurrReading() == null) { |
| | | throw new ServiceException("æ¬æ¬¡çµéä¸è½ä¸ºç©º"); |
| | | } |
| | | if (record.getPrevReading() == null) { |
| | | throw new ServiceException("䏿¬¡çµéä¸è½ä¸ºç©º"); |
| | | } |
| | | record.setReadingMethod("manual"); |
| | | } |
| | | |
| | | private boolean existsUnique(TqdianbiaoEleRecord record, Long excludeId) { |
| | | return count(Wrappers.<TqdianbiaoEleRecord>lambdaQuery() |
| | | .eq(TqdianbiaoEleRecord::getMeterId, record.getMeterId()) |
| | | .eq(TqdianbiaoEleRecord::getDimension, record.getDimension()) |
| | | .eq(TqdianbiaoEleRecord::getTimeKey, record.getTimeKey()) |
| | | .ne(excludeId != null, TqdianbiaoEleRecord::getId, excludeId)) > 0; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.http.config.TqdianbiaoConfig; |
| | | import com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper; |
| | | import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | import com.ruoyi.http.service.TqdianbiaoEleSyncService; |
| | | import com.ruoyi.http.service.TqdianbiaoSyncLogService; |
| | | import com.ruoyi.http.util.StatisticEleParseUtil; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoEleSyncServiceImpl implements TqdianbiaoEleSyncService { |
| | | |
| | | private static final DateTimeFormatter HOUR_FMT = DateTimeFormatter.ofPattern("yyyyMMddHH"); |
| | | private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd"); |
| | | private static final DateTimeFormatter MONTH_FMT = DateTimeFormatter.ofPattern("yyyyMM"); |
| | | private static final DateTimeFormatter YEAR_FMT = DateTimeFormatter.ofPattern("yyyy"); |
| | | |
| | | private final TqdianbiaoConfig config; |
| | | private final TqdianbiaoEleRecordMapper eleRecordMapper; |
| | | private final TqdianbiaoMeterMapper meterMapper; |
| | | private final TqdianbiaoSyncLogService syncLogService; |
| | | |
| | | @Override |
| | | public int syncHourData() { |
| | | int window = config.getSync().getHourWindow() != null ? config.getSync().getHourWindow() : 2; |
| | | LocalDateTime end = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); |
| | | LocalDateTime start = end.minusHours(window); |
| | | String startTime = start.format(HOUR_FMT); |
| | | String endTime = end.format(HOUR_FMT); |
| | | return syncDimension("hour", startTime, endTime); |
| | | } |
| | | |
| | | @Override |
| | | public int syncLast3DaysHourData() { |
| | | LocalDateTime start = LocalDate.now().minusDays(3).atStartOfDay(); |
| | | LocalDateTime end = LocalDateTime.now().withMinute(0).withSecond(0).withNano(0); |
| | | return syncHourRangeInChunks(start, end); |
| | | } |
| | | |
| | | /** ææå¤ 24 å°æ¶ä¸æ®µåæ¹æåï¼å¹³å°æ¥å£éå¶ï¼ */ |
| | | private int syncHourRangeInChunks(LocalDateTime start, LocalDateTime end) { |
| | | int total = 0; |
| | | LocalDateTime windowStart = start; |
| | | while (!windowStart.isAfter(end)) { |
| | | LocalDateTime windowEnd = windowStart.plusHours(23); |
| | | if (windowEnd.isAfter(end)) { |
| | | windowEnd = end; |
| | | } |
| | | total += syncDimension("hour", windowStart.format(HOUR_FMT), windowEnd.format(HOUR_FMT)); |
| | | windowStart = windowEnd.plusHours(1); |
| | | } |
| | | return total; |
| | | } |
| | | |
| | | @Override |
| | | public int syncDayData() { |
| | | LocalDate yesterday = LocalDate.now().minusDays(1); |
| | | String day = yesterday.format(DAY_FMT); |
| | | return syncDimension("day", day, day); |
| | | } |
| | | |
| | | @Override |
| | | public int syncMonthData() { |
| | | LocalDate now = LocalDate.now(); |
| | | String month = now.format(MONTH_FMT); |
| | | return syncDimension("month", month, month); |
| | | } |
| | | |
| | | @Override |
| | | public int syncYearData() { |
| | | LocalDate now = LocalDate.now(); |
| | | String year = now.format(YEAR_FMT); |
| | | return syncDimension("year", year, year); |
| | | } |
| | | |
| | | private int syncDimension(String dimension, String startTime, String endTime) { |
| | | return syncDimension(dimension, startTime, endTime, config.getIgnoreRadio()); |
| | | } |
| | | |
| | | private int syncDimension(String dimension, String startTime, String endTime, int ignoreRadio) { |
| | | try { |
| | | String url = config.getBaseUrl() + "/Api/StatisticEle/" + dimension; |
| | | String param = String.format( |
| | | "auth=%s&start_time=%s&end_time=%s&ignore_radio=%d", |
| | | config.getAuth(), startTime, endTime, ignoreRadio |
| | | ); |
| | | log.info("忥çµéæ°æ®: {} {}-{} ignore_radio={}", dimension, startTime, endTime, ignoreRadio); |
| | | String raw = HttpUtils.sendGet(url, param); |
| | | List<TqdianbiaoEleRecord> records = StatisticEleParseUtil.parseToEntities(raw, dimension); |
| | | enrichMeterInfo(records); |
| | | int count = upsertRecords(records); |
| | | syncLogService.logSuccess(dimension, startTime, endTime, count); |
| | | return count; |
| | | } catch (Exception e) { |
| | | log.error("çµéåæ¥å¤±è´¥ dimension={} {}-{}", dimension, startTime, endTime, e); |
| | | syncLogService.logFailure(dimension, startTime, endTime, e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private void enrichMeterInfo(List<TqdianbiaoEleRecord> records) { |
| | | for (TqdianbiaoEleRecord record : records) { |
| | | if (record.getMeterId() == null) { |
| | | continue; |
| | | } |
| | | TqdianbiaoMeter meter = meterMapper.selectOne( |
| | | Wrappers.<TqdianbiaoMeter>lambdaQuery() |
| | | .eq(TqdianbiaoMeter::getMeterId, record.getMeterId()) |
| | | .last("LIMIT 1")); |
| | | if (meter != null && StringUtils.hasText(meter.getMeterName())) { |
| | | record.setMeterName(meter.getMeterName()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private int upsertRecords(List<TqdianbiaoEleRecord> records) { |
| | | int count = 0; |
| | | for (TqdianbiaoEleRecord record : records) { |
| | | TqdianbiaoEleRecord existing = eleRecordMapper.selectOne( |
| | | Wrappers.<TqdianbiaoEleRecord>lambdaQuery() |
| | | .eq(TqdianbiaoEleRecord::getMeterId, record.getMeterId()) |
| | | .eq(TqdianbiaoEleRecord::getDimension, record.getDimension()) |
| | | .eq(TqdianbiaoEleRecord::getTimeKey, record.getTimeKey()) |
| | | .last("LIMIT 1")); |
| | | if (existing == null) { |
| | | eleRecordMapper.insert(record); |
| | | } else { |
| | | record.setId(existing.getId()); |
| | | eleRecordMapper.updateById(record); |
| | | } |
| | | count++; |
| | | } |
| | | return count; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | import com.ruoyi.http.service.TqdianbiaoMeterManageService; |
| | | import com.ruoyi.http.service.TqdianbiaoMeterSyncService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoMeterManageServiceImpl |
| | | extends ServiceImpl<TqdianbiaoMeterMapper, TqdianbiaoMeter> |
| | | implements TqdianbiaoMeterManageService { |
| | | |
| | | private static final long MANUAL_METER_ID_BASE = 900_000_000L; |
| | | |
| | | private final TqdianbiaoMeterSyncService meterSyncService; |
| | | |
| | | @Override |
| | | public IPage<TqdianbiaoMeter> listPage(Page page, TqdianbiaoMeter query) { |
| | | return page(page, Wrappers.<TqdianbiaoMeter>lambdaQuery() |
| | | .and(StringUtils.isNotEmpty(query.getKeyword()), w -> w |
| | | .like(TqdianbiaoMeter::getMeterName, query.getKeyword()) |
| | | .or() |
| | | .like(TqdianbiaoMeter::getAddress, query.getKeyword()) |
| | | .or() |
| | | .like(TqdianbiaoMeter::getDescription, query.getKeyword())) |
| | | .eq(query.getMeterId() != null, TqdianbiaoMeter::getMeterId, query.getMeterId()) |
| | | .eq(StringUtils.isNotEmpty(query.getSource()), TqdianbiaoMeter::getSource, query.getSource()) |
| | | .orderByDesc(TqdianbiaoMeter::getUpdateTime)); |
| | | } |
| | | |
| | | @Override |
| | | public boolean addMeter(TqdianbiaoMeter meter) { |
| | | validateManualMeter(meter); |
| | | meter.setSource("manual"); |
| | | meter.setMeterId(generateManualMeterId()); |
| | | if (meter.getRate() == null) { |
| | | meter.setRate(1); |
| | | } |
| | | if (StringUtils.isEmpty(meter.getRelayState())) { |
| | | meter.setRelayState("1"); |
| | | } |
| | | meter.setSyncTime(LocalDateTime.now()); |
| | | return save(meter); |
| | | } |
| | | |
| | | @Override |
| | | public boolean updateMeter(TqdianbiaoMeter meter) { |
| | | if (meter.getId() == null) { |
| | | throw new ServiceException("IDä¸è½ä¸ºç©º"); |
| | | } |
| | | TqdianbiaoMeter existing = getById(meter.getId()); |
| | | if (existing == null) { |
| | | throw new ServiceException("çµè¡¨ä¸åå¨"); |
| | | } |
| | | if (StringUtils.isEmpty(meter.getAddress())) { |
| | | throw new ServiceException("表å°åä¸è½ä¸ºç©º"); |
| | | } |
| | | String meterName = StringUtils.isNotEmpty(meter.getMeterName()) ? meter.getMeterName() : meter.getAddress(); |
| | | existing.setMeterName(meterName); |
| | | existing.setAddress(meter.getAddress()); |
| | | existing.setDescription(meter.getDescription()); |
| | | existing.setRelayState(meter.getRelayState()); |
| | | if (existing.getSource() == null) { |
| | | existing.setSource("sync"); |
| | | } |
| | | if ("manual".equals(existing.getSource())) { |
| | | existing.setRate(meter.getRate()); |
| | | } |
| | | existing.setSyncTime(LocalDateTime.now()); |
| | | return updateById(existing); |
| | | } |
| | | |
| | | @Override |
| | | public boolean deleteByIds(List<Long> ids) { |
| | | if (CollectionUtils.isEmpty(ids)) { |
| | | throw new ServiceException("è¯·éæ©è³å°ä¸æ¡æ°æ®"); |
| | | } |
| | | return removeBatchByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public int syncFromRemote() { |
| | | return meterSyncService.syncMeters(); |
| | | } |
| | | |
| | | private void validateManualMeter(TqdianbiaoMeter meter) { |
| | | if (StringUtils.isEmpty(meter.getMeterName())) { |
| | | throw new ServiceException("çµè¡¨åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(meter.getAddress())) { |
| | | throw new ServiceException("表å°åä¸è½ä¸ºç©º"); |
| | | } |
| | | } |
| | | |
| | | private Long generateManualMeterId() { |
| | | TqdianbiaoMeter maxManual = getOne( |
| | | Wrappers.<TqdianbiaoMeter>lambdaQuery() |
| | | .eq(TqdianbiaoMeter::getSource, "manual") |
| | | .orderByDesc(TqdianbiaoMeter::getMeterId) |
| | | .last("LIMIT 1"), |
| | | false); |
| | | if (maxManual == null || maxManual.getMeterId() == null) { |
| | | return MANUAL_METER_ID_BASE + 1; |
| | | } |
| | | return maxManual.getMeterId() + 1; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.ruoyi.common.utils.http.HttpUtils; |
| | | import com.ruoyi.http.config.TqdianbiaoConfig; |
| | | import com.ruoyi.http.mapper.TqdianbiaoMeterMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoMeter; |
| | | import com.ruoyi.http.service.TqdianbiaoMeterSyncService; |
| | | import com.ruoyi.http.service.TqdianbiaoSyncLogService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Service |
| | | @Slf4j |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoMeterSyncServiceImpl implements TqdianbiaoMeterSyncService { |
| | | |
| | | private final TqdianbiaoConfig config; |
| | | private final TqdianbiaoMeterMapper meterMapper; |
| | | private final TqdianbiaoSyncLogService syncLogService; |
| | | |
| | | @Override |
| | | public int syncMeters() { |
| | | String syncType = "meter"; |
| | | try { |
| | | String url = config.getBaseUrl() + "/Api/Meter"; |
| | | String param = "auth=" + config.getAuth(); |
| | | String raw = HttpUtils.sendGet(url, param); |
| | | JSONArray list = parseList(raw); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int count = 0; |
| | | for (int i = 0; i < list.size(); i++) { |
| | | JSONObject item = list.getJSONObject(i); |
| | | if (!"0".equals(String.valueOf(item.get("device_type")))) { |
| | | continue; |
| | | } |
| | | Long meterId = item.getLong("id"); |
| | | if (meterId == null) { |
| | | continue; |
| | | } |
| | | TqdianbiaoMeter entity = meterMapper.selectOne( |
| | | Wrappers.<TqdianbiaoMeter>lambdaQuery() |
| | | .eq(TqdianbiaoMeter::getMeterId, meterId) |
| | | .last("LIMIT 1")); |
| | | boolean isNew = entity == null; |
| | | if (isNew) { |
| | | entity = new TqdianbiaoMeter(); |
| | | entity.setMeterId(meterId); |
| | | entity.setSource("sync"); |
| | | } |
| | | String savedName = entity.getMeterName(); |
| | | String savedAddress = entity.getAddress(); |
| | | String savedDesc = entity.getDescription(); |
| | | String savedRelay = entity.getRelayState(); |
| | | |
| | | entity.setCollectorId(item.getString("cid")); |
| | | entity.setCollectorNo(item.getString("collectorid")); |
| | | entity.setMeterType(item.getString("type")); |
| | | entity.setCsq(item.getInteger("csq")); |
| | | entity.setRate(item.getInteger("rate")); |
| | | entity.setSyncTime(now); |
| | | |
| | | if (isNew) { |
| | | entity.setAddress(item.getString("address")); |
| | | entity.setRelayState(item.getString("relay_state")); |
| | | entity.setDescription(item.getString("description")); |
| | | entity.setMeterName(StringUtils.hasText(savedName) ? savedName : item.getString("address")); |
| | | } else { |
| | | entity.setMeterName(savedName); |
| | | entity.setAddress(savedAddress); |
| | | entity.setDescription(savedDesc); |
| | | entity.setRelayState(savedRelay); |
| | | } |
| | | if (entity.getId() == null) { |
| | | meterMapper.insert(entity); |
| | | } else { |
| | | meterMapper.updateById(entity); |
| | | } |
| | | count++; |
| | | } |
| | | syncLogService.logSuccess(syncType, null, null, count); |
| | | return count; |
| | | } catch (Exception e) { |
| | | log.error("çµè¡¨åæ¥å¤±è´¥", e); |
| | | syncLogService.logFailure(syncType, null, null, e.getMessage()); |
| | | throw e; |
| | | } |
| | | } |
| | | |
| | | private JSONArray parseList(String raw) { |
| | | Object parsed = JSON.parse(raw); |
| | | if (parsed instanceof JSONArray) { |
| | | return (JSONArray) parsed; |
| | | } |
| | | JSONObject root = (JSONObject) parsed; |
| | | if (root.getIntValue("status") != 1) { |
| | | throw new IllegalStateException("çµè¡¨æ¥å£è¿åå¼å¸¸: " + raw); |
| | | } |
| | | Object data = root.get("data"); |
| | | if (data instanceof JSONArray) { |
| | | return (JSONArray) data; |
| | | } |
| | | return new JSONArray(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.service.impl; |
| | | |
| | | import com.ruoyi.http.mapper.TqdianbiaoSyncLogMapper; |
| | | import com.ruoyi.http.pojo.TqdianbiaoSyncLog; |
| | | import com.ruoyi.http.service.TqdianbiaoSyncLogService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoSyncLogServiceImpl implements TqdianbiaoSyncLogService { |
| | | |
| | | private final TqdianbiaoSyncLogMapper syncLogMapper; |
| | | |
| | | @Override |
| | | public void logSuccess(String syncType, String windowStart, String windowEnd, int recordCount) { |
| | | TqdianbiaoSyncLog log = new TqdianbiaoSyncLog(); |
| | | log.setSyncType(syncType); |
| | | log.setWindowStart(windowStart); |
| | | log.setWindowEnd(windowEnd); |
| | | log.setStatus("success"); |
| | | log.setRecordCount(recordCount); |
| | | log.setApiCallCount(1); |
| | | syncLogMapper.insert(log); |
| | | } |
| | | |
| | | @Override |
| | | public void logFailure(String syncType, String windowStart, String windowEnd, String errorMsg) { |
| | | TqdianbiaoSyncLog log = new TqdianbiaoSyncLog(); |
| | | log.setSyncType(syncType); |
| | | log.setWindowStart(windowStart); |
| | | log.setWindowEnd(windowEnd); |
| | | log.setStatus("fail"); |
| | | log.setRecordCount(0); |
| | | log.setApiCallCount(1); |
| | | log.setErrorMsg(errorMsg != null && errorMsg.length() > 500 ? errorMsg.substring(0, 500) : errorMsg); |
| | | syncLogMapper.insert(log); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.task; |
| | | |
| | | import com.ruoyi.http.config.TqdianbiaoConfig; |
| | | import com.ruoyi.http.service.TqdianbiaoCollectorSyncService; |
| | | import com.ruoyi.http.service.TqdianbiaoEleSyncService; |
| | | import com.ruoyi.http.service.TqdianbiaoMeterSyncService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * 天å¯çµè¡¨å®æ¶åæ¥ï¼ä»
忥ééå¨ãçµè¡¨ãå°æ¶çµéï¼ç»è®¡ç±å°æ¶ç´¯ç§¯è®¡ç®ï¼ |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | @RequiredArgsConstructor |
| | | public class TqdianbiaoSyncTask { |
| | | |
| | | private final TqdianbiaoConfig config; |
| | | private final TqdianbiaoCollectorSyncService collectorSyncService; |
| | | private final TqdianbiaoMeterSyncService meterSyncService; |
| | | private final TqdianbiaoEleSyncService eleSyncService; |
| | | |
| | | @Scheduled(cron = "30 5 * * * ?") |
| | | public void syncCollectors() { |
| | | if (!isEnabled()) return; |
| | | try { |
| | | collectorSyncService.syncCollectors(); |
| | | } catch (Exception e) { |
| | | log.error("ééå¨å®æ¶åæ¥å¼å¸¸", e); |
| | | } |
| | | } |
| | | |
| | | @Scheduled(cron = "30 7 * * * ?") |
| | | public void syncMeters() { |
| | | if (!isEnabled()) return; |
| | | try { |
| | | meterSyncService.syncMeters(); |
| | | } catch (Exception e) { |
| | | log.error("çµè¡¨å®æ¶åæ¥å¼å¸¸", e); |
| | | } |
| | | } |
| | | |
| | | @Scheduled(cron = "30 10 * * * ?") |
| | | public void syncHourEle() { |
| | | if (!isEnabled()) return; |
| | | try { |
| | | eleSyncService.syncHourData(); |
| | | } catch (Exception e) { |
| | | log.error("å°æ¶çµé宿¶åæ¥å¼å¸¸", e); |
| | | } |
| | | } |
| | | |
| | | private boolean isEnabled() { |
| | | return config.getSync() != null && Boolean.TRUE.equals(config.getSync().getEnabled()); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.util; |
| | | |
| | | import com.ruoyi.http.vo.StatisticEleRecordVo; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.time.YearMonth; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.Comparator; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * çµéç»è®¡èåå·¥å
·ï¼åºäºå°æ¶æ°æ®å䏿±æ»ï¼ |
| | | */ |
| | | public final class StatisticEleAggregateUtil { |
| | | |
| | | private static final DateTimeFormatter DAY_FMT = DateTimeFormatter.ofPattern("yyyyMMdd"); |
| | | |
| | | private StatisticEleAggregateUtil() { |
| | | } |
| | | |
| | | public static final Function<String, String> HOUR_TO_DAY = tk -> tk != null && tk.length() >= 8 ? tk.substring(0, 8) : null; |
| | | public static final Function<String, String> HOUR_TO_HOUR = tk -> normalizeHourKey(tk); |
| | | public static final Function<String, String> HOUR_TO_MONTH = tk -> tk != null && tk.length() >= 6 ? tk.substring(0, 6) : null; |
| | | public static final Function<String, String> HOUR_TO_YEAR = tk -> tk != null && tk.length() >= 4 ? tk.substring(0, 4) : null; |
| | | public static final Function<String, String> HOUR_TO_QUARTER = tk -> { |
| | | String monthKey = HOUR_TO_MONTH.apply(tk); |
| | | return monthKey != null ? toQuarterKey(monthKey) : null; |
| | | }; |
| | | |
| | | /** |
| | | * ææ¶é´æ¡¶æ±æ»ï¼å¤çµè¡¨åå¹¶ï¼ç¨äºå¾è¡¨ï¼ |
| | | */ |
| | | public static List<StatisticEleRecordVo> aggregateHourToBuckets( |
| | | List<StatisticEleRecordVo> hourRecords, Function<String, String> bucketFn) { |
| | | Map<String, StatisticEleRecordVo> map = new LinkedHashMap<>(); |
| | | for (StatisticEleRecordVo record : hourRecords) { |
| | | String bucket = bucketFn.apply(record.getTimeKey()); |
| | | if (bucket == null) { |
| | | continue; |
| | | } |
| | | mergeInto(map, bucket, null, record); |
| | | } |
| | | return sorted(map); |
| | | } |
| | | |
| | | /** |
| | | * ææ¶é´æ¡¶+çµè¡¨æ±æ»ï¼ç¨äºæç»ï¼ |
| | | */ |
| | | public static List<StatisticEleRecordVo> aggregateHourPerMeter( |
| | | List<StatisticEleRecordVo> hourRecords, Function<String, String> bucketFn) { |
| | | Map<String, StatisticEleRecordVo> map = new LinkedHashMap<>(); |
| | | for (StatisticEleRecordVo record : hourRecords) { |
| | | String bucket = bucketFn.apply(record.getTimeKey()); |
| | | if (bucket == null || record.getMeterId() == null) { |
| | | continue; |
| | | } |
| | | String key = bucket + "_" + record.getMeterId(); |
| | | mergeInto(map, key, bucket, record); |
| | | StatisticEleRecordVo agg = map.get(key); |
| | | agg.setMeterId(record.getMeterId()); |
| | | agg.setAddress(record.getAddress()); |
| | | agg.setCollectorNo(record.getCollectorNo()); |
| | | } |
| | | return sorted(map); |
| | | } |
| | | |
| | | /** |
| | | * å
¼å®¹ï¼æ timeKey ç´æ¥æ±æ» |
| | | */ |
| | | public static List<StatisticEleRecordVo> aggregateByTimeKey(List<StatisticEleRecordVo> records) { |
| | | Map<String, StatisticEleRecordVo> map = new LinkedHashMap<>(); |
| | | for (StatisticEleRecordVo record : records) { |
| | | String key = record.getTimeKey(); |
| | | if (key == null) { |
| | | continue; |
| | | } |
| | | mergeInto(map, key, key, record); |
| | | } |
| | | return sorted(map); |
| | | } |
| | | |
| | | public static String toQuarterKey(String monthOrDayKey) { |
| | | if (monthOrDayKey == null || monthOrDayKey.length() < 6) { |
| | | return null; |
| | | } |
| | | int year = Integer.parseInt(monthOrDayKey.substring(0, 4)); |
| | | int month = Integer.parseInt(monthOrDayKey.substring(4, 6)); |
| | | int quarter = (month - 1) / 3 + 1; |
| | | return year + "Q" + quarter; |
| | | } |
| | | |
| | | /** |
| | | * ç»è®¡ç»´åº¦ -> å°æ¶ time_key æ¥è¯¢èå´ |
| | | */ |
| | | public static HourRange toHourQueryRange(String dimension, String startTime, String endTime) { |
| | | return switch (dimension) { |
| | | case "hour" -> new HourRange(startTime, endTime); |
| | | case "day" -> new HourRange(startTime + "00", endTime + "23"); |
| | | case "month" -> new HourRange(startTime + "0100", endTime + lastDayOfMonth(endTime) + "23"); |
| | | case "year" -> new HourRange(startTime + "010100", endTime + "123123"); |
| | | case "quarter" -> new HourRange( |
| | | startTime.length() >= 8 ? startTime.substring(0, 8) + "00" : startTime + "00", |
| | | endTime.length() >= 8 ? endTime.substring(0, 8) + "23" : endTime + "23"); |
| | | default -> throw new IllegalArgumentException("䏿¯æç维度: " + dimension); |
| | | }; |
| | | } |
| | | |
| | | public static HourRange yesterdayHourRange() { |
| | | String day = LocalDate.now().minusDays(1).format(DAY_FMT); |
| | | return new HourRange(normalizeQueryStartTimeKey(day + "00"), normalizeQueryEndTimeKey(day + "23")); |
| | | } |
| | | |
| | | /** æ¥è¯¢èµ·å§ time_keyï¼ç»ä¸ 12 ä½ï¼å
¼å®¹ 10 ä½å°æ¶é®ï¼ */ |
| | | public static String normalizeQueryStartTimeKey(String timeKey) { |
| | | if (timeKey == null || timeKey.isBlank()) { |
| | | return timeKey; |
| | | } |
| | | if (timeKey.length() == 8) { |
| | | return timeKey + "0000"; |
| | | } |
| | | if (timeKey.length() == 10) { |
| | | return timeKey + "00"; |
| | | } |
| | | return timeKey.length() > 12 ? timeKey.substring(0, 12) : timeKey; |
| | | } |
| | | |
| | | /** æ¥è¯¢ç»æ time_keyï¼ç»ä¸ 12 ä½ï¼å
¼å®¹ 10 ä½å°æ¶é®ï¼ */ |
| | | public static String normalizeQueryEndTimeKey(String timeKey) { |
| | | if (timeKey == null || timeKey.isBlank()) { |
| | | return timeKey; |
| | | } |
| | | if (timeKey.length() == 8) { |
| | | return timeKey + "2359"; |
| | | } |
| | | if (timeKey.length() == 10) { |
| | | return timeKey + "59"; |
| | | } |
| | | return timeKey.length() > 12 ? timeKey.substring(0, 12) : timeKey; |
| | | } |
| | | |
| | | /** æç»è®°å½æ»ç¨çµéï¼ä¸æ°æ®éé页æ±åæ¹å¼ä¸è´ï¼ */ |
| | | public static double sumRecordsTotal(List<StatisticEleRecordVo> records) { |
| | | return records.stream() |
| | | .map(StatisticEleRecordVo::getTotalConsumption) |
| | | .filter(v -> v != null) |
| | | .mapToDouble(Double::doubleValue) |
| | | .sum(); |
| | | } |
| | | |
| | | public static Function<String, String> bucketFn(String dimension) { |
| | | return switch (dimension) { |
| | | case "hour" -> HOUR_TO_HOUR; |
| | | case "day" -> HOUR_TO_DAY; |
| | | case "month" -> HOUR_TO_MONTH; |
| | | case "quarter" -> HOUR_TO_QUARTER; |
| | | case "year" -> HOUR_TO_YEAR; |
| | | default -> StatisticEleAggregateUtil::normalizeHourKey; |
| | | }; |
| | | } |
| | | |
| | | public static String normalizeHourKey(String timeKey) { |
| | | if (timeKey == null) { |
| | | return null; |
| | | } |
| | | return timeKey.length() >= 10 ? timeKey.substring(0, 10) : timeKey; |
| | | } |
| | | |
| | | public static StatisticEleSummaryMetrics calcMetrics(List<StatisticEleRecordVo> buckets) { |
| | | StatisticEleSummaryMetrics metrics = new StatisticEleSummaryMetrics(); |
| | | metrics.setRecordCount(buckets.size()); |
| | | if (buckets.isEmpty()) { |
| | | metrics.setTotalConsumption(0.0); |
| | | metrics.setAvgConsumption(0.0); |
| | | metrics.setMaxConsumption(0.0); |
| | | metrics.setMinConsumption(0.0); |
| | | return metrics; |
| | | } |
| | | List<Double> values = buckets.stream() |
| | | .map(StatisticEleRecordVo::getTotalConsumption) |
| | | .filter(v -> v != null) |
| | | .collect(Collectors.toList()); |
| | | double total = values.stream().mapToDouble(Double::doubleValue).sum(); |
| | | metrics.setTotalConsumption(round(total)); |
| | | metrics.setAvgConsumption(round(total / values.size())); |
| | | metrics.setMaxConsumption(round(values.stream().mapToDouble(Double::doubleValue).max().orElse(0))); |
| | | metrics.setMinConsumption(round(values.stream().mapToDouble(Double::doubleValue).min().orElse(0))); |
| | | return metrics; |
| | | } |
| | | |
| | | private static void mergeInto(Map<String, StatisticEleRecordVo> map, String mapKey, |
| | | String timeKey, StatisticEleRecordVo record) { |
| | | StatisticEleRecordVo agg = map.computeIfAbsent(mapKey, k -> { |
| | | StatisticEleRecordVo vo = new StatisticEleRecordVo(); |
| | | vo.setTimeKey(timeKey != null ? timeKey : k); |
| | | vo.setTotalConsumption(0.0); |
| | | vo.setSharpConsumption(0.0); |
| | | vo.setPeakConsumption(0.0); |
| | | vo.setFlatConsumption(0.0); |
| | | vo.setValleyConsumption(0.0); |
| | | return vo; |
| | | }); |
| | | agg.setTotalConsumption(add(agg.getTotalConsumption(), record.getTotalConsumption())); |
| | | agg.setSharpConsumption(add(agg.getSharpConsumption(), record.getSharpConsumption())); |
| | | agg.setPeakConsumption(add(agg.getPeakConsumption(), record.getPeakConsumption())); |
| | | agg.setFlatConsumption(add(agg.getFlatConsumption(), record.getFlatConsumption())); |
| | | agg.setValleyConsumption(add(agg.getValleyConsumption(), record.getValleyConsumption())); |
| | | } |
| | | |
| | | private static List<StatisticEleRecordVo> sorted(Map<String, StatisticEleRecordVo> map) { |
| | | return map.values().stream() |
| | | .sorted(Comparator.comparing(StatisticEleRecordVo::getTimeKey)) |
| | | .collect(Collectors.toList()); |
| | | } |
| | | |
| | | private static String lastDayOfMonth(String yyyyMM) { |
| | | YearMonth ym = YearMonth.parse(yyyyMM, DateTimeFormatter.ofPattern("yyyyMM")); |
| | | return yyyyMM + String.format("%02d", ym.lengthOfMonth()); |
| | | } |
| | | |
| | | private static Double add(Double a, Double b) { |
| | | return (a == null ? 0.0 : a) + (b == null ? 0.0 : b); |
| | | } |
| | | |
| | | private static double round(double value) { |
| | | return Math.round(value * 100.0) / 100.0; |
| | | } |
| | | |
| | | public record HourRange(String startTime, String endTime) {} |
| | | |
| | | public static class StatisticEleSummaryMetrics { |
| | | private Double totalConsumption; |
| | | private Double avgConsumption; |
| | | private Double maxConsumption; |
| | | private Double minConsumption; |
| | | private Integer recordCount; |
| | | |
| | | public Double getTotalConsumption() { return totalConsumption; } |
| | | public void setTotalConsumption(Double v) { this.totalConsumption = v; } |
| | | public Double getAvgConsumption() { return avgConsumption; } |
| | | public void setAvgConsumption(Double v) { this.avgConsumption = v; } |
| | | public Double getMaxConsumption() { return maxConsumption; } |
| | | public void setMaxConsumption(Double v) { this.maxConsumption = v; } |
| | | public Double getMinConsumption() { return minConsumption; } |
| | | public void setMinConsumption(Double v) { this.minConsumption = v; } |
| | | public Integer getRecordCount() { return recordCount; } |
| | | public void setRecordCount(Integer v) { this.recordCount = v; } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.util; |
| | | |
| | | import com.alibaba.fastjson2.JSON; |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | import com.alibaba.fastjson2.JSONObject; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.http.pojo.TqdianbiaoEleRecord; |
| | | import com.ruoyi.http.vo.StatisticEleRecordVo; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.ArrayList; |
| | | import java.util.Comparator; |
| | | import java.util.List; |
| | | |
| | | public final class StatisticEleParseUtil { |
| | | |
| | | private StatisticEleParseUtil() { |
| | | } |
| | | |
| | | public static List<TqdianbiaoEleRecord> parseToEntities(String raw, String dimension) { |
| | | JSONObject root = JSON.parseObject(raw); |
| | | if (root == null) { |
| | | throw new ServiceException("çµè¡¨æ¥å£è¿å为空"); |
| | | } |
| | | if (root.getIntValue("status") != 1) { |
| | | String msg = root.getString("msg"); |
| | | throw new ServiceException("çµè¡¨æ¥å£è¿åå¼å¸¸: " + (msg != null ? msg : raw)); |
| | | } |
| | | JSONObject data = root.getJSONObject("data"); |
| | | if (data == null || data.isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | List<TqdianbiaoEleRecord> list = new ArrayList<>(); |
| | | for (String timeKey : data.keySet()) { |
| | | JSONArray records = data.getJSONArray(timeKey); |
| | | if (records == null) { |
| | | continue; |
| | | } |
| | | for (int i = 0; i < records.size(); i++) { |
| | | JSONObject rec = records.getJSONObject(i); |
| | | TqdianbiaoEleRecord entity = new TqdianbiaoEleRecord(); |
| | | entity.setMeterId(rec.getLong("mid")); |
| | | entity.setDimension(dimension); |
| | | entity.setTimeKey(timeKey); |
| | | entity.setReadingMethod("sync"); |
| | | Integer ratio = rec.getInteger("r"); |
| | | entity.setRatio(ratio); |
| | | entity.setStartTime(rec.getString("st")); |
| | | entity.setEndTime(rec.getString("et")); |
| | | |
| | | JSONArray sArr = rec.getJSONArray("s"); |
| | | JSONArray eArr = rec.getJSONArray("e"); |
| | | JSONArray dArr = rec.getJSONArray("d"); |
| | | entity.setStartReading(StatisticEleReadingUtil.formatReadingArray(sArr)); |
| | | entity.setEndReading(StatisticEleReadingUtil.formatReadingArray(eArr)); |
| | | entity.setPrevReading(StatisticEleReadingUtil.firstReading(sArr)); |
| | | entity.setCurrReading(StatisticEleReadingUtil.firstReading(eArr)); |
| | | |
| | | fillConsumption(entity, dArr, ratio, sArr, eArr); |
| | | entity.setSyncTime(now); |
| | | list.add(entity); |
| | | } |
| | | } |
| | | list.sort(Comparator.comparing(TqdianbiaoEleRecord::getTimeKey)); |
| | | return list; |
| | | } |
| | | |
| | | public static List<StatisticEleRecordVo> toVoList(List<TqdianbiaoEleRecord> entities) { |
| | | List<StatisticEleRecordVo> list = new ArrayList<>(); |
| | | for (TqdianbiaoEleRecord entity : entities) { |
| | | list.add(toVo(entity)); |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | public static StatisticEleRecordVo toVo(TqdianbiaoEleRecord entity) { |
| | | StatisticEleRecordVo vo = new StatisticEleRecordVo(); |
| | | vo.setId(entity.getId()); |
| | | vo.setTimeKey(entity.getTimeKey()); |
| | | vo.setMeterId(entity.getMeterId()); |
| | | vo.setMeterName(entity.getMeterName()); |
| | | vo.setRatio(entity.getRatio()); |
| | | vo.setReadingMethod(entity.getReadingMethod()); |
| | | vo.setPrevReading(toDouble(entity.getPrevReading())); |
| | | vo.setCurrReading(toDouble(entity.getCurrReading())); |
| | | vo.setStartTime(entity.getStartTime()); |
| | | vo.setEndTime(entity.getEndTime()); |
| | | vo.setTotalConsumption(toDouble(entity.getTotalConsumption())); |
| | | vo.setSharpConsumption(toDouble(entity.getSharpConsumption())); |
| | | vo.setPeakConsumption(toDouble(entity.getPeakConsumption())); |
| | | vo.setFlatConsumption(toDouble(entity.getFlatConsumption())); |
| | | vo.setValleyConsumption(toDouble(entity.getValleyConsumption())); |
| | | vo.setStartReading(entity.getStartReading()); |
| | | vo.setEndReading(entity.getEndReading()); |
| | | return vo; |
| | | } |
| | | |
| | | private static void fillConsumption(TqdianbiaoEleRecord entity, JSONArray d, Integer ratio, |
| | | JSONArray sArr, JSONArray eArr) { |
| | | BigDecimal rawTotal = null; |
| | | if (d != null && !d.isEmpty()) { |
| | | rawTotal = d.getBigDecimal(0); |
| | | entity.setSharpConsumption(d.size() >= 2 ? d.getBigDecimal(1) : null); |
| | | entity.setPeakConsumption(d.size() >= 3 ? d.getBigDecimal(2) : null); |
| | | entity.setFlatConsumption(d.size() >= 4 ? d.getBigDecimal(3) : null); |
| | | entity.setValleyConsumption(d.size() >= 5 ? d.getBigDecimal(4) : null); |
| | | entity.setDeepValleyConsumption(d.size() >= 6 ? d.getBigDecimal(5) : null); |
| | | } else { |
| | | rawTotal = StatisticEleReadingUtil.calcConsumption( |
| | | StatisticEleReadingUtil.firstReading(sArr), |
| | | StatisticEleReadingUtil.firstReading(eArr), |
| | | 1); |
| | | } |
| | | entity.setTotalConsumption(StatisticEleReadingUtil.calcConsumptionFromRaw(rawTotal, ratio)); |
| | | } |
| | | |
| | | private static Double toDouble(BigDecimal value) { |
| | | return value == null ? null : value.doubleValue(); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.util; |
| | | |
| | | import com.alibaba.fastjson2.JSONArray; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | |
| | | /** |
| | | * çµé读æ°ä¸åçè®¡ç® |
| | | */ |
| | | public final class StatisticEleReadingUtil { |
| | | |
| | | private StatisticEleReadingUtil() { |
| | | } |
| | | |
| | | public static BigDecimal firstReading(JSONArray arr) { |
| | | if (arr == null || arr.isEmpty()) { |
| | | return null; |
| | | } |
| | | return arr.getBigDecimal(0); |
| | | } |
| | | |
| | | public static BigDecimal calcConsumption(BigDecimal prev, BigDecimal curr, Integer ratio) { |
| | | if (prev == null || curr == null) { |
| | | return null; |
| | | } |
| | | BigDecimal diff = curr.subtract(prev); |
| | | return applyRatio(diff, ratio); |
| | | } |
| | | |
| | | public static BigDecimal calcConsumptionFromRaw(BigDecimal raw, Integer ratio) { |
| | | if (raw == null) { |
| | | return null; |
| | | } |
| | | return applyRatio(raw, ratio); |
| | | } |
| | | |
| | | public static BigDecimal applyRatio(BigDecimal value, Integer ratio) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | int r = ratio == null || ratio <= 0 ? 1 : ratio; |
| | | return value.multiply(BigDecimal.valueOf(r)).setScale(4, RoundingMode.HALF_UP); |
| | | } |
| | | |
| | | public static String formatReadingArray(JSONArray arr) { |
| | | if (arr == null || arr.isEmpty()) { |
| | | return ""; |
| | | } |
| | | StringBuilder sb = new StringBuilder(); |
| | | for (int i = 0; i < arr.size(); i++) { |
| | | if (i > 0) { |
| | | sb.append("/"); |
| | | } |
| | | sb.append(arr.getDouble(i)); |
| | | } |
| | | return sb.toString(); |
| | | } |
| | | |
| | | public static BigDecimal parseFirstReading(String reading) { |
| | | if (reading == null || reading.isBlank()) { |
| | | return null; |
| | | } |
| | | int idx = reading.indexOf('/'); |
| | | String first = idx > 0 ? reading.substring(0, idx) : reading; |
| | | try { |
| | | return new BigDecimal(first.trim()); |
| | | } catch (NumberFormatException e) { |
| | | return null; |
| | | } |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.vo; |
| | | |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import lombok.Data; |
| | | |
| | | /** |
| | | * çµè¡¨ç»è®¡æ°æ®è®°å½ |
| | | */ |
| | | @Data |
| | | public class StatisticEleRecordVo { |
| | | |
| | | private Long id; |
| | | |
| | | @Excel(name = "æ¶é´æ è¯") |
| | | private String timeKey; |
| | | |
| | | @Excel(name = "çµè¡¨ID") |
| | | private Long meterId; |
| | | |
| | | @Excel(name = "çµè¡¨åç§°") |
| | | private String meterName; |
| | | |
| | | @Excel(name = "表å°å") |
| | | private String address; |
| | | |
| | | @Excel(name = "åç") |
| | | private Integer ratio; |
| | | |
| | | @Excel(name = "䏿¬¡çµé") |
| | | private Double prevReading; |
| | | |
| | | @Excel(name = "æ¬æ¬¡çµé") |
| | | private Double currReading; |
| | | |
| | | @Excel(name = "æ¬æ¬¡ç¨çµé(kWh)") |
| | | private Double totalConsumption; |
| | | |
| | | @Excel(name = "æè¡¨æ¹å¼") |
| | | private String readingMethod; |
| | | |
| | | @Excel(name = "å¼å§æ¶é´") |
| | | private String startTime; |
| | | |
| | | @Excel(name = "ç»ææ¶é´") |
| | | private String endTime; |
| | | |
| | | @Excel(name = "å°å³°(kWh)") |
| | | private Double sharpConsumption; |
| | | |
| | | @Excel(name = "å³°(kWh)") |
| | | private Double peakConsumption; |
| | | |
| | | @Excel(name = "å¹³(kWh)") |
| | | private Double flatConsumption; |
| | | |
| | | @Excel(name = "è°·(kWh)") |
| | | private Double valleyConsumption; |
| | | |
| | | @Excel(name = "èµ·å§è¯»æ°") |
| | | private String startReading; |
| | | |
| | | @Excel(name = "ç»æè¯»æ°") |
| | | private String endReading; |
| | | |
| | | /** å
¼å®¹å段ï¼é¡µé¢ä¸å±ç¤º */ |
| | | private String collectorNo; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * çµè¡¨ç»è®¡æ±æ» |
| | | */ |
| | | @Data |
| | | public class StatisticEleSummaryVo { |
| | | |
| | | /** æ»ç¨çµé(kWh) */ |
| | | private Double totalConsumption; |
| | | |
| | | /** å¹³åç¨çµé(kWh) */ |
| | | private Double avgConsumption; |
| | | |
| | | /** æå¤§ç¨çµé(kWh) */ |
| | | private Double maxConsumption; |
| | | |
| | | /** æå°ç¨çµé(kWh) */ |
| | | private Double minConsumption; |
| | | |
| | | /** æ°æ®æ¡æ° */ |
| | | private Integer recordCount; |
| | | |
| | | /** å¾è¡¨æ°æ®ï¼ææ¶é´æ±æ»ï¼ */ |
| | | private List<StatisticEleRecordVo> chartRecords; |
| | | |
| | | /** æç»è®°å½ï¼æçµè¡¨ï¼ */ |
| | | private List<StatisticEleRecordVo> records; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.http.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.util.Map; |
| | | |
| | | @Data |
| | | public class StatisticEleSyncStatusVo { |
| | | |
| | | private Integer meterCount; |
| | | |
| | | private Integer collectorCount; |
| | | |
| | | private Integer onlineCollectorCount; |
| | | |
| | | private Map<String, Long> recordCountByDimension; |
| | | |
| | | private Map<String, String> lastSyncTimeByType; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8" ?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.http.mapper.TqdianbiaoEleRecordMapper"> |
| | | |
| | | <select id="selectRecordList" resultType="com.ruoyi.http.vo.StatisticEleRecordVo"> |
| | | SELECT |
| | | r.id AS id, |
| | | r.time_key AS timeKey, |
| | | r.meter_id AS meterId, |
| | | COALESCE(r.meter_name, m.meter_name) AS meterName, |
| | | m.address AS address, |
| | | r.ratio AS ratio, |
| | | r.reading_method AS readingMethod, |
| | | r.prev_reading AS prevReading, |
| | | r.curr_reading AS currReading, |
| | | r.start_time AS startTime, |
| | | r.end_time AS endTime, |
| | | r.total_consumption AS totalConsumption, |
| | | r.sharp_consumption AS sharpConsumption, |
| | | r.peak_consumption AS peakConsumption, |
| | | r.flat_consumption AS flatConsumption, |
| | | r.valley_consumption AS valleyConsumption, |
| | | r.start_reading AS startReading, |
| | | r.end_reading AS endReading |
| | | FROM tqdianbiao_ele_record r |
| | | LEFT JOIN tqdianbiao_meter m ON r.meter_id = m.meter_id |
| | | WHERE r.dimension IN |
| | | <foreach collection="dimensions" item="dim" open="(" separator="," close=")"> |
| | | #{dim} |
| | | </foreach> |
| | | AND RPAD(r.time_key, 12, '0') >= #{startTime} |
| | | AND RPAD(r.time_key, 12, '0') <= #{endTime} |
| | | ORDER BY r.time_key DESC, r.meter_id ASC |
| | | </select> |
| | | |
| | | <select id="selectPrevReading" resultType="com.ruoyi.http.pojo.TqdianbiaoEleRecord"> |
| | | SELECT id, meter_id, time_key, curr_reading, end_reading, ratio |
| | | FROM tqdianbiao_ele_record |
| | | WHERE meter_id = #{meterId} |
| | | AND time_key < #{timeKey} |
| | | ORDER BY time_key DESC |
| | | LIMIT 1 |
| | | </select> |
| | | |
| | | </mapper> |