Browse Source

Merge remote-tracking branch 'origin/develop' into develop

develop
wangsixiang 1 year ago
parent
commit
60a368b674
  1. 48
      zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java
  2. 74
      zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java
  3. 7
      zc-business/src/main/java/com/zc/business/domain/DcFacility.java
  4. 67
      zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java
  5. 200
      zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java
  6. 80
      zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java
  7. 6
      zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java
  8. 129
      zc-business/src/main/java/com/zc/business/domain/OdsTollEnpassData.java
  9. 118
      zc-business/src/main/java/com/zc/business/domain/OdsTollEtctuData.java
  10. 212
      zc-business/src/main/java/com/zc/business/domain/OdsTollExpassData.java
  11. 44
      zc-business/src/main/java/com/zc/business/domain/OdsTollViuData.java
  12. 26
      zc-business/src/main/java/com/zc/business/enums/FacilityTypeEnum.java
  13. 61
      zc-business/src/main/java/com/zc/business/enums/VehicleTypeEnum.java
  14. 33
      zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java
  15. 33
      zc-business/src/main/java/com/zc/business/mapper/DcTollStationStatisticsDataMapper.java
  16. 13
      zc-business/src/main/java/com/zc/business/mapper/OdsTollEnpassDataMapper.java
  17. 14
      zc-business/src/main/java/com/zc/business/mapper/OdsTollEtctuDataMapper.java
  18. 15
      zc-business/src/main/java/com/zc/business/mapper/OdsTollExpassDataMapper.java
  19. 14
      zc-business/src/main/java/com/zc/business/mapper/OdsTollViuDataMapper.java
  20. 2
      zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java
  21. 28
      zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java
  22. 37
      zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java
  23. 36
      zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java
  24. 66
      zc-business/src/main/java/com/zc/business/service/IDcTrafficSectionStatisticsService.java
  25. 62
      zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java
  26. 48
      zc-business/src/main/java/com/zc/business/service/IOdsTollEnpassDataService.java
  27. 40
      zc-business/src/main/java/com/zc/business/service/IOdsTollEtctuDataService.java
  28. 45
      zc-business/src/main/java/com/zc/business/service/IOdsTollExpassDataService.java
  29. 11
      zc-business/src/main/java/com/zc/business/service/IOdsTollViuDataService.java
  30. 138
      zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java
  31. 170
      zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java
  32. 200
      zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java
  33. 506
      zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java
  34. 503
      zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java
  35. 244
      zc-business/src/main/java/com/zc/business/service/impl/OdsTollEnpassDataServiceImpl.java
  36. 234
      zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java
  37. 239
      zc-business/src/main/java/com/zc/business/service/impl/OdsTollExpassDataServiceImpl.java
  38. 18
      zc-business/src/main/java/com/zc/business/service/impl/OdsTollViuDataServiceImpl.java
  39. 5
      zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java
  40. 148
      zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java
  41. 152
      zc-business/src/main/java/com/zc/business/statistics/cache/gantry/MonthlyTrafficGantryStatisticsCache.java
  42. 154
      zc-business/src/main/java/com/zc/business/statistics/cache/gantry/QuarterlyTrafficGantryStatisticsCache.java
  43. 148
      zc-business/src/main/java/com/zc/business/statistics/cache/gantry/YearlyTrafficGantryStatisticsCache.java
  44. 21
      zc-business/src/main/java/com/zc/business/statistics/cache/section/DailyTrafficSectionStatisticsCache.java
  45. 19
      zc-business/src/main/java/com/zc/business/statistics/cache/section/MonthlyTrafficSectionStatisticsCache.java
  46. 20
      zc-business/src/main/java/com/zc/business/statistics/cache/section/QuarterlyTrafficSectionStatisticsCache.java
  47. 20
      zc-business/src/main/java/com/zc/business/statistics/cache/section/YearlyTrafficSectionStatisticsCache.java
  48. 148
      zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/DailyTrafficTollStationStatisticsCache.java
  49. 152
      zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/MonthlyTrafficTollStationStatisticsCache.java
  50. 154
      zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/QuarterlyTrafficTollStationStatisticsCache.java
  51. 148
      zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/YearlyTrafficTollStationStatisticsCache.java
  52. 241
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java
  53. 2
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionAnalysis.java
  54. 31
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionStatistics.java
  55. 243
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficTollStationStatistics.java
  56. 91
      zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml
  57. 94
      zc-business/src/main/resources/mapper/business/DcTollStationStatisticsDataMapper.xml
  58. 2
      zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml

48
zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java

@ -14,6 +14,8 @@ import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
/**
@ -68,15 +70,16 @@ public class DcEmergencyPlansController extends BaseController {
}
/**
* 感知事件-根据事件数据查询事件预案列表
* 交通事件-切换智能发布生成内容
*/
// @ApiOperation("感知事件-根据事件数据查询事件预案列表")
@PreAuthorize("@ss.hasPermi('business:plans:list')")
@PostMapping("/list/warning/type")
public AjaxResult listByEventType(@RequestBody DcWarning dcWarning) {
List<DcEmergencyPlans> dcEmergencyPlansList = dcEmergencyPlansService.selectDcEmergencyPlansByWarningType(dcWarning);
return AjaxResult.success(dcEmergencyPlansList);
@ApiOperation("交通事件-切换智能发布生成内容")
// @PreAuthorize("@ss.hasPermi('business:plans:list')")
@PostMapping("/list/event/emergencyPlans")
public AjaxResult switchIntelligentPublishingToContent(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
List<DcEmergencyPlans> dcEmergencyPlansList = new ArrayList<>();
dcEmergencyPlansList.add(dcEventAnDcEmergencyPlans.getDcEmergencyPlans());
dcEmergencyPlansService.dispositionDeviceContent(dcEmergencyPlansList,dcEventAnDcEmergencyPlans.getDcEvent());
return AjaxResult.success(dcEmergencyPlansList.get(0));
}
/**
@ -89,25 +92,6 @@ public class DcEmergencyPlansController extends BaseController {
return AjaxResult.success(dcEmergencyPlansService.eventBoardConfirm(dcEventAnDcEmergencyPlans));
}
/**
* 感知事件-情报板确认回显原始模板
*/
// @ApiOperation("感知事件-情报板确认回显原始模板")
@PreAuthorize("@ss.hasPermi('business:plans:list')")
@PostMapping("/warning/board/confirm")
public AjaxResult warningBoardConfirm(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
return AjaxResult.success(dcEmergencyPlansService.warningBoardConfirm(dcEventAnDcEmergencyPlans));
}
/**
* 感知事件-情报板自动生成文字
*/
// @ApiOperation("感知事件-情报板自动生成文字")
@PostMapping("/warning/automatic")
public AjaxResult warningAutomaticGeneration(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
return AjaxResult.success(dcEmergencyPlansService.warningAutomaticGeneration(dcEventAnDcEmergencyPlans));
}
/**
* 交通事件-情报板自动生成文字
*/
@ -145,16 +129,6 @@ public class DcEmergencyPlansController extends BaseController {
return AjaxResult.success(dcEmergencyPlansService.selectEventPlanAssocById(assocId));
}
/**
* 感知事件确定
*/
// @ApiOperation("感知事件确认")
@PreAuthorize("@ss.hasPermi('business:plans:edit')")
@PostMapping("/warning/confirm")
public AjaxResult warningConfirm(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
return AjaxResult.success(dcEmergencyPlansService.executionWarningConfirmation(dcEventAnDcEmergencyPlans));
}
/**
* 新增事件预案
*/

74
zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java

@ -1,11 +1,12 @@
package com.zc.business.controller;
import com.ruoyi.common.core.domain.AjaxResult;
import com.zc.business.domain.DcCongestedSectionData;
import com.zc.business.domain.DcTrafficMetricsData;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.domain.*;
import com.zc.business.request.DcTrafficMetricsDataRequest;
import com.zc.business.request.DcTrafficSectionDataRequest;
import com.zc.business.service.IDcGantryStatisticsDataService;
import com.zc.business.service.IDcTollStationStatisticsDataService;
import com.zc.business.service.IDcTrafficSectionStatisticsService;
import com.zc.business.service.IDcTrafficStatisticsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -24,9 +25,17 @@ import java.util.List;
@RequestMapping("/business/traffic-statistics")
public class DcTrafficStatisticsController {
@Autowired
private IDcTrafficSectionStatisticsService dcTrafficSectionStatisticsService;
@Autowired
private IDcTrafficStatisticsService dcTrafficStatisticsService;
@Autowired
private IDcTollStationStatisticsDataService dcTollStationStatisticsDataService;
@Autowired
private IDcGantryStatisticsDataService dcGantryStatisticsDataService;
/**
* 获取当前交通截面数据
@ -38,7 +47,7 @@ public class DcTrafficStatisticsController {
@GetMapping("/current/sections")
public AjaxResult currentSections(DcTrafficSectionDataRequest request){
// 调用服务层方法,查询当前交通截面数据
List<DcTrafficSectionData> dcTrafficMetricsData = dcTrafficStatisticsService.currentSections(request);
List<DcTrafficSectionData> dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentSections(request);
// 将查询结果封装为AjaxResult对象返回
return AjaxResult.success(dcTrafficMetricsData);
}
@ -53,7 +62,7 @@ public class DcTrafficStatisticsController {
@GetMapping("/history/sections")
public AjaxResult historySections(DcTrafficSectionDataRequest request){
// 调用服务层方法,获取历史交通截面的统计数据
List<DcTrafficSectionData> dcTrafficMetricsData = dcTrafficStatisticsService.historySections(request);
List<DcTrafficSectionData> dcTrafficMetricsData = dcTrafficSectionStatisticsService.historySections(request);
// 将获取到的历史交通截面数据封装成AjaxResult对象并返回
return AjaxResult.success(dcTrafficMetricsData);
}
@ -69,7 +78,7 @@ public class DcTrafficStatisticsController {
@GetMapping("/current/metrics")
public AjaxResult currentTrafficMetrics(DcTrafficMetricsDataRequest request){
// 调用服务层方法,获取当前交通指标数据
List<DcTrafficMetricsData> dcTrafficMetricsData = dcTrafficStatisticsService.currentTrafficMetrics(request);
List<DcTrafficMetricsData> dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentTrafficMetrics(request);
// 将获取到的交通指标数据封装为成功的结果并返回
return AjaxResult.success(dcTrafficMetricsData);
}
@ -84,7 +93,7 @@ public class DcTrafficStatisticsController {
@GetMapping("/history/metrics")
public AjaxResult historyTrafficMetrics(DcTrafficMetricsDataRequest request){
// 调用服务层方法,查询历史交通特征指数数据
List<DcTrafficMetricsData> dcTrafficMetricsDataList = dcTrafficStatisticsService.historyTrafficMetrics(request);
List<DcTrafficMetricsData> dcTrafficMetricsDataList = dcTrafficSectionStatisticsService.historyTrafficMetrics(request);
// 将查询结果封装成成功响应并返回
return AjaxResult.success(dcTrafficMetricsDataList);
}
@ -99,9 +108,58 @@ public class DcTrafficStatisticsController {
@GetMapping("/current/congested")
public AjaxResult currentCongestedSection(Byte direction){
// 调用服务层方法,获取当前交通指标数据
List<DcCongestedSectionData> dcTrafficMetricsData = dcTrafficStatisticsService.currentCongestedSection(direction);
List<DcCongestedSectionData> dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentCongestedSection(direction);
// 将获取到的交通指标数据封装为成功的结果并返回
return AjaxResult.success(dcTrafficMetricsData);
}
/**
* 获取累计车流量
*
* 本方法用于根据特定条件查询历史车流量数据通过接收一个包含查询条件的请求对象
* 调用服务层方法进行数据查询并将查询结果封装成AjaxResult对象返回
*
* @param request 包含查询条件的请求对象用于获取特定条件下的历史车流量数据请求对象中可能包含了时间范围地点等查询条件
* @return 返回一个AjaxResult对象其中包含了查询到的历史车流量数据列表AjaxResult是本系统中用于封装响应数据的通用对象其中的success方法用于将查询结果封装为成功响应返回
*/
@ApiOperation("获取累计车流量")
@GetMapping("/history/flow")
public AjaxResult historyFlow(DcStatisticsData request){
// 调用服务层方法,根据请求条件查询历史车流量数据
List<DcStatisticsData> dcStatisticsData = dcTrafficStatisticsService.historyFlow(request);
// 将查询结果封装为成功响应并返回
return AjaxResult.success(dcStatisticsData);
}
/**
* 获取收费站统计数据
*
* @param request 包含查询条件的请求对象用于筛选历史收费站统计数据
* @return 返回一个AjaxResult对象其中包含了查询到的收费站统计数据列表
*/
@ApiOperation("获取收费站统计数据")
@GetMapping("/history/toll-station")
public AjaxResult historyTollStation(DcTollStationStatisticsData request){
// 调用服务层方法,根据请求条件查询历史车收费站数据
List<DcTollStationStatisticsData> dcStatisticsData = dcTollStationStatisticsDataService.tollStationData(request);
// 将查询结果封装为成功响应并返回
return AjaxResult.success(dcStatisticsData);
}
/**
* 获取门架统计数据
*
* @param request 包含查询条件的请求对象用于筛选历史门架统计数据
* @return 返回一个AjaxResult对象其中包含了查询到的门架统计数据列表
*/
@ApiOperation("获取门架统计数据")
@GetMapping("/history/gantry")
public AjaxResult historyTollStation(DcGantryStatisticsData request){
// 调用服务层方法,根据请求条件查询历史车门架数据
List<DcGantryStatisticsData> dcStatisticsData = dcGantryStatisticsDataService.gantryData(request);
// 将查询结果封装为成功响应并返回
return AjaxResult.success(dcStatisticsData);
}
}

7
zc-business/src/main/java/com/zc/business/domain/DcFacility.java

@ -23,9 +23,9 @@ public class DcFacility {
private String direction;
@ApiModelProperty("道路标识")
private String roadId;
@ApiModelProperty("设类型")
@ApiModelProperty("设类型")
private Integer facilityType;
@ApiModelProperty("设名称")
@ApiModelProperty("设名称")
private String facilityName;
@ApiModelProperty("备注")
private String remark;
@ -35,5 +35,6 @@ public class DcFacility {
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("设施编号")
private String facilityCode;
}

67
zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java

@ -0,0 +1,67 @@
package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.Serializable;
import java.util.Objects;
/**
* 门架数据统计实体类
* @author xiepufeng
*/
@Data
public class DcGantryStatisticsData extends DcStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 门架标识
*/
private String gantryCode;
/**
* 重写equals方法用于比较两个DcGantryStatisticsData对象是否相等
*
* @param o 要与当前对象比较的对象
* @return 如果两个对象相等则返回true否则返回false
*/
@Override
public boolean equals(Object o) {
if (this == o) return true; // 比较是否为同一对象,是则直接返回true
if (o == null || getClass() != o.getClass()) return false; // 比较对象是否为空或类型是否不同,是则返回false
DcGantryStatisticsData that = (DcGantryStatisticsData) o; // 类型转换
// 使用Objects.equals方法比较对象的各个属性值
return Objects.equals(gantryCode, that.gantryCode) &&
Objects.equals(getReportTime(), that.getReportTime()) &&
Objects.equals(getPeriodType(), that.getPeriodType());
}
/**
* 重写hashCode方法基于对象的属性生成哈希码
* @return 对象的哈希码值
*/
@Override
public int hashCode() {
return Objects.hash(gantryCode, getReportTime(), getPeriodType());
}
/**
* 生成唯一标识符
* 该方法通过将多个属性结合然后对结合后的字符串进行MD5哈希处理来生成一个唯一标识符
* 结合的属性包括门架站点标识统计日期道路方向统计粒度和出入类型
*/
@Override
public void generateUniqueId() {
// 将多个属性按照指定格式组合成一个字符串
String combinedAttributes = gantryCode + "_" + DateUtil.format(getStatisticalDate(), "yyyyMMdd_HHmmss") + "_" + getPeriodType();
// 对组合后的字符串进行MD5哈希处理,生成唯一标识符,并赋值给当前对象的id属性
this.setId(DigestUtils.md5Hex(combinedAttributes));
}
}

200
zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java

@ -0,0 +1,200 @@
package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
public class DcStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
private String id;
/**
* 统计时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date statisticalDate;
/**
* 上报时间
*/
@TableField(exist = false)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date reportTime;
/**
* 车流量
*/
private Integer trafficVolume;
/**
* 统计粒度
* 1-
* 2-
* 3-
* 4-
*/
private Byte periodType;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改时间
*/
private Date updateTime;
/**
* 1型客车车流量
*/
private Integer type1PassengerFlow;
/**
* 2型客车车流量
*/
private Integer type2PassengerFlow;
/**
* 3型客车车流量
*/
private Integer type3PassengerFlow;
/**
* 4型客车车流量
*/
private Integer type4PassengerFlow;
/**
* 1型货车车流量
*/
private Integer type1TruckFlow;
/**
* 2型货车车流量
*/
private Integer type2TruckFlow;
/**
* 3型货车车流量
*/
private Integer type3TruckFlow;
/**
* 4型货车车流量
*/
private Integer type4TruckFlow;
/**
* 5型货车车流量
*/
private Integer type5TruckFlow;
/**
* 6型货车车流量
*/
private Integer type6TruckFlow;
/**
* 1型专项作业车车流量
*/
private Integer type1SpecialVehicleFlow;
/**
* 2型专项作业车车流量
*/
private Integer type2SpecialVehicleFlow;
/**
* 3型专项作业车车流量
*/
private Integer type3SpecialVehicleFlow;
/**
* 4型专项作业车车流量
*/
private Integer type4SpecialVehicleFlow;
/**
* 5型专项作业车车流量
*/
private Integer type5SpecialVehicleFlow;
/**
* 6型专项作业车车流量
*/
private Integer type6SpecialVehicleFlow;
/**
* 开始时间
*/
@TableField(exist = false)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
* 结束时间
*/
@TableField(exist = false)
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
public void setPeriodType(Byte periodType) {
this.periodType = periodType;
}
/**
* 设置交通数据的统计周期类型
* @param periodType 统计周期类型的枚举值
*/
public void setPeriodType(TrafficDataPeriodTypeEnum periodType) {
this.periodType = periodType.getCode(); // 将枚举类型转换为代码值存储
}
/**
* 根据给定的统计周期类型和日期设置统计日期为相应周期的起始日期
* @param statisticalDate 原始统计日期
* @param typeEnum 统计周期类型
*/
public void setStatisticalDate(Date statisticalDate, TrafficDataPeriodTypeEnum typeEnum) {
switch (typeEnum) {
case DAY:
// 设置为当天的起始时间
this.statisticalDate = DateUtil.beginOfDay(statisticalDate);
break;
case MONTH:
// 设置为当月的起始日期
this.statisticalDate = DateUtil.beginOfMonth(statisticalDate);
break;
case QUARTER:
// 设置为当季度的起始日期
this.statisticalDate = DateUtil.beginOfQuarter(statisticalDate);
break;
case YEAR:
// 设置为当年的起始日期
this.statisticalDate = DateUtil.beginOfYear(statisticalDate);
break;
default:
// 如果不是预定义的周期类型,则不做任何处理
this.statisticalDate = statisticalDate;
}
}
public void generateUniqueId() {
}
}

80
zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java

@ -0,0 +1,80 @@
package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
import java.io.Serializable;
import java.util.Objects;
/**
* 收费站数据统计实体类
* @author xiepufeng
*/
@Data
public class DcTollStationStatisticsData extends DcStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;
// 入口
public static final byte ENTRANCE = 1;
// 出口
public static final byte EXIT = 2;
/**
* 收费站站点标识
*/
private String tollStationCode;
/**
* 出入类型
* 1-入口
* 2-出口
*/
private Byte accessType;
/**
* 重写equals方法用于比较两个DcTollStationStatisticsData对象是否相等
*
* @param o 要与当前对象比较的对象
* @return 如果两个对象相等则返回true否则返回false
*/
@Override
public boolean equals(Object o) {
if (this == o) return true; // 比较是否为同一对象,是则直接返回true
if (o == null || getClass() != o.getClass()) return false; // 比较对象是否为空或类型是否不同,是则返回false
DcTollStationStatisticsData that = (DcTollStationStatisticsData) o; // 类型转换
// 使用Objects.equals方法比较对象的各个属性值
return Objects.equals(tollStationCode, that.tollStationCode) &&
Objects.equals(getReportTime(), that.getReportTime()) &&
Objects.equals(getPeriodType(), that.getPeriodType()) &&
Objects.equals(accessType, that.accessType);
}
/**
* 重写hashCode方法基于对象的属性生成哈希码
* @return 对象的哈希码值
*/
@Override
public int hashCode() {
return Objects.hash(tollStationCode, getReportTime(), getPeriodType(), accessType);
}
/**
* 生成唯一标识符
* 该方法通过将多个属性结合然后对结合后的字符串进行MD5哈希处理来生成一个唯一标识符
* 结合的属性包括收费站站点标识统计日期道路方向统计粒度和出入类型
*/
@Override
public void generateUniqueId() {
// 将多个属性按照指定格式组合成一个字符串
String combinedAttributes = tollStationCode + "_" + DateUtil.format(getStatisticalDate(), "yyyyMMdd_HHmmss") + "_" + getPeriodType() + "_" + accessType;
// 对组合后的字符串进行MD5哈希处理,生成唯一标识符,并赋值给当前对象的id属性
this.setId(DigestUtils.md5Hex(combinedAttributes));
}
}

6
zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java

@ -58,4 +58,10 @@ public class EventPlanAssoc {
*/
@ApiModelProperty("执行操作结果")
private String controlResult;
/**
* 执行操作
*/
@ApiModelProperty("执行操作")
private String control;
}

129
zc-business/src/main/java/com/zc/business/domain/OdsTollEnpassData.java

@ -0,0 +1,129 @@
package com.zc.business.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 收费站入口流水数据
* @author xiepufeng
*/
@Data
public class OdsTollEnpassData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 入口通行时间
*/
private Date entime;
/**
* 交易流水号主键
*/
private String id;
/**
* 交易编码
*/
private String transcode;
/**
* 车道类型
*/
private Short lanetype;
/**
* 入口站号
*/
private String entollstation;
/**
* 入口车道号
*/
private String entolllane;
/**
* 入口站HEX编码
*/
private String entollstationhex;
/**
* 入口车道HEX编码
*/
private String entolllanehex;
/**
* 入口站号(国标)
*/
private String entollstationid;
/**
* 入口车道号(国标)
*/
private String entolllaneid;
/**
* 通行介质类型1-OBU2-CPC卡3-纸券4-M1卡9-无通行介质
*/
private Short mediatype;
/**
* 过车数量
*/
private Integer vcount;
/**
* 车牌号经过MD5加密
*/
@TableField("vlp_MD5")
private String vlpMD5;
/**
* 车牌颜色
*/
private Short vlpc;
/**
* 车型1-一型客车...具体车型定义请参考注释
*/
private Integer vehicletype;
/**
* 车种0-普通8-军警10-紧急...具体车种定义请参考注释
*/
private Short vehicleclass;
/**
* 入口重量
*/
private String enweight;
/**
* 轴组信息
*/
private String axisinfo;
/**
* 限载总重KG
*/
private Integer limitweight;
/**
* 超限率
*/
private Short overweightrate;
/**
* 入口轴数
*/
private Integer enaxlecount;
/**
* 通行标识ID
*/
private String passid;
}

118
zc-business/src/main/java/com/zc/business/domain/OdsTollEtctuData.java

@ -0,0 +1,118 @@
package com.zc.business.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 门架流水数据实体类
* @author xiepufeng
*/
@Data
public class OdsTollEtctuData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 计费交易时间
*/
private Date transtime;
/**
* 门架编号
*/
private String gantryid;
/**
* 计费交易编号主键
*/
private String tradeid;
/**
* 门架顺序号
*/
private Integer gantryordernum;
/**
* 门架 Hex
*/
private String gantryhex;
/**
* 对向门架 Hex
*/
private String gantryhexopposite;
/**
* 通行介质类型1-OBU2-CPC卡3-纸券4-M1卡9-无通行介质
*/
private Integer mediatype;
/**
* 收费单元编号组
*/
private String tollintervalid;
/**
* 车牌号经过MD5加密
*/
@TableField("vehicleplate_MD5")
private String vehicleplateMD5;
/**
* 车型1-一型客车...具体车型定义请参考注释
*/
private Integer vehicletype;
/**
* 车种0-普通8-军警10-紧急...具体车种定义请参考注释
*/
private Integer vehicleclass;
/**
* 交易类型标识
*/
private String transtype;
/**
* 入口车道编号
*/
private String entolllaneid;
/**
* 入口站hex字符串
*/
private String entollstationhex;
/**
* 入口时间
*/
private String entime;
/**
* 入口车道类型
*/
private String enlanetype;
/**
* 通行标识ID
*/
private String passid;
/**
* 交易结果0-成功1-失败
*/
private Integer traderesult;
/**
* 门架类型1-路段2-省界入口3-省界出口
*/
private String gantrytype;
/**
* 计费车型与车型字段定义相同
*/
private Integer feevehicletype;
}

212
zc-business/src/main/java/com/zc/business/domain/OdsTollExpassData.java

@ -0,0 +1,212 @@
package com.zc.business.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 收费站出口流水数据实体类
* @author xiepufeng
*/
@Data
public class OdsTollExpassData implements Serializable {
/**
* 出口时间
*/
private Date extime;
/**
* 交易流水号主键
*/
private String id;
/**
* 交易支付方式1-出口ETC通行2-出口ETC刷卡通行11-现金12-其他第三方账户支付13-银联卡支付16-支付宝17-微信
*/
private Short transpaytype;
/**
* 交易编码
*/
private String transcode;
/**
* 入口重量
*/
private Integer enweight;
/**
* 入口轴数
*/
private Integer enaxlecount;
/**
* 入口站号
*/
private String entollstation;
/**
* 入口站名称
*/
private String entollstationname;
/**
* 入口车道号
*/
private String entolllane;
/**
* 入口站HEX编码
*/
private String entollstationhex;
/**
* 入口车道HEX编码
*/
private String entolllanehex;
/**
* 入口站号(国标)
*/
private String entollstationid;
/**
* 入口车道号(国标)
*/
private String entolllaneid;
/**
* 入口时间
*/
private String entime;
/**
* 车道类型1-ETC车道2-MTC车道3-混合车道
*/
private Short lanetype;
/**
* 出口站号
*/
private String extollstation;
/**
* 出口收费站名称
*/
private String extollstationname;
/**
* 出口车道号
*/
private String extolllane;
/**
* 出口站HEX编码
*/
private String extollstationhex;
/**
* 出口车道HEX编码
*/
private String extolllanehex;
/**
* 出口站号(国标)
*/
private String extollstationid;
/**
* 出口车道号(国标)
*/
private String extolllaneid;
/**
* 通行介质类型1-OBU2-CPC卡3-纸券4-M19-无通行介质
*/
private Short mediatype;
/**
* 过车数量
*/
private Integer vcount;
/**
* 入口车牌号经过MD5加密
*/
@TableField("envlp_MD5")
private String envlpMD5;
/**
* 入口车牌颜色
*/
private Short envlpc;
/**
* 出口车牌号经过MD5加密
*/
@TableField("exvlp_MD5")
private String exvlpMD5;
/**
* 出口车牌颜色
*/
private Short exvlpc;
/**
* 入口车型
*/
private Integer envehicletype;
/**
* 出口车型
*/
private Integer exvehicletype;
/**
* 入口车种
*/
private Short envehicleclass;
/**
* 出口车种
*/
private Short exvehicleclass;
/**
* 出口重量
*/
private Integer exweight;
/**
* 轴组信息
*/
private String axisinfo;
/**
* 限载总重(KG)
*/
private Integer limitweight;
/**
* 超限率
*/
private Short overweightrate;
/**
* 轴数
*/
private Integer axlecount;
/**
* 交易类型
*/
private String transtype;
/**
* 支付类型1-现金2-其他第三方账户支付3-银联卡支付4-ETC 用户卡6-支付宝7-微信
*/
private Short paytype;
}

44
zc-business/src/main/java/com/zc/business/domain/OdsTollViuData.java

@ -0,0 +1,44 @@
package com.zc.business.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
* 门架牌识流水数据实体类
* @author xiepufeng
*/
@Data
public class OdsTollViuData implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 抓拍时间
*/
private Date pictime;
/**
* 门架编号
*/
private String gantryid;
/**
* 车牌识别流水号主键
*/
private String picid;
/**
* 拍摄位置1表示车头0表示车尾
*/
private Integer shootposition;
/**
* 车牌号经过MD5加密
*/
@TableField("vehicleplate_MD5")
private String vehicleplateMD5;
}

26
zc-business/src/main/java/com/zc/business/enums/FacilityTypeEnum.java

@ -0,0 +1,26 @@
package com.zc.business.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 设置类型枚举类
* @author xiepufeng
*/
@Getter
@AllArgsConstructor
public enum FacilityTypeEnum {
TOLL_STATION(1, "收费站"),
BRIDGE(2, "桥梁"),
INTERCHANGE(3, "互通立交"),
JUNCTION(4, "枢纽立交"),
TUNNEL(5, "隧道"),
SERVICE_AREA(6, "服务区"),
PARKING_AREA(7, "停车区"),
RECOVERY_POST(8, "清障驻点"),
SLOPE(9, "边坡"),
GANTRY(10, "门架");
private final int code;
private final String description;
}

61
zc-business/src/main/java/com/zc/business/enums/VehicleTypeEnum.java

@ -0,0 +1,61 @@
package com.zc.business.enums;
import lombok.Getter;
/**
* 车辆类型枚举用于定义不同类型的车辆及其相关信息
* @author xiepufeng
*/
@Getter
public enum VehicleTypeEnum {
// 客车类型
TYPE_1_PASSENGER_CAR(1, "1型客车"),
TYPE_2_PASSENGER_CAR(2, "2型客车"),
TYPE_3_PASSENGER_CAR(3, "3型客车"),
TYPE_4_PASSENGER_CAR(4, "4型客车"),
// 货车类型
TYPE_1_TRUCK(11, "1型货车"),
TYPE_2_TRUCK(12, "2型货车"),
TYPE_3_TRUCK(13, "3型货车"),
TYPE_4_TRUCK(14, "4型货车"),
TYPE_5_TRUCK(15,"5型货车"),
TYPE_6_TRUCK(16, "6型货车"),
// 专项作业车类型
TYPE_1_SPECIAL_PURPOSE_VEHICLE(21, "1型专项作业车"),
TYPE_2_SPECIAL_PURPOSE_VEHICLE(22, "2型专项作业车"),
TYPE_3_SPECIAL_PURPOSE_VEHICLE(23, "3型专项作业车"),
TYPE_4_SPECIAL_PURPOSE_VEHICLE(24, "4型专项作业车"),
TYPE_5_SPECIAL_PURPOSE_VEHICLE(25, "5型专项作业车"),
TYPE_6_SPECIAL_PURPOSE_VEHICLE(26,"6型专项作业车");
// 枚举类型的成员变量
private final Integer value; // 类型代号
private final String description; // 类型描述
/**
* 构造函数用于初始化车辆类型枚举
* @param value 车辆类型的代号
* @param description 车辆类型的描述信息
*/
VehicleTypeEnum(Integer value, String description) {
this.value = value;
this.description = description;
}
/**
* 根据描述查找枚举值
* @param value 车辆类型的代号
* @return 对应的车辆类型枚举
* @throws IllegalArgumentException 如果传入的代号无效则抛出异常
*/
public static VehicleTypeEnum fromDescription(Integer value) {
for (VehicleTypeEnum type : VehicleTypeEnum.values()) {
if (type.getValue().equals(value)) {
return type;
}
}
throw new IllegalArgumentException("无效的车辆类型: " + value);
}
}

33
zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java

@ -0,0 +1,33 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.DcGantryStatisticsData;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
/**
* 这是一个接口的注释用于描述门架数据统计的Mapper
* 它继承了BaseMapper接口并且指定DcGantryStatisticsData作为映射实体
*
* @author xiepufeng 这是作者标注用于指明代码的编写者
*/
@Mapper
public interface DcGantryStatisticsDataMapper extends BaseMapper<DcGantryStatisticsData> {
/**
* 插入或更新门架数据统计
*
* @param gantryStatisticsData 门架数据统计对象包含需要插入或更新的数据
* @return 返回一个布尔值表示操作是否成功true表示插入或更新成功false表示失败
*/
boolean insertOrUpdate(DcGantryStatisticsData gantryStatisticsData);
/**
* 获取最大的统计日期
*
* @return 返回最大的统计日期
*/
Date getMaxStatisticalDate();
}

33
zc-business/src/main/java/com/zc/business/mapper/DcTollStationStatisticsDataMapper.java

@ -0,0 +1,33 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.DcTollStationStatisticsData;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
/**
* 这是一个接口的注释用于描述收费站数据统计的Mapper
* 它继承了BaseMapper接口并且指定DcTollStationStatisticsData作为映射实体
*
* @author xiepufeng 这是作者标注用于指明代码的编写者
*/
@Mapper
public interface DcTollStationStatisticsDataMapper extends BaseMapper<DcTollStationStatisticsData> {
/**
* 插入或更新收费站数据统计
*
* @param tollStatisticsData 收费站数据统计对象包含需要插入或更新的数据
* @return 返回一个布尔值表示操作是否成功true表示插入或更新成功false表示失败
*/
boolean insertOrUpdate(DcTollStationStatisticsData tollStatisticsData);
/**
* 获取最大的统计日期
*
* @return 返回最大的统计日期
*/
Date getMaxStatisticalDate();
}

13
zc-business/src/main/java/com/zc/business/mapper/OdsTollEnpassDataMapper.java

@ -0,0 +1,13 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.OdsTollEnpassData;
import org.apache.ibatis.annotations.Mapper;
/**
* 收费站入口流水数据Mapper接口
*
* @author xiepufeng
*/
@Mapper
public interface OdsTollEnpassDataMapper extends BaseMapper<OdsTollEnpassData> {
}

14
zc-business/src/main/java/com/zc/business/mapper/OdsTollEtctuDataMapper.java

@ -0,0 +1,14 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.OdsTollEtctuData;
import org.apache.ibatis.annotations.Mapper;
/**
* 门架流水数据Mapper接口
*
* @author xiepufeng
*/
@Mapper
public interface OdsTollEtctuDataMapper extends BaseMapper<OdsTollEtctuData> {
}

15
zc-business/src/main/java/com/zc/business/mapper/OdsTollExpassDataMapper.java

@ -0,0 +1,15 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.OdsTollEnpassData;
import com.zc.business.domain.OdsTollExpassData;
import org.apache.ibatis.annotations.Mapper;
/**
* 收费站出口流水数据Mapper接口
*
* @author xiepufeng
*/
@Mapper
public interface OdsTollExpassDataMapper extends BaseMapper<OdsTollExpassData> {
}

14
zc-business/src/main/java/com/zc/business/mapper/OdsTollViuDataMapper.java

@ -0,0 +1,14 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.OdsTollViuData;
import org.apache.ibatis.annotations.Mapper;
/**
* 门架牌识流水数据Mapper接口
*
* @author xiepufeng
*/
@Mapper
public interface OdsTollViuDataMapper extends BaseMapper<OdsTollViuData> {
}

2
zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java

@ -57,7 +57,7 @@ public class DeviceMessageHandler {
private RedisCache redisCache;
@Resource
private IDcTrafficStatisticsService dcTrafficSectionDataService;
private IDcTrafficSectionStatisticsService dcTrafficSectionDataService;
@Autowired
private IDcMeteorologicalDetectorDataService meteorologicalDetectorDataService;

28
zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java

@ -44,24 +44,11 @@ public interface DcEmergencyPlansService {
*/
void dispositionDeviceContent(List<DcEmergencyPlans> list, DcEvent dcEvent);
/**
* 感知事件-根据事件类型查询事件预案
*
* @param dcWarning 感知事件
* @return 结果
*/
List<DcEmergencyPlans> selectDcEmergencyPlansByWarningType(DcWarning dcWarning);
/**
* 交通事件-情报板确认回显原始模板
*/
Map<String,List<DcInfoBoardTemplate>> eventBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans);
/**
* 感知事件-情报板确认回显原始模板
*/
Map<String,List<DcInfoBoardTemplate>> warningBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans);
/**
* 交通事件确定
*
@ -86,21 +73,6 @@ public interface DcEmergencyPlansService {
*/
EventPlanAssoc selectEventPlanAssocById(Long id);
/**
* 感知事件确定
*
* @param dcEventAnDcEmergencyPlans 事件数据 事件预案数据
* @return 结果
*/
JSONObject executionWarningConfirmation(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans);
/**
* 感知事件-情报板自动生成
* @param dcEventAnDcEmergencyPlans
* @return
*/
DcInfoBoardTemplate warningAutomaticGeneration(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans);
/**
* 交通事件-情报板自动生成
* @param dcEventAnDcEmergencyPlans

37
zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java

@ -0,0 +1,37 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.domain.DcStatisticsData;
import com.zc.business.domain.DcTollStationStatisticsData;
import java.util.List;
/**
* 门架数据统计接口该接口扩展了IService接口用于对DcGantryStatisticsData类型的实体进行数据库操作
* 主要用于门架数据的增删改查等统计工作
*
* @author xiepufeng 提供者
*/
public interface IDcGantryStatisticsDataService extends IService<DcGantryStatisticsData> {
/**
* 获取过去一小时的DcGantryStatisticsData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的DcGantryStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryStatisticsData> - 过去一小时的DcGantryStatisticsData数据列表
*/
List<DcGantryStatisticsData> lastHourData();
/**
* 进行门架数据统计的函数
* 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数用来包含门架统计所需的各项数据
* 然后基于这些数据进行统计处理并返回一个包含统计结果的DcGantryStatisticsData对象列表
*
* @param dcGantryStatisticsData 包含门架统计所需数据的对象
* @return 返回一个DcGantryStatisticsData对象的列表包含统计后的结果
*/
List<DcGantryStatisticsData> gantryData(DcGantryStatisticsData dcGantryStatisticsData);
}

36
zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java

@ -0,0 +1,36 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcStatisticsData;
import com.zc.business.domain.DcTollStationStatisticsData;
import java.util.List;
/**
* 收费站数据统计接口该接口扩展了IService接口用于对DcTollStationStatisticsData类型的实体进行数据库操作
* 主要用于收费站数据的增删改查等统计工作
*
* @author xiepufeng 提供者
*/
public interface IDcTollStationStatisticsDataService extends IService<DcTollStationStatisticsData> {
/**
* 获取过去一小时的DcTollStationStatisticsData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的DcTollStationStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcTollStationStatisticsData> - 过去一小时的DcTollStationStatisticsData数据列表
*/
List<DcTollStationStatisticsData> lastHourData();
/**
* 收费站数据统计函数
*
* 通过对传入的统计数据进行处理计算并返回收费站数据的统计结果列表
*
* @param request 包含统计所需参数
* @return 返回一个包收费站站数据统计结果的列表
*/
List<DcTollStationStatisticsData> tollStationData(DcTollStationStatisticsData request);
}

66
zc-business/src/main/java/com/zc/business/service/IDcTrafficSectionStatisticsService.java

@ -0,0 +1,66 @@
package com.zc.business.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcCongestedSectionData;
import com.zc.business.domain.DcTrafficMetricsData;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.request.DcTrafficMetricsDataRequest;
import com.zc.business.request.DcTrafficSectionDataRequest;
import java.util.List;
/**
* 交通统计服务接口提供处理交通数据的相关方法
* @author xiepufeng
*/
public interface IDcTrafficSectionStatisticsService extends IService<DcTrafficSectionData> {
/**
* 处理实时接收到的一类交流站设备消息并将其转换为交通断面统计数据对象并缓存
*
* @param msg 设备发送的JSON格式实时消息
*/
void processRealtimeOneStopMessage(JSONObject msg);
/**
* 根据提供的请求参数获取当前的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含当前交通截面数据列表
*/
List<DcTrafficSectionData> currentSections(DcTrafficSectionDataRequest request);
/**
* 根据提供的请求参数获取历史的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含历史交通截面数据列表
*/
List<DcTrafficSectionData> historySections(DcTrafficSectionDataRequest request);
/**
* 根据提供的请求参数获取当前的交通指标数据
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return DcTrafficMetricsData 返回一个包含当前交通指标数据列表
*/
List<DcTrafficMetricsData> currentTrafficMetrics(DcTrafficMetricsDataRequest request);
/**
* 获取历史交通指标数据列表
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return 返回符合查询条件的历史交通指标数据列表
*/
List<DcTrafficMetricsData> historyTrafficMetrics(DcTrafficMetricsDataRequest request);
/**
* 获取当前拥堵路段列表
*
* @param direction 交通方向指定查询哪个方向的拥堵路段具体方向的定义根据实际业务而定
* @return 返回当前拥堵路段列表
*/
List<DcCongestedSectionData> currentCongestedSection(Byte direction);
}

62
zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java

@ -1,66 +1,16 @@
package com.zc.business.service;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcCongestedSectionData;
import com.zc.business.domain.DcTrafficMetricsData;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.request.DcTrafficMetricsDataRequest;
import com.zc.business.request.DcTrafficSectionDataRequest;
import com.zc.business.domain.DcStatisticsData;
import java.util.List;
/**
* 交通统计服务接口提供处理交通数据的相关方法
* @author xiepufeng
*/
public interface IDcTrafficStatisticsService extends IService<DcTrafficSectionData> {
public interface IDcTrafficStatisticsService {
/**
* 处理实时接收到的一类交流站设备消息并将其转换为交通断面统计数据对象并缓存
* 根据传入的统计请求数据查询历史累计车流量数据
*
* @param msg 设备发送的JSON格式实时消息
* @param request 包含统计查询条件的DcStatisticsData对象用于指定查询的历史流量数据的细节如时间范围等
* @return 返回一个DcStatisticsData列表
*/
void processRealtimeOneStopMessage(JSONObject msg);
/**
* 根据提供的请求参数获取当前的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含当前交通截面数据列表
*/
List<DcTrafficSectionData> currentSections(DcTrafficSectionDataRequest request);
/**
* 根据提供的请求参数获取历史的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含历史交通截面数据列表
*/
List<DcTrafficSectionData> historySections(DcTrafficSectionDataRequest request);
/**
* 根据提供的请求参数获取当前的交通指标数据
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return DcTrafficMetricsData 返回一个包含当前交通指标数据列表
*/
List<DcTrafficMetricsData> currentTrafficMetrics(DcTrafficMetricsDataRequest request);
/**
* 获取历史交通指标数据列表
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return 返回符合查询条件的历史交通指标数据列表
*/
List<DcTrafficMetricsData> historyTrafficMetrics(DcTrafficMetricsDataRequest request);
/**
* 获取当前拥堵路段列表
*
* @param direction 交通方向指定查询哪个方向的拥堵路段具体方向的定义根据实际业务而定
* @return 返回当前拥堵路段列表
*/
List<DcCongestedSectionData> currentCongestedSection(Byte direction);
List<DcStatisticsData> historyFlow(DcStatisticsData request);
}

48
zc-business/src/main/java/com/zc/business/service/IOdsTollEnpassDataService.java

@ -0,0 +1,48 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.domain.OdsTollEnpassData;
import java.util.List;
/**
* 入口流水数据服务接口
* 提供对入口流水数据的增删改查等操作
*
* @author xiepufeng
* @extends IService<OdsTollEnpassData> 继承自IService接口指定操作的数据类型为OdsTollEnpassData
*/
public interface IOdsTollEnpassDataService extends IService<OdsTollEnpassData> {
/**
* 获取过去一小时的OdsTollEnpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的OdsTollEnpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEnpassData> - 过去一小时的OdsTollEnpassData数据列表
*/
List<OdsTollEnpassData> lastHourData();
/**
* 获取当月的OdsTollEnpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的OdsTollEnpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEnpassData> - 当月的OdsTollEnpassData数据列表
*/
List<OdsTollEnpassData> currentMonthData();
/**
* 计算收费站在指定时间段内的统计数据
*
* @param list 包含收费通行数据的列表不应为null或空
* @return 返回一个包含各收费站统计数据的列表如果输入列表为空则返回空列表
*/
List<DcTollStationStatisticsData> calculateTollStationStatistics(List<OdsTollEnpassData> list);
}

40
zc-business/src/main/java/com/zc/business/service/IOdsTollEtctuDataService.java

@ -0,0 +1,40 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.domain.OdsTollEtctuData;
import java.util.List;
/**
* 门架流水数据
* @author xiepufeng
*/
public interface IOdsTollEtctuDataService extends IService<OdsTollEtctuData> {
/**
* 获取最后一小时的门架流水数据列表
*
* @return List<OdsTollEtctuData> - 返回最后一小时的门架流水数据列表如果没有数据则返回空列表
*/
List<OdsTollEtctuData> lastHourData();
/**
* 获取当月的门架流水数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的门架流水数据对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEtctuData> - 当月的门架流水数据列表
*/
List<OdsTollEtctuData> currentMonthData();
/**
* 计算门架在指定时间段内的统计数据
*
* @param odsTollEtctuDataList 包含门架数据的列表不应为null或空
* @return 返回一个包含各门架统计数据的列表如果输入列表为空则返回空列表
*/
List<DcGantryStatisticsData> calculateGantryStatistics(List<OdsTollEtctuData> odsTollEtctuDataList);
}

45
zc-business/src/main/java/com/zc/business/service/IOdsTollExpassDataService.java

@ -0,0 +1,45 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.domain.OdsTollExpassData;
import com.zc.business.domain.OdsTollExpassData;
import java.util.List;
/**
* 流水数据操作接口用于对出口流水数据进行操作
* 该接口继承自IService接口针对OdsTollExpassData类型的数据提供服务
*
* @author xiepufeng
*/
public interface IOdsTollExpassDataService extends IService<OdsTollExpassData> {
/**
* 获取过去一小时的OdsTollExpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的OdsTollExpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollExpassData> - 过去一小时的OdsTollExpassData数据列表
*/
List<OdsTollExpassData> lastHourData();
/**
* 获取当月的OdsTollExpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的OdsTollExpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollExpassData> - 当月的OdsTollExpassData数据列表
*/
List<OdsTollExpassData> currentMonthData();
/**
* 计算收费站在指定时间段内的统计数据
*
* @param list 包含收费通行数据的列表不应为null或空
* @return 返回一个包含各收费站统计数据的列表如果输入列表为空则返回空列表
*/
List<DcTollStationStatisticsData> calculateTollStationStatistics(List<OdsTollExpassData> list);
}

11
zc-business/src/main/java/com/zc/business/service/IOdsTollViuDataService.java

@ -0,0 +1,11 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.OdsTollViuData;
/**
* 门架牌识流水数据
* @author xiepufeng
*/
public interface IOdsTollViuDataService extends IService<OdsTollViuData> {
}

138
zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java

@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.io.IOException;
@ -170,12 +171,12 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
dcExecuteActions.forEach(dcExecuteAction -> {
JSONObject executeConfig = JSON.parseObject(dcExecuteAction.getExecuteConfig());
if (dcExecuteAction.getDeviceType() == DeviceTypeEnum.ROAD_SECTION_VOICE_BROADCASTING.getCode()
&& executeConfig.get("operationType").equals("2")) {
&& executeConfig.getString("operationType").equals("2")) {
// 执行操作中智能发布语音广播
String content = intelligentPublishingOfInformation(dcEvent);
updateIntelligentPublishingContent(dcExecuteAction,markArray,dcEvent,content,dcEvent.getDirection());
} else if (dcExecuteAction.getDeviceType() == DeviceTypeEnum.VARIABLE_INFORMATION_FLAG.getCode()
&& executeConfig.get("operationType").equals("2")) {
&& executeConfig.getString("operationType").equals("2")) {
// 执行操作中智能发布情报板
String content = intelligentPublishingOfInformation(dcEvent);
updateIntelligentPublishingContent(dcExecuteAction,markArray,dcEvent,content,dcEvent.getDirection());
@ -255,37 +256,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
dcExecuteAction.setExecuteConfig(executeConfig.toJSONString());
}
/**
* 感知事件 - 根据事件类型查询事件预案
*
* @param dcWarning 感知事件
* @return 结果
*/
@Override
public List<DcEmergencyPlans> selectDcEmergencyPlansByWarningType(DcWarning dcWarning) {
int eventType = ValueConverter.convertValueHost(dcWarning.getWarningType());
List<DcEmergencyPlans> dcEmergencyPlansList = dcEmergencyPlansMapper.selectDcEmergencyPlansByWarningType(eventType);
int warningType = Integer.parseInt(dcWarning.getWarningType().toString());
if (warningType == WarningTypeEnum.UNUSUAL_WEATHER.getCode()) {
return dcEmergencyPlansList.stream()
.filter(dcEmergencyPlans -> {
String triggerMechanism = dcEmergencyPlans.getTriggerMechanism();
JSONObject triggerJson = JSONObject.parseObject(triggerMechanism);
String eventSubclass = triggerJson.get("eventSubclass").toString();
// 事件--异常天气数据
String warningSubclass = ValueConverter.convertValueSon(dcWarning.getWarningSubclass());
return eventSubclass.equals(warningSubclass);
})
.collect(Collectors.toList());
} else {
return dcEmergencyPlansList;
}
}
/**
* 交通事件-情报板确认回显原始模板
*/
@ -307,27 +277,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
return getBoardTemplate(dcDevices);
}
/**
* 感知事件-情报板确认回显原始模板
*/
@Override
public Map<String, List<DcInfoBoardTemplate>> warningBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
// 获取事件数据
DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning();
// 方向
String direction = dcWarning.getDirection();
// 事件桩号
dcWarning.setStakeMark(dcWarning.getStakeMark().replace("K", ""));
String[] markArray = dcWarning.getStakeMark().split("\\+");
if (markArray[1].length() < 3) {
// 不足三位 补零
markArray[1] = String.format("%0" + 3 + "d", markArray[1]);
}
DcExecuteAction executeAction = dcEventAnDcEmergencyPlans.getDcEmergencyPlans().getExecuteAction();
List<DcDevice> dcDevices = ruleFiltering(executeAction, markArray, direction);
return getBoardTemplate(dcDevices);
}
/**
* 情报板设备执行3A功能,获取模板
*/
@ -575,36 +524,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
return eventPlanAssocMapper.selectById(eventPlanAssoc);
}
/**
* 感知事件-情报板自动生成
*
* @param dcEventAnDcEmergencyPlans
* @return
*/
@Override
public DcInfoBoardTemplate warningAutomaticGeneration(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
// 根据感知事件类型等、生成情报板的内容
DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning();
DcInfoBoardTemplate dcInfoBoardTemplate = dcEventAnDcEmergencyPlans.getDcInfoBoardTemplate();
Integer warningType = dcWarning.getWarningType();
if (warningType.equals(WarningTypeEnum.TRAFFIC_JAM.getCode())) {
// 交通拥堵
dcInfoBoardTemplate.setContent("前方" + WarningTypeEnum.TRAFFIC_JAM.getInfo() + "请谨慎驾驶");
} else if (warningType.equals(WarningTypeEnum.NON_MOTOR_VEHICLE.getCode())) {
dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.NON_MOTOR_VEHICLE.getInfo() + "请注意避让");
} else if (warningType.equals(WarningTypeEnum.PEDESTRIAN.getCode())) {
dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.PEDESTRIAN.getInfo() + "请注意避让");
} else if (warningType.equals(WarningTypeEnum.FIREWORKS.getCode())) {
dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.FIREWORKS.getInfo() + "请注意避让");
} else if (warningType.equals(WarningTypeEnum.OUTFALL.getCode())) {
dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.OUTFALL.getInfo() + "请注意避让");
} else if (warningType.equals(WarningTypeEnum.VEHICLE_CONVERSE_RUNNING.getCode())) {
dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.OUTFALL.getInfo() + "请注意避让");
}
return dcInfoBoardTemplate;
}
/**
* 交通事件-情报板自动生成
*
@ -669,24 +588,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
return content;
}
/**
* 感知事件确认
*
* @param dcEventAnDcEmergencyPlans 事件数据 事件预案数据
* @return
*/
@Override
public JSONObject executionWarningConfirmation(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) {
// 获取事件数据
DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning();
// 方向
String direction = dcWarning.getDirection();
// 事件编号
String id = dcWarning.getId();
return executionConfirmation(dcEventAnDcEmergencyPlans, dcWarning.getStakeMark(), direction, id);
}
/**
* 事件确认
*
@ -718,6 +619,30 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
dcEmergencyPlans.getDcExecuteAction()
.forEach(dcExecuteAction -> {
List<DcDevice> dcDevices = ruleFiltering(dcExecuteAction, markArray, direction);
// // 此代码 是为了防止事件没有匹配到预案,但是还执行了设备控制,并且执行操作中带有智能发布
// boolean isIdEmpty = ObjectUtils.isEmpty(dcEmergencyPlans.getId());
// Set<Integer> targetDeviceTypes = new HashSet<>(Arrays.asList(
// DeviceTypeEnum.ROAD_SECTION_VOICE_BROADCASTING.getCode(),
// DeviceTypeEnum.VARIABLE_INFORMATION_FLAG.getCode()
// ));
// boolean isTargetDeviceType = targetDeviceTypes.contains(dcExecuteAction.getDeviceType());
// if (isIdEmpty && isTargetDeviceType) {
// String configJson = operationType.equals(1) ? dcExecuteAction.getExecuteConfig() : dcExecuteAction.getRecoverConfig();
// JSONObject config = JSON.parseObject(configJson);
// if (config.getString("operationType").equals("2")) {
// DcEvent dcEvent = dcEventAnDcEmergencyPlans.getDcEvent();
// String content = intelligentPublishingOfInformation(dcEvent);
// updateIntelligentPublishingContent(
// dcExecuteAction,
// markArray,
// dcEvent,
// content,
// dcEvent.getDirection()
// );
// }
// }
JSONObject otherConfig = operationType.equals(1)?
JSON.parseObject(dcExecuteAction.getExecuteConfig()): JSON.parseObject(dcExecuteAction.getRecoverConfig());
try {
@ -753,6 +678,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
eventPlanAssoc.setControlDevice(deviceIds.toString().replaceFirst(";", ""));
eventPlanAssoc.setControlResult(resultArray.toJSONString());
eventPlanAssoc.setCreateTime(DateUtils.getNowDate());
eventPlanAssoc.setControl(JSON.toJSONString(dcEmergencyPlans.getDcExecuteAction()));
eventPlanAssocMapper.insertEventPlanAssoc(eventPlanAssoc);
resultObject.put("eventPlanAssocId",eventPlanAssoc.getId());
@ -819,7 +745,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
else if (device.getDeviceType().equals(DeviceTypeConstants.VARIABLE_INFORMATION_FLAG.toString())) {
if (operationType == 1) {
// 情报板发布全流程
if (otherConfig.get("operationType").equals("2")) {
if (otherConfig.getString("operationType").equals("2")) {
JSONArray contentList = JSON.parseArray(otherConfig.get("contentList").toString());
JSONObject foundContent = contentList.stream()
.map(content -> JSON.parseObject(content.toString()))
@ -843,7 +769,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
} else {
// 恢复操作
if (otherConfig.get("operationType").equals("2")) {
if (otherConfig.getString("operationType").equals("2")) {
// 还原上次
props.put("fileId", "10");
functionId = DeviceFunctionIdConstants.VARIABLE_INFORMATION_FLAG_1B;
@ -869,7 +795,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
params.put("name", "task-event");
params.put("outVVol", "8");
params.put("priority", "1");
if (otherConfig.get("operationType").equals("2")) {
if (otherConfig.getString("operationType").equals("2")) {
// 智能发布
JSONArray contentList = JSON.parseArray(otherConfig.get("contentList").toString());
JSONObject foundContent = contentList.stream()
@ -1169,7 +1095,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService {
}else if (dcExecuteAction.getDeviceType() == DeviceTypeConstants.VARIABLE_INFORMATION_FLAG ||
dcExecuteAction.getDeviceType() == DeviceTypeConstants.ROAD_SECTION_VOICE_BROADCASTING) {
// 情报板/语音广播
if (jsonObject.get("operationType").equals("1")) {
if (jsonObject.getString("operationType").equals("1")) {
config.put("content",jsonObject.get("content"));
}else {
config.put("operationType","智能发布");

170
zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java

@ -0,0 +1,170 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.domain.OdsTollEtctuData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcGantryStatisticsDataMapper;
import com.zc.business.service.*;
import com.zc.business.statistics.cache.gantry.DailyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.MonthlyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.QuarterlyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.YearlyTrafficGantryStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
/**
* 门架数据统计服务实现类
* 该类扩展了ServiceImpl用于处理DcGantryStatisticsData表的数据库操作
* 实现了IDcGantryStatisticsDataService接口提门架数据的统计方法
*/
@Service
public class DcGantryStatisticsDataImpl extends ServiceImpl<DcGantryStatisticsDataMapper, DcGantryStatisticsData>
implements IDcGantryStatisticsDataService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
// 门架流水数据服务
@Resource
private IOdsTollEtctuDataService odsTollEtctuDataService;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通门架缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通门架缓存
recoveryQuarterlyCache(); // 恢复每季度交通门架缓存
recoveryYearlyCache(); // 恢复每年交通门架缓存
}
/**
* 获取最后一小时的门架统计数据
* 该方法首先从ODS数据操作服务获取过去一小时的门架数据
* 然后基于这些数据计算并生成门架的统计信息列表
*
* @return List<DcGantryStatisticsData> 包含门架统计信息的列表
*/
@Override
public List<DcGantryStatisticsData> lastHourData() {
// 获取门架入口过去一小时的通过数据
List<OdsTollEtctuData> odsTollEnpassDataList = odsTollEtctuDataService.lastHourData();
return odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList);
}
/**
* 进行门架数据统计的函数
* 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数用来包含门架统计所需的各项数据
* 然后基于这些数据进行统计处理并返回一个包含统计结果的DcGantryStatisticsData对象列表
*
* @param request 包含门架统计所需数据的对象
* @return 返回一个DcGantryStatisticsData对象的列表包含统计后的结果
*/
@Override
public List<DcGantryStatisticsData> gantryData(DcGantryStatisticsData request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
if (request.getGantryCode() == null) {
throw new ServiceException("门架标识不能为空");
}
LambdaQueryWrapper<DcGantryStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
// 如果请求中包含唯一标识符,则根据唯一标识符进行过滤
if (request.getId() != null) {
queryWrapper.eq(DcGantryStatisticsData::getId, request.getId());
}
// 如果请求中包含统计日期,则根据统计日期进行过滤
if (request.getStatisticalDate() != null) {
queryWrapper.eq(DcGantryStatisticsData::getStatisticalDate, request.getStatisticalDate());
}
queryWrapper.eq(DcGantryStatisticsData::getPeriodType, request.getPeriodType());
queryWrapper.eq(DcGantryStatisticsData::getGantryCode, request.getGantryCode());
queryWrapper.between(DcGantryStatisticsData::getCreateTime, request.getStartTime(), request.getEndTime());
return list(queryWrapper);
}
/**
* 恢复日缓存数据的方法
* 该方法首先会获取当前月份的门架入口数据
* 然后分别计算每个门架的统计信息并将这些统计信息添加到每日交通门架统计缓存中
*/
private void recoveryDailyCache() {
// 获取当月门架数据
List<OdsTollEtctuData> odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData();
// 计算每个门架数据的统计信息,并添加到每日门架统计缓存中
odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList).forEach(DailyTrafficGantryStatisticsCache::addCacheData);
}
/**
* 恢复每月交通门架缓存的方法
* 通过查询当前月份至今的每日交通数据并将其添加到每月门架统计缓存中
*/
private void recoveryMonthlyCache() {
// 构建查询条件,查询当前月份至今的每日交通数据
LambdaQueryWrapper<DcGantryStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.DAY);
queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date());
List<DcGantryStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每日数据添加到每月门架统计缓存中
dcTrafficSectionDataList.forEach(MonthlyTrafficGantryStatisticsCache::addCacheData);
}
/**
* 恢复每季度交通门架缓存的方法
* 通过查询当前季度至今的每月交通数据并将其添加到每季度门架统计缓存中
*/
private void recoveryQuarterlyCache() {
// 构建查询条件,查询当前季度至今的每月交通数据
LambdaQueryWrapper<DcGantryStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH);
queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date());
List<DcGantryStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每月数据添加到每季度门架统计缓存
dcTrafficSectionDataList.forEach(QuarterlyTrafficGantryStatisticsCache::addCacheData);
}
/**
* 恢复每年交通门架缓存的方法
* 通过查询当前年份至今的每季度交通数据并将其添加到每年门架统计缓存中
*/
private void recoveryYearlyCache() {
// 构建查询条件,查询当前年份至今的每季度交通数据
LambdaQueryWrapper<DcGantryStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER);
queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date());
List<DcGantryStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每季度数据添加到每年门架统计缓存
dcTrafficSectionDataList.forEach(YearlyTrafficGantryStatisticsCache::addCacheData);
}
}

200
zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java

@ -0,0 +1,200 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.exception.ServiceException;
import com.zc.business.domain.*;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcTollStationStatisticsDataMapper;
import com.zc.business.service.IDcTollStationStatisticsDataService;
import com.zc.business.service.IOdsTollEnpassDataService;
import com.zc.business.service.IOdsTollExpassDataService;
import com.zc.business.statistics.cache.tollstation.DailyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.MonthlyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.QuarterlyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.YearlyTrafficTollStationStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 收费站数据统计服务实现类
* 该类扩展了ServiceImpl用于处理DcTollStationStatisticsData表的数据库操作
* 实现了IDcTollStationStatisticsDataService接口提供收费站数据的统计方法
*/
@Service
public class DcTollStationStatisticsDataImpl extends ServiceImpl<DcTollStationStatisticsDataMapper, DcTollStationStatisticsData>
implements IDcTollStationStatisticsDataService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
// 收费站入口流水数据
@Resource
private IOdsTollEnpassDataService odsTollEnpassDataService;
// 出口流水数据
@Resource
private IOdsTollExpassDataService odsTollExpassDataService;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通收费站站点缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通收费站站点缓存
recoveryQuarterlyCache(); // 恢复每季度交通收费站站点缓存
recoveryYearlyCache(); // 恢复每年交通收费站站点缓存
}
/**
* 获取最后一小时的收费站点统计数据
* 该方法首先从ODS数据操作服务获取过去一小时的通过Enpass和出口Expass数据
* 然后基于这些数据计算并生成收费站点的统计信息列表
*
* @return List<DcTollStationStatisticsData> 包含收费站点统计信息的列表
*/
@Override
public List<DcTollStationStatisticsData> lastHourData() {
// 获取站点入口过去一小时的通过数据
List<OdsTollEnpassData> odsTollEnpassDataList = odsTollEnpassDataService.lastHourData();
// 获取站点出口过去一小时的通过数据
List<OdsTollExpassData> odsTollExpassDataList = odsTollExpassDataService.lastHourData();
// 初始化用于存放最终统计结果的列表
List<DcTollStationStatisticsData> dcTollStationStatisticsDataList = new ArrayList<>();
// 根据入口和出口数据计算并添加收费站点的统计信息到结果列表中
dcTollStationStatisticsDataList.addAll(odsTollEnpassDataService.calculateTollStationStatistics(odsTollEnpassDataList));
dcTollStationStatisticsDataList.addAll(odsTollExpassDataService.calculateTollStationStatistics(odsTollExpassDataList));
return dcTollStationStatisticsDataList;
}
/**
* 收费站数据统计函数
* <p>
* 通过对传入的统计数据进行处理计算并返回收费站数据的统计结果列表
*
* @param request 包含统计所需参数
* @return 返回一个包收费站站数据统计结果的列表
*/
@Override
public List<DcTollStationStatisticsData> tollStationData(DcTollStationStatisticsData request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
if (request.getTollStationCode() == null) {
throw new ServiceException("收费站站点编号不能为空");
}
LambdaQueryWrapper<DcTollStationStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
// 如果请求中包含出入类型,则根据出入类型进行过滤
if (request.getAccessType() != null) {
queryWrapper.eq(DcTollStationStatisticsData::getAccessType, request.getAccessType());
}
// 如果请求中包含唯一标识符,则根据唯一标识符进行过滤
if (request.getId() != null) {
queryWrapper.eq(DcTollStationStatisticsData::getId, request.getId());
}
// 如果请求中包含统计日期,则根据统计日期进行过滤
if (request.getStatisticalDate() != null) {
queryWrapper.eq(DcTollStationStatisticsData::getStatisticalDate, request.getStatisticalDate());
}
queryWrapper.eq(DcTollStationStatisticsData::getPeriodType, request.getPeriodType());
queryWrapper.between(DcTollStationStatisticsData::getCreateTime, request.getStartTime(), request.getEndTime());
queryWrapper.eq(DcTollStationStatisticsData::getTollStationCode, request.getTollStationCode());
return list(queryWrapper);
}
/**
* 恢复日缓存数据的方法获取当月收费站站点入口和出口数据
* 该方法首先会获取当前月份的收费站站点入口和出口数据
* 然后分别计算每个收费站的统计信息并将这些统计信息添加到每日交通收费站统计缓存中
*/
private void recoveryDailyCache() {
// 获取当月收费站站点入口数据
List<OdsTollEnpassData> odsTollEnpassDataList = odsTollEnpassDataService.currentMonthData();
// 计算每个收费站入口数据的统计信息,并添加到每日交通收费站统计缓存中
odsTollEnpassDataService.calculateTollStationStatistics(odsTollEnpassDataList).forEach(DailyTrafficTollStationStatisticsCache::addCacheData);
// 获取当月收费站站点出口数据
List<OdsTollExpassData> odsTollExpassDataList = odsTollExpassDataService.currentMonthData();
// 计算每个收费站出口数据的统计信息,并添加到每日交通收费站统计缓存中
odsTollExpassDataService.calculateTollStationStatistics(odsTollExpassDataList).forEach(DailyTrafficTollStationStatisticsCache::addCacheData);
}
/**
* 恢复每月交通收费站站点缓存的方法
* 通过查询当前月份至今的每日交通数据并将其添加到每月交通收费站统计缓存中
*/
private void recoveryMonthlyCache() {
// 构建查询条件,查询当前月份至今的每日交通数据
LambdaQueryWrapper<DcTollStationStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTollStationStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.DAY);
queryWrapper.between(DcTollStationStatisticsData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date());
List<DcTollStationStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每日数据添加到每月交通收费站统计缓存中
dcTrafficSectionDataList.forEach(MonthlyTrafficTollStationStatisticsCache::addCacheData);
}
/**
* 恢复每季度交通收费站站点缓存的方法
* 通过查询当前季度至今的每月交通数据并将其添加到每季度交通收费站统计缓存中
*/
private void recoveryQuarterlyCache() {
// 构建查询条件,查询当前季度至今的每月交通数据
LambdaQueryWrapper<DcTollStationStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTollStationStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH);
queryWrapper.between(DcTollStationStatisticsData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date());
List<DcTollStationStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每月数据添加到每季度交通收费站统计缓存
dcTrafficSectionDataList.forEach(QuarterlyTrafficTollStationStatisticsCache::addCacheData);
}
/**
* 恢复每年交通收费站站点缓存的方法
* 通过查询当前年份至今的每季度交通数据并将其添加到每年交通收费站统计缓存中
*/
private void recoveryYearlyCache() {
// 构建查询条件,查询当前年份至今的每季度交通数据
LambdaQueryWrapper<DcTollStationStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTollStationStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER);
queryWrapper.between(DcTollStationStatisticsData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date());
List<DcTollStationStatisticsData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每季度数据添加到每年交通收费站统计缓存
dcTrafficSectionDataList.forEach(YearlyTrafficTollStationStatisticsCache::addCacheData);
}
}

506
zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java

@ -0,0 +1,506 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.zc.business.constant.DeviceTypeConstants;
import com.zc.business.constant.RedisKeyConstants;
import com.zc.business.controller.DcDeviceController;
import com.zc.business.domain.*;
import com.zc.business.enums.*;
import com.zc.business.request.DcTrafficMetricsDataRequest;
import com.zc.business.request.DcTrafficSectionDataRequest;
import com.zc.business.mapper.DcTrafficSectionDataMapper;
import com.zc.business.service.IDcTrafficSectionStatisticsService;
import com.zc.business.statistics.cache.section.DailyTrafficSectionStatisticsCache;
import com.zc.business.statistics.cache.section.MonthlyTrafficSectionStatisticsCache;
import com.zc.business.statistics.cache.section.QuarterlyTrafficSectionStatisticsCache;
import com.zc.business.statistics.cache.section.YearlyTrafficSectionStatisticsCache;
import com.zc.business.statistics.handler.TrafficSectionAnalysis;
import com.zc.business.statistics.handler.TrafficSectionStatistics;
import com.zc.business.utils.StakeMarkUtils;
import com.zc.common.core.httpclient.exception.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* 交通断面数据服务实现类负责处理实时设备消息缓存数据定时任务以及数据保存等功能
*
* @author xiepufeng
*/
@Service
public class DcTrafficSectionStatisticsServiceImpl
extends ServiceImpl<DcTrafficSectionDataMapper, DcTrafficSectionData>
implements IDcTrafficSectionStatisticsService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private RedisCache redisCache;
@Resource
private DcDeviceController dcDeviceController;
@Resource
private TrafficSectionStatistics trafficSectionStatistics;
@Resource
private TrafficSectionAnalysis trafficSectionAnalysis;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通数据缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从es中恢复当月交通数据缓存
recoveryMonthlyCache(); // 恢复每月交通数据缓存
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
recoveryYearlyCache(); // 恢复每年交通数据缓存
}
/**
* 处理实时接收到的一类交流站设备消息并将其转换为交通断面统计数据对象并缓存
*
* @param msg 设备发送的JSON格式实时消息
*/
@Override
public void processRealtimeOneStopMessage(JSONObject msg) {
// 1. 将设备消息转换为交通断面数据统计定义对象
List<DcTrafficSectionData> dcTrafficSectionDataList = trafficSectionStatistics.convertToTrafficStatistics(msg, DeviceDataCategoryEnum.REAL_TIME);
if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) {
// 2. 添加到缓存中
dcTrafficSectionDataList.forEach(this::addCacheData);
}
}
/**
* 根据提供的请求参数获取当前的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含当前交通截面数据列表
*/
@Override
public List<DcTrafficSectionData> currentSections(DcTrafficSectionDataRequest request) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(request.getDirection());
return filterTrafficDataByRequest(request, dcTrafficSectionDataCaches);
}
/**
* 根据提供的请求参数获取历史的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含历史交通截面数据列表
*/
@Override
public List<DcTrafficSectionData> historySections(DcTrafficSectionDataRequest request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
// 构建查询条件
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, request.getStartTime(), request.getEndTime());
queryWrapper.eq(DcTrafficSectionData::getPeriodType, request.getPeriodType());
if (request.getDirection() != null) {
queryWrapper.eq(DcTrafficSectionData::getDirection, request.getDirection());
}
if (request.getDeviceType() != null) {
queryWrapper.eq(DcTrafficSectionData::getDeviceType, request.getDeviceType());
}
if (request.getDeviceId() != null) {
queryWrapper.eq(DcTrafficSectionData::getDeviceId, request.getDeviceId());
}
return list(queryWrapper);
}
/**
* 根据请求获取当前交通指标数据
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return 返回当前交通指标数据
* @throws ServiceException 如果没有获取到交通数据则抛出异常
*/
@Override
public List<DcTrafficMetricsData> currentTrafficMetrics(DcTrafficMetricsDataRequest request) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(request.getDirection());
// 过滤掉时间错误的交通数据
processStaleTrafficSectionData(dcTrafficSectionDataCaches);
// 根据请求过滤交通数据
List<DcTrafficSectionData> trafficSectionDataList = filterTrafficDataByRequest(request, dcTrafficSectionDataCaches);
// 对获取的交通路段数据进行分析,得到交通指标数据
List<DcTrafficMetricsData> dcTrafficMetricsDataList = trafficSectionAnalysis.calculateTrafficMetrics(request, trafficSectionDataList);
if (dcTrafficMetricsDataList == null || dcTrafficMetricsDataList.isEmpty()) {
// 如果没有获取到数据,则抛出异常
throw new ServiceException("获取当前交通特征指数失败");
}
// 根据收集到的交通段数据计算并返回交通指标数据
return dcTrafficMetricsDataList;
}
/**
* 计算获取指定条件下的历史交通指标数据
*
* @param request 包含查询条件的请求对象包括开始时间结束时间方向周期类型和所属路段ID等信息
* @return 返回一个包含交通指标数据的列表
*/
@Override
public List<DcTrafficMetricsData> historyTrafficMetrics(DcTrafficMetricsDataRequest request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
// 构建查询条件
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, request.getStartTime(), request.getEndTime());
queryWrapper.eq(DcTrafficSectionData::getPeriodType, request.getPeriodType());
if (request.getDirection() != null) {
queryWrapper.eq(DcTrafficSectionData::getDirection, request.getDirection());
}
// 根据请求获取所属路段ID,并进一步筛选路段范围内的数据
Long roadSectionId = request.getRoadSectionId();
if (roadSectionId != null) {
// 从缓存中获取路段信息,并根据路段的起止桩号筛选数据
DcRoadSection dcRoadSection = redisCache.getCacheMapValue(RedisKeyConstants.DC_ROAD_SECTION, roadSectionId);
if (dcRoadSection != null) {
queryWrapper.between(
DcTrafficSectionData::getStakeMark,
StakeMarkUtils.stakeMarkToInt(dcRoadSection.getStartStakeMark()),
StakeMarkUtils.stakeMarkToInt(dcRoadSection.getEndStakeMark()));
}
}
// 获取设备类型
Integer deviceType = request.getDeviceType();
if (deviceType == null) {
// 如果没有指定设备类型,则默认为毫米波雷达
deviceType = DeviceTypeConstants.MILLIMETER_WAVE_RADAR;
}
queryWrapper.eq(DcTrafficSectionData::getDeviceType, deviceType);
// 根据收集到的交通段数据计算并返回交通指标数据
return trafficSectionAnalysis.calculateTrafficMetrics(request, list(queryWrapper));
}
/**
* 获取当前拥堵的路段数据列表
*
* @param direction 指定的方向用于筛选交通数据
* @return 返回一个包含当前拥堵路段数据的列表
*/
@Override
public List<DcCongestedSectionData> currentCongestedSection(Byte direction) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(direction);
// 过滤掉时间错误的交通数据
processStaleTrafficSectionData(dcTrafficSectionDataCaches);
// 移除非毫米波雷达设备类型的交通数据
dcTrafficSectionDataCaches.removeIf(data -> !Objects.equals(data.getDeviceType(), DeviceTypeConstants.MILLIMETER_WAVE_RADAR));
// 分析交通数据,计算出当前拥堵的路段列表并返回
return trafficSectionAnalysis.calculateCongestedSection(dcTrafficSectionDataCaches);
}
/**
* 恢复每日缓存的函数
* 该方法尝试从物联平台获取所有设备信息并对这些信息进行处理
* 如果获取信息失败或处理过程中发生异常则记录错误信息
*/
private void recoveryDailyCache() {
try {
// 尝试从指定产品ID获取设备信息
Map<String, Object> oneStopDeviceMap = dcDeviceController.getDeviceByProductId(IotProductEnum.ONE_STOP_PRODUCT.value());
// 检查获取的设备信息是否为空
if (oneStopDeviceMap == null || oneStopDeviceMap.get("data") == null) {
logger.error("获取一类交通量调查站设备数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value());
return;
}
// 将获取的设备信息转换为JSON数组,并遍历处理每个设备的数据
JSONArray deviceJsonArray = JSONArray.parseArray(oneStopDeviceMap.get("data").toString());
deviceJsonArray.forEach(trafficSectionStatistics::processDeviceData);
} catch (HttpException | IOException e) {
// 记录处理设备数据时发生的异常
logger.error("处理设备数据时发生异常", e);
}
}
/**
* 恢复每月交通数据缓存的方法
* 通过查询当前月份至今的每日交通数据并将其添加到每月交通统计缓存中
*/
private void recoveryMonthlyCache() {
// 构建查询条件,查询当前月份至今的每日交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.DAY);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每日数据添加到每月交通统计缓存
dcTrafficSectionDataList.forEach(MonthlyTrafficSectionStatisticsCache::addCacheData);
}
/**
* 恢复每季度交通数据缓存的方法
* 通过查询当前季度至今的每月交通数据并将其添加到每季度交通统计缓存中
*/
private void recoveryQuarterlyCache() {
// 构建查询条件,查询当前季度至今的每月交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每月数据添加到每季度交通统计缓存
dcTrafficSectionDataList.forEach(QuarterlyTrafficSectionStatisticsCache::addCacheData);
}
/**
* 恢复每年交通数据缓存的方法
* 通过查询当前年份至今的每季度交通数据并将其添加到每年交通统计缓存中
*/
private void recoveryYearlyCache() {
// 构建查询条件,查询当前年份至今的每季度交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每季度数据添加到每年交通统计缓存
dcTrafficSectionDataList.forEach(YearlyTrafficSectionStatisticsCache::addCacheData);
}
/**
* 将交通数据添加到缓存中
* 该方法将交通数据既添加到日交通数据缓存中也实时缓存到Redis中
*
* @param dcTrafficSectionData 交通段数据对象包含交通数据的详细信息
*/
private void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 添加到日交通数据缓存中
DailyTrafficSectionStatisticsCache.addCacheData(dcTrafficSectionData);
// 将数据缓存到redis中
redisCache.setCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(dcTrafficSectionData.getDirection()),
dcTrafficSectionData.getDeviceId(),
dcTrafficSectionData);
}
/**
* 从Redis缓存中获取指定方向的交通路段数据列表
*
* @param direction 交通方向如果为null则获取双向数据
* @return DcTrafficSectionData列表确保列表非空
*/
public List<DcTrafficSectionData> getDcTrafficSectionDataRedisCache(Byte direction) {
Map<String, DcTrafficSectionData> dcTrafficSectionDataMap = new HashMap<>();
// 根据方向选择相应的交通数据
Map<String, DcTrafficSectionData> trafficDataMap = (direction != null)
? getTrafficDataByDirection(direction)
: getTrafficDataForBothDirections();
// 将获取的交通数据合并到结果映射中
if (trafficDataMap != null) {
dcTrafficSectionDataMap.putAll(trafficDataMap);
}
// 将结果映射的值转换为列表并返回,确保列表非空
return new ArrayList<>(dcTrafficSectionDataMap.values());
}
/**
* 过滤掉时间错误的交通路段数据并更新数据的统计时间为当前时间
* 该方法会遍历交通路段数据列表移除统计时间早于20分钟前的数据并将其它数据的统计时间更新为当前时间
*
* @param dcTrafficSectionDataList 交通路段数据列表列表中每一项包含一个交通路段的统计数据
*/
public void processStaleTrafficSectionData(List<DcTrafficSectionData> dcTrafficSectionDataList) {
// 计算20分钟前的时间戳
Instant twentyMinutesAgo = Instant.now().minus(20, ChronoUnit.MINUTES);
// 获取当前时间
Instant currentTime = Instant.now();
// 遍历交通路段数据列表
dcTrafficSectionDataList.removeIf(data -> {
// 获取数据的统计时间
Instant dataStatisticalTime = data.getStatisticalDate().toInstant();
// 如果数据的统计时间早于20分钟前,则移除该数据,并记录日志
if (dataStatisticalTime.isBefore(twentyMinutesAgo)) {
logger.error("过滤掉时间错误的交通路段数据,已移除:" + data);
return true; // 移除该元素
}
// 更新数据的统计时间为当前时间
data.setStatisticalDate(Date.from(currentTime)); // 直接使用Instant对象
return false; // 保留该元素
});
}
/**
* 根据方向获取交通数据
*
* @param direction 方向
* @return 单向交通数据
*/
private Map<String, DcTrafficSectionData> getTrafficDataByDirection(Byte direction) {
return redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(direction));
}
/**
* 获取上行和下行所有交通数据
*
* @return 所有交通数据
*/
private Map<String, DcTrafficSectionData> getTrafficDataForBothDirections() {
Map<String, DcTrafficSectionData> allData = new HashMap<>();
allData.putAll(redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(LaneDirectionEnum.UPWARD.getValue())));
allData.putAll(redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(LaneDirectionEnum.DOWNWARD.getValue())));
return allData;
}
/**
* 根据请求过滤交通数据
*
* @param request 包含交通数据查询条件的请求对象可以指定路段ID和设备类型
* @param dcTrafficSectionDataCaches 存储的交通数据段列表
* @return 过滤后的交通数据段列表仅包含满足请求条件的数据
*/
public List<DcTrafficSectionData> filterTrafficDataByRequest(
DcTrafficMetricsDataRequest request,
List<DcTrafficSectionData> dcTrafficSectionDataCaches
) {
if (request == null) {
return dcTrafficSectionDataCaches; // 如果请求对象为空,直接返回原始交通数据列表
}
Long roadSectionId = request.getRoadSectionId();
// 初始化路段起始和终止桩号
Integer startStakeMark = null;
Integer endStakeMark = null;
if (roadSectionId != null) {
// 根据提供的路段ID查询路段信息,并转换为起始和终止桩号
DcRoadSection dcRoadSection = redisCache.getCacheMapValue(RedisKeyConstants.DC_ROAD_SECTION, roadSectionId);
// 验证路段ID是否存在
if (dcRoadSection == null) {
throw new ServiceException("路段ID不存在");
}
startStakeMark = StakeMarkUtils.stakeMarkToInt(dcRoadSection.getStartStakeMark());
endStakeMark = StakeMarkUtils.stakeMarkToInt(dcRoadSection.getEndStakeMark());
}
// 获取请求中的设备类型
Integer deviceType = request.getDeviceType();
// 如果设备类型为空,则默认为毫米波雷达
if (deviceType == null) {
deviceType = DeviceTypeConstants.MILLIMETER_WAVE_RADAR;
}
// 使用lambda表达式和流对交通数据进行过滤
final Integer finalStartStakeMark = startStakeMark;
final Integer finalEndStakeMark = endStakeMark;
final Integer finalDeviceType = deviceType;
// 筛选并返回符合路段范围条件和设备类型条件的交通数据列表
return dcTrafficSectionDataCaches.stream()
.filter(data -> (finalStartStakeMark == null || finalEndStakeMark == null
|| (data.getStakeMark() >= finalStartStakeMark && data.getStakeMark() <= finalEndStakeMark))
&& finalDeviceType.equals(data.getDeviceType()))
.collect(Collectors.toList());
}
/**
* 根据请求过滤交通数据
*
* @param request 包含设备类型和设备ID的请求对象用于指定过滤条件
* @param dcTrafficSectionDataCaches 原始的交通数据列表
* @return 过滤后的交通数据列表仅包含与请求条件匹配的数据
*/
public List<DcTrafficSectionData> filterTrafficDataByRequest(
DcTrafficSectionDataRequest request,
List<DcTrafficSectionData> dcTrafficSectionDataCaches
) {
if (request == null) {
return dcTrafficSectionDataCaches; // 如果请求对象为空,直接返回原始交通数据列表
}
Integer deviceType = request.getDeviceType();
Long deviceId = request.getDeviceId();
if (deviceType == null && deviceId == null) {
return dcTrafficSectionDataCaches; // 如果请求中既没有设备类型也没有设备ID,返回原始数据列表
}
// 使用流对交通数据进行过滤,只返回与请求中的设备类型和设备ID匹配的数据
return dcTrafficSectionDataCaches.stream()
.filter(data -> {
boolean typeMatches = deviceType == null || data.getDeviceType().equals(deviceType); // 设备类型匹配条件
boolean idMatches = deviceId == null || data.getDeviceId().equals(deviceId); // 设备ID匹配条件
return typeMatches && idMatches; // 只有当两者都匹配时,数据才被保留
})
.collect(Collectors.toList());
}
}

503
zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java

@ -1,508 +1,23 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.zc.business.constant.DeviceTypeConstants;
import com.zc.business.constant.RedisKeyConstants;
import com.zc.business.controller.DcDeviceController;
import com.zc.business.domain.DcCongestedSectionData;
import com.zc.business.domain.DcRoadSection;
import com.zc.business.domain.DcTrafficMetricsData;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.*;
import com.zc.business.request.DcTrafficMetricsDataRequest;
import com.zc.business.request.DcTrafficSectionDataRequest;
import com.zc.business.statistics.cache.*;
import com.zc.business.mapper.DcTrafficSectionDataMapper;
import com.zc.business.domain.DcStatisticsData;
import com.zc.business.service.IDcTrafficStatisticsService;
import com.zc.business.statistics.handler.TrafficAnalysis;
import com.zc.business.statistics.handler.TrafficStatistics;
import com.zc.business.utils.StakeMarkUtils;
import com.zc.common.core.httpclient.exception.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.List;
/**
* 交通断面数据服务实现类负责处理实时设备消息缓存数据定时任务以及数据保存等功能
*
* @author xiepufeng
*/
@Service
public class DcTrafficStatisticsServiceImpl
extends ServiceImpl<DcTrafficSectionDataMapper, DcTrafficSectionData>
implements IDcTrafficStatisticsService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private RedisCache redisCache;
@Resource
private DcDeviceController dcDeviceController;
@Resource
private TrafficStatistics trafficStatistics;
@Resource
private TrafficAnalysis trafficAnalysis;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通数据缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从es中恢复当天交通数据缓存
recoveryMonthlyCache(); // 恢复每月交通数据缓存
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
recoveryYearlyCache(); // 恢复每年交通数据缓存
}
/**
* 处理实时接收到的一类交流站设备消息并将其转换为交通断面统计数据对象并缓存
*
* @param msg 设备发送的JSON格式实时消息
*/
@Override
public void processRealtimeOneStopMessage(JSONObject msg) {
// 1. 将设备消息转换为交通断面数据统计定义对象
List<DcTrafficSectionData> dcTrafficSectionDataList = trafficStatistics.convertToTrafficStatistics(msg, DeviceDataCategoryEnum.REAL_TIME);
if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) {
// 2. 添加到缓存中
dcTrafficSectionDataList.forEach(this::addCacheData);
}
}
/**
* 根据提供的请求参数获取当前的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含当前交通截面数据列表
*/
@Override
public List<DcTrafficSectionData> currentSections(DcTrafficSectionDataRequest request) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(request.getDirection());
return filterTrafficDataByRequest(request, dcTrafficSectionDataCaches);
}
/**
* 根据提供的请求参数获取历史的交通截面数据
*
* @param request 包含获取交通截面所需的所有请求参数的对象
* @return DcTrafficSectionData 返回一个包含历史交通截面数据列表
*/
@Override
public List<DcTrafficSectionData> historySections(DcTrafficSectionDataRequest request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
// 构建查询条件
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, request.getStartTime(), request.getEndTime());
queryWrapper.eq(DcTrafficSectionData::getPeriodType, request.getPeriodType());
if (request.getDirection() != null) {
queryWrapper.eq(DcTrafficSectionData::getDirection, request.getDirection());
}
if (request.getDeviceType() != null) {
queryWrapper.eq(DcTrafficSectionData::getDeviceType, request.getDeviceType());
}
if (request.getDeviceId() != null) {
queryWrapper.eq(DcTrafficSectionData::getDeviceId, request.getDeviceId());
}
return list(queryWrapper);
}
/**
* 根据请求获取当前交通指标数据
*
* @param request 包含获取交通指标所需的所有请求参数的对象
* @return 返回当前交通指标数据
* @throws ServiceException 如果没有获取到交通数据则抛出异常
*/
@Override
public List<DcTrafficMetricsData> currentTrafficMetrics(DcTrafficMetricsDataRequest request) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(request.getDirection());
// 过滤掉时间错误的交通数据
processStaleTrafficSectionData(dcTrafficSectionDataCaches);
// 根据请求过滤交通数据
List<DcTrafficSectionData> trafficSectionDataList = filterTrafficDataByRequest(request, dcTrafficSectionDataCaches);
// 对获取的交通路段数据进行分析,得到交通指标数据
List<DcTrafficMetricsData> dcTrafficMetricsDataList = trafficAnalysis.calculateTrafficMetrics(request, trafficSectionDataList);
if (dcTrafficMetricsDataList == null || dcTrafficMetricsDataList.isEmpty()) {
// 如果没有获取到数据,则抛出异常
throw new ServiceException("获取当前交通特征指数失败");
}
// 根据收集到的交通段数据计算并返回交通指标数据
return dcTrafficMetricsDataList;
}
public class DcTrafficStatisticsServiceImpl implements IDcTrafficStatisticsService {
/**
* 计算获取指定条件下的历史交通指标数据
* 根据传入的统计请求数据查询历史累计车流量数据
*
* @param request 包含查询条件的请求对象包括开始时间结束时间方向周期类型和所属路段ID等信息
* @return 返回一个包含交通指标数据的列表
* @param request 包含统计查询条件的DcStatisticsData对象用于指定查询的历史流量数据的细节如时间范围等
* @return 返回一个DcStatisticsData列表
*/
@Override
public List<DcTrafficMetricsData> historyTrafficMetrics(DcTrafficMetricsDataRequest request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
// 构建查询条件
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, request.getStartTime(), request.getEndTime());
queryWrapper.eq(DcTrafficSectionData::getPeriodType, request.getPeriodType());
if (request.getDirection() != null) {
queryWrapper.eq(DcTrafficSectionData::getDirection, request.getDirection());
}
// 根据请求获取所属路段ID,并进一步筛选路段范围内的数据
Long roadSectionId = request.getRoadSectionId();
if (roadSectionId != null) {
// 从缓存中获取路段信息,并根据路段的起止桩号筛选数据
DcRoadSection dcRoadSection = redisCache.getCacheMapValue(RedisKeyConstants.DC_ROAD_SECTION, roadSectionId);
if (dcRoadSection != null) {
queryWrapper.between(
DcTrafficSectionData::getStakeMark,
StakeMarkUtils.stakeMarkToInt(dcRoadSection.getStartStakeMark()),
StakeMarkUtils.stakeMarkToInt(dcRoadSection.getEndStakeMark()));
}
}
// 获取设备类型
Integer deviceType = request.getDeviceType();
if (deviceType == null) {
// 如果没有指定设备类型,则默认为毫米波雷达
deviceType = DeviceTypeConstants.MILLIMETER_WAVE_RADAR;
}
queryWrapper.eq(DcTrafficSectionData::getDeviceType, deviceType);
// 根据收集到的交通段数据计算并返回交通指标数据
return trafficAnalysis.calculateTrafficMetrics(request, list(queryWrapper));
public List<DcStatisticsData> historyFlow(DcStatisticsData request) {
// TODO
return null;
}
/**
* 获取当前拥堵的路段数据列表
*
* @param direction 指定的方向用于筛选交通数据
* @return 返回一个包含当前拥堵路段数据的列表
*/
@Override
public List<DcCongestedSectionData> currentCongestedSection(Byte direction) {
// 从Redis缓存中获取指定方向的交通路段数据列表
List<DcTrafficSectionData> dcTrafficSectionDataCaches = getDcTrafficSectionDataRedisCache(direction);
// 过滤掉时间错误的交通数据
processStaleTrafficSectionData(dcTrafficSectionDataCaches);
// 移除非毫米波雷达设备类型的交通数据
dcTrafficSectionDataCaches.removeIf(data -> !Objects.equals(data.getDeviceType(), DeviceTypeConstants.MILLIMETER_WAVE_RADAR));
// 分析交通数据,计算出当前拥堵的路段列表并返回
return trafficAnalysis.calculateCongestedSection(dcTrafficSectionDataCaches);
}
/**
* 恢复每日缓存的函数
* 该方法尝试从物联平台获取所有设备信息并对这些信息进行处理
* 如果获取信息失败或处理过程中发生异常则记录错误信息
*/
private void recoveryDailyCache() {
try {
// 尝试从指定产品ID获取设备信息
Map<String, Object> oneStopDeviceMap = dcDeviceController.getDeviceByProductId(IotProductEnum.ONE_STOP_PRODUCT.value());
// 检查获取的设备信息是否为空
if (oneStopDeviceMap == null || oneStopDeviceMap.get("data") == null) {
logger.error("获取一类交通量调查站设备数据失败,产品id:{}", IotProductEnum.ONE_STOP_PRODUCT.value());
return;
}
// 将获取的设备信息转换为JSON数组,并遍历处理每个设备的数据
JSONArray deviceJsonArray = JSONArray.parseArray(oneStopDeviceMap.get("data").toString());
deviceJsonArray.forEach(trafficStatistics::processDeviceData);
} catch (HttpException | IOException e) {
// 记录处理设备数据时发生的异常
logger.error("处理设备数据时发生异常", e);
}
}
/**
* 恢复每月交通数据缓存的方法
* 通过查询当前月份至今的每日交通数据并将其添加到每月交通统计缓存中
*/
private void recoveryMonthlyCache() {
// 构建查询条件,查询当前月份至今的每日交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.DAY);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每日数据添加到每月交通统计缓存
dcTrafficSectionDataList.forEach(MonthlyTrafficStatisticsCache::addCacheData);
}
/**
* 恢复每季度交通数据缓存的方法
* 通过查询当前季度至今的每月交通数据并将其添加到每季度交通统计缓存中
*/
private void recoveryQuarterlyCache() {
// 构建查询条件,查询当前季度至今的每月交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每月数据添加到每季度交通统计缓存
dcTrafficSectionDataList.forEach(QuarterlyTrafficStatisticsCache::addCacheData);
}
/**
* 恢复每年交通数据缓存的方法
* 通过查询当前年份至今的每季度交通数据并将其添加到每年交通统计缓存中
*/
private void recoveryYearlyCache() {
// 构建查询条件,查询当前年份至今的每季度交通数据
LambdaQueryWrapper<DcTrafficSectionData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcTrafficSectionData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER);
queryWrapper.between(DcTrafficSectionData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date());
List<DcTrafficSectionData> dcTrafficSectionDataList = this.list(queryWrapper);
// 遍历查询结果,将每季度数据添加到每年交通统计缓存
dcTrafficSectionDataList.forEach(YearlyTrafficStatisticsCache::addCacheData);
}
/**
* 将交通数据添加到缓存中
* 该方法将交通数据既添加到日交通数据缓存中也实时缓存到Redis中
*
* @param dcTrafficSectionData 交通段数据对象包含交通数据的详细信息
*/
private void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 添加到日交通数据缓存中
DailyTrafficStatisticsCache.addCacheData(dcTrafficSectionData);
// 将数据缓存到redis中
redisCache.setCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(dcTrafficSectionData.getDirection()),
dcTrafficSectionData.getDeviceId(),
dcTrafficSectionData);
}
/**
* 从Redis缓存中获取指定方向的交通路段数据列表
*
* @param direction 交通方向如果为null则获取双向数据
* @return DcTrafficSectionData列表确保列表非空
*/
public List<DcTrafficSectionData> getDcTrafficSectionDataRedisCache(Byte direction) {
Map<String, DcTrafficSectionData> dcTrafficSectionDataMap = new HashMap<>();
// 根据方向选择相应的交通数据
Map<String, DcTrafficSectionData> trafficDataMap = (direction != null)
? getTrafficDataByDirection(direction)
: getTrafficDataForBothDirections();
// 将获取的交通数据合并到结果映射中
if (trafficDataMap != null) {
dcTrafficSectionDataMap.putAll(trafficDataMap);
}
// 将结果映射的值转换为列表并返回,确保列表非空
return new ArrayList<>(dcTrafficSectionDataMap.values());
}
/**
* 过滤掉时间错误的交通路段数据并更新数据的统计时间为当前时间
* 该方法会遍历交通路段数据列表移除统计时间早于20分钟前的数据并将其它数据的统计时间更新为当前时间
*
* @param dcTrafficSectionDataList 交通路段数据列表列表中每一项包含一个交通路段的统计数据
*/
public void processStaleTrafficSectionData(List<DcTrafficSectionData> dcTrafficSectionDataList) {
// 计算20分钟前的时间戳
Instant twentyMinutesAgo = Instant.now().minus(20, ChronoUnit.MINUTES);
// 获取当前时间
Instant currentTime = Instant.now();
// 遍历交通路段数据列表
dcTrafficSectionDataList.removeIf(data -> {
// 获取数据的统计时间
Instant dataStatisticalTime = data.getStatisticalDate().toInstant();
// 如果数据的统计时间早于20分钟前,则移除该数据,并记录日志
if (dataStatisticalTime.isBefore(twentyMinutesAgo)) {
logger.error("过滤掉时间错误的交通路段数据,已移除:" + data);
return true; // 移除该元素
}
// 更新数据的统计时间为当前时间
data.setStatisticalDate(Date.from(currentTime)); // 直接使用Instant对象
return false; // 保留该元素
});
}
/**
* 根据方向获取交通数据
*
* @param direction 方向
* @return 单向交通数据
*/
private Map<String, DcTrafficSectionData> getTrafficDataByDirection(Byte direction) {
return redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(direction));
}
/**
* 获取上行和下行所有交通数据
*
* @return 所有交通数据
*/
private Map<String, DcTrafficSectionData> getTrafficDataForBothDirections() {
Map<String, DcTrafficSectionData> allData = new HashMap<>();
allData.putAll(redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(LaneDirectionEnum.UPWARD.getValue())));
allData.putAll(redisCache.getCacheMapValue(RedisKeyConstants.getDcDevicesTrafficStatisticsKey(LaneDirectionEnum.DOWNWARD.getValue())));
return allData;
}
/**
* 根据请求过滤交通数据
*
* @param request 包含交通数据查询条件的请求对象可以指定路段ID和设备类型
* @param dcTrafficSectionDataCaches 存储的交通数据段列表
* @return 过滤后的交通数据段列表仅包含满足请求条件的数据
*/
public List<DcTrafficSectionData> filterTrafficDataByRequest(
DcTrafficMetricsDataRequest request,
List<DcTrafficSectionData> dcTrafficSectionDataCaches
) {
if (request == null) {
return dcTrafficSectionDataCaches; // 如果请求对象为空,直接返回原始交通数据列表
}
Long roadSectionId = request.getRoadSectionId();
// 初始化路段起始和终止桩号
Integer startStakeMark = null;
Integer endStakeMark = null;
if (roadSectionId != null) {
// 根据提供的路段ID查询路段信息,并转换为起始和终止桩号
DcRoadSection dcRoadSection = redisCache.getCacheMapValue(RedisKeyConstants.DC_ROAD_SECTION, roadSectionId);
// 验证路段ID是否存在
if (dcRoadSection == null) {
throw new ServiceException("路段ID不存在");
}
startStakeMark = StakeMarkUtils.stakeMarkToInt(dcRoadSection.getStartStakeMark());
endStakeMark = StakeMarkUtils.stakeMarkToInt(dcRoadSection.getEndStakeMark());
}
// 获取请求中的设备类型
Integer deviceType = request.getDeviceType();
// 如果设备类型为空,则默认为毫米波雷达
if (deviceType == null) {
deviceType = DeviceTypeConstants.MILLIMETER_WAVE_RADAR;
}
// 使用lambda表达式和流对交通数据进行过滤
final Integer finalStartStakeMark = startStakeMark;
final Integer finalEndStakeMark = endStakeMark;
final Integer finalDeviceType = deviceType;
// 筛选并返回符合路段范围条件和设备类型条件的交通数据列表
return dcTrafficSectionDataCaches.stream()
.filter(data -> (finalStartStakeMark == null || finalEndStakeMark == null
|| (data.getStakeMark() >= finalStartStakeMark && data.getStakeMark() <= finalEndStakeMark))
&& finalDeviceType.equals(data.getDeviceType()))
.collect(Collectors.toList());
}
/**
* 根据请求过滤交通数据
*
* @param request 包含设备类型和设备ID的请求对象用于指定过滤条件
* @param dcTrafficSectionDataCaches 原始的交通数据列表
* @return 过滤后的交通数据列表仅包含与请求条件匹配的数据
*/
public List<DcTrafficSectionData> filterTrafficDataByRequest(
DcTrafficSectionDataRequest request,
List<DcTrafficSectionData> dcTrafficSectionDataCaches
) {
if (request == null) {
return dcTrafficSectionDataCaches; // 如果请求对象为空,直接返回原始交通数据列表
}
Integer deviceType = request.getDeviceType();
Long deviceId = request.getDeviceId();
if (deviceType == null && deviceId == null) {
return dcTrafficSectionDataCaches; // 如果请求中既没有设备类型也没有设备ID,返回原始数据列表
}
// 使用流对交通数据进行过滤,只返回与请求中的设备类型和设备ID匹配的数据
return dcTrafficSectionDataCaches.stream()
.filter(data -> {
boolean typeMatches = deviceType == null || data.getDeviceType().equals(deviceType); // 设备类型匹配条件
boolean idMatches = deviceId == null || data.getDeviceId().equals(deviceId); // 设备ID匹配条件
return typeMatches && idMatches; // 只有当两者都匹配时,数据才被保留
})
.collect(Collectors.toList());
}
}

244
zc-business/src/main/java/com/zc/business/service/impl/OdsTollEnpassDataServiceImpl.java

@ -0,0 +1,244 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.domain.OdsTollEnpassData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.enums.VehicleTypeEnum;
import com.zc.business.mapper.OdsTollEnpassDataMapper;
import com.zc.business.service.IOdsTollEnpassDataService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 收费站入口流水数据
*/
@Service
@DataSource(value = DataSourceType.SLAVE)//切换数据源
public class OdsTollEnpassDataServiceImpl extends ServiceImpl<OdsTollEnpassDataMapper, OdsTollEnpassData>
implements IOdsTollEnpassDataService {
/**
* 获取过去一小时的OdsTollEnpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的OdsTollEnpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEnpassData> - 过去一小时的OdsTollEnpassData数据列表
*/
@Override
public List<OdsTollEnpassData> lastHourData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollEnpassData> queryWrapper = new LambdaQueryWrapper<>();
// 计算一小时前的时间点
DateTime lastHour = DateUtil.offsetHour(new Date(), -1);
// 设置查询条件,查询时间包含在lastHour内的数据
queryWrapper.between(OdsTollEnpassData::getEntime, DateUtil.beginOfHour(lastHour), DateUtil.endOfHour(lastHour));
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 获取当月的OdsTollEnpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的OdsTollEnpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEnpassData> - 当月的OdsTollEnpassData数据列表
*/
@Override
public List<OdsTollEnpassData> currentMonthData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollEnpassData> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件,查询当前月份的所有数据
queryWrapper.between(OdsTollEnpassData::getEntime, DateUtil.beginOfMonth(new Date()), new Date());
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 计算收费站在指定时间段内的统计数据
*
* @param list 包含收费通行数据的列表不应为null或空
* @return 返回一个包含各收费站统计数据的列表如果输入列表为空则返回空列表
*/
public List<DcTollStationStatisticsData> calculateTollStationStatistics(List<OdsTollEnpassData> list) {
// 检查输入列表是否为空,如果是则直接返回空列表
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
// 使用Stream API按入口站号和入口时间进行分组
Map<String, Map<String, List<OdsTollEnpassData>>> groupedData =
list.stream()
.collect(Collectors.groupingBy(
OdsTollEnpassData::getEntollstationid, // 按入口站号分组
Collectors.groupingBy(odsTollEnpassData -> DateUtil.beginOfHour(odsTollEnpassData.getEntime()).toString()) // 在每个入口站内按入口通行时间进一步分组
));
// 遍历分组后的数据,计算每个收费站每个时间周期内的统计数据
List<DcTollStationStatisticsData> dcTollStationStatisticsDataList = new ArrayList<>();
groupedData.forEach((entollstationid, timeGroupedData) -> timeGroupedData.forEach((entime, dataList) -> dcTollStationStatisticsDataList.add(trafficStatistics(dataList))));
return dcTollStationStatisticsDataList;
}
/**
* 计算交通统计数据
*
* @param list 包含收费过境数据的列表
* @return DcTollStationStatisticsData 对象包含计算后的交通统计数据
*/
private DcTollStationStatisticsData trafficStatistics(List<OdsTollEnpassData> list) {
// 创建DcTollStationStatisticsData对象并初始化
DcTollStationStatisticsData dcTollStationStatisticsData = new DcTollStationStatisticsData();
// 初始化车流量总和
int trafficVolume = 0;
// 初始化1型客车车流量
int type1PassengerFlow = 0;
// 初始化2型客车车流量
int type2PassengerFlow = 0;
// 初始化3型客车车流量
int type3PassengerFlow = 0;
// 初始化4型客车车流量
int type4PassengerFlow = 0;
// 初始化1型货车车流量
int type1TruckFlow = 0;
// 初始化2型货车车流量
int type2TruckFlow = 0;
// 初始化3型货车车流量
int type3TruckFlow = 0;
// 初始化4型货车车流量
int type4TruckFlow = 0;
// 初始化5型货车车流量
int type5TruckFlow = 0;
// 初始化6型货车车流量
int type6TruckFlow = 0;
// 初始化1型专项作业车车流量
int type1SpecialVehicleFlow = 0;
// 初始化2型专项作业车车流量
int type2SpecialVehicleFlow = 0;
// 初始化3型专项作业车车流量
int type3SpecialVehicleFlow = 0;
// 初始化4型专项作业车车流量
int type4SpecialVehicleFlow = 0;
// 初始化5型专项作业车车流量
int type5SpecialVehicleFlow = 0;
// 初始化6型专项作业车车流量
int type6SpecialVehicleFlow = 0;
for (OdsTollEnpassData odsTollEnpassData : list) {
Integer vcount = odsTollEnpassData.getVcount();
Integer vehicletype = odsTollEnpassData.getVehicletype();
trafficVolume += vcount;
if (VehicleTypeEnum.TYPE_1_PASSENGER_CAR.getValue().equals(vehicletype)) {
type1PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_PASSENGER_CAR.getValue().equals(vehicletype)) {
type2PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_3_PASSENGER_CAR.getValue().equals(vehicletype)) {
type3PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_PASSENGER_CAR.getValue().equals(vehicletype)) {
type4PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_1_TRUCK.getValue().equals(vehicletype)) {
type1TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_TRUCK.getValue().equals(vehicletype)) {
type2TruckFlow+= vcount;
} else if (VehicleTypeEnum.TYPE_3_TRUCK.getValue().equals(vehicletype)) {
type3TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_TRUCK.getValue().equals(vehicletype)) {
type4TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_5_TRUCK.getValue().equals(vehicletype)) {
type5TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_6_TRUCK.getValue().equals(vehicletype)) {
type6TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_1_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type1SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type2SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_3_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type3SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type4SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_5_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type5SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_6_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type6SpecialVehicleFlow += vcount;
}
}
// 使用第一个数据项的信息填充汇总统计对象的基本属性
OdsTollEnpassData firstOdsTollEnpassData = list.iterator().next();
// 收费站站点标识
dcTollStationStatisticsData.setTollStationCode(firstOdsTollEnpassData.getEntollstationid());
Date date = DateUtil.beginOfHour(firstOdsTollEnpassData.getEntime());
// 统计时间
dcTollStationStatisticsData.setStatisticalDate(date);
// 上报时间
dcTollStationStatisticsData.setReportTime(date);
// 统计粒度
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 出入类型
dcTollStationStatisticsData.setAccessType(DcTollStationStatisticsData.ENTRANCE);
// 车流量
dcTollStationStatisticsData.setTrafficVolume(trafficVolume);
// 1型客车车流量
dcTollStationStatisticsData.setType1PassengerFlow(type1PassengerFlow);
// 2型客车车流量
dcTollStationStatisticsData.setType2PassengerFlow(type2PassengerFlow);
// 3型客车车流量
dcTollStationStatisticsData.setType3PassengerFlow(type3PassengerFlow);
// 4型客车车流量
dcTollStationStatisticsData.setType4PassengerFlow(type4PassengerFlow);
// 1型货车车流量
dcTollStationStatisticsData.setType1TruckFlow(type1TruckFlow);
// 2型货车车流量
dcTollStationStatisticsData.setType2TruckFlow(type2TruckFlow);
// 3型货车车流量
dcTollStationStatisticsData.setType3TruckFlow(type3TruckFlow);
// 4型货车车流量
dcTollStationStatisticsData.setType4TruckFlow(type4TruckFlow);
// 5型货车车流量
dcTollStationStatisticsData.setType5TruckFlow(type5TruckFlow);
// 6型货车车流量
dcTollStationStatisticsData.setType6TruckFlow(type6TruckFlow);
// 1型专项作业车车流量
dcTollStationStatisticsData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow);
// 2型专项作业车车流量
dcTollStationStatisticsData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow);
// 3型专项作业车车流量
dcTollStationStatisticsData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow);
// 4型专项作业车车流量
dcTollStationStatisticsData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow);
// 5型专项作业车车流量
dcTollStationStatisticsData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow);
// 6型专项作业车车流量
dcTollStationStatisticsData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
return dcTollStationStatisticsData;
}
}

234
zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java

@ -0,0 +1,234 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.zc.business.domain.*;
import com.zc.business.domain.OdsTollEtctuData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.enums.VehicleTypeEnum;
import com.zc.business.mapper.OdsTollEtctuDataMapper;
import com.zc.business.service.IOdsTollEtctuDataService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 门架流水数据
*/
@Service
@DataSource(value = DataSourceType.SLAVE)//切换数据源
public class OdsTollEtctuDataImpl extends ServiceImpl<OdsTollEtctuDataMapper, OdsTollEtctuData>
implements IOdsTollEtctuDataService {
/**
* 获取最后一小时的门架流水数据列表
*
* @return List<OdsTollEtctuData> - 返回最后一小时的门架流水数据列表如果没有数据则返回空列表
*/
@Override
public List<OdsTollEtctuData> lastHourData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollEtctuData> queryWrapper = new LambdaQueryWrapper<>();
// 计算一小时前的时间点
DateTime lastHour = DateUtil.offsetHour(new Date(), -1);
// 设置查询条件,查询时间包含在lastHour内的数据
queryWrapper.between(OdsTollEtctuData::getTranstime, DateUtil.beginOfHour(lastHour), DateUtil.endOfHour(lastHour));
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 获取当月的门架流水数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的门架流水数据对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollEtctuData> - 当月的门架流水数据列表
*/
@Override
public List<OdsTollEtctuData> currentMonthData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollEtctuData> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件,查询当前月份的所有数据
queryWrapper.between(OdsTollEtctuData::getTranstime, DateUtil.beginOfMonth(new Date()), new Date());
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 计算门架在指定时间段内的统计数据
*
* @param odsTollEtctuDataList 包含门架数据的列表不应为null或空
* @return 返回一个包含各门架统计数据的列表如果输入列表为空则返回空列表
*/
@Override
public List<DcGantryStatisticsData> calculateGantryStatistics(List<OdsTollEtctuData> odsTollEtctuDataList) {
// 检查输入列表是否为空,如果是则直接返回空列表
if (odsTollEtctuDataList == null || odsTollEtctuDataList.isEmpty()) {
return new ArrayList<>();
}
// 使用Stream API按入口站号和入口时间进行分组
Map<String, Map<String, List<OdsTollEtctuData>>> groupedData =
odsTollEtctuDataList.stream()
.collect(Collectors.groupingBy(
OdsTollEtctuData::getGantryid, // 按入口站号分组
Collectors.groupingBy(odsTollEnpassData -> DateUtil.beginOfHour(odsTollEnpassData.getTranstime()).toString()) // 在每个入口站内按入口通行时间进一步分组
));
// 遍历分组后的数据,计算每个门架每个时间周期内的统计数据
List<DcGantryStatisticsData> dcGantryStatisticsDataList = new ArrayList<>();
groupedData.forEach((gantryid, timeGroupedData) -> timeGroupedData.forEach((entime, dataList) -> dcGantryStatisticsDataList.add(trafficStatistics(dataList))));
return dcGantryStatisticsDataList;
}
/**
* 计算交通统计数据
*
* @param list 包含收费过境数据的列表
* @return DcGantryStatisticsData 对象包含计算后的交通统计数据
*/
private DcGantryStatisticsData trafficStatistics(List<OdsTollEtctuData> list) {
// 创建DcGantryStatisticsData对象并初始化
DcGantryStatisticsData dcGantryStatisticsData = new DcGantryStatisticsData();
// 初始化车流量总和
int trafficVolume = 0;
// 初始化1型客车车流量
int type1PassengerFlow = 0;
// 初始化2型客车车流量
int type2PassengerFlow = 0;
// 初始化3型客车车流量
int type3PassengerFlow = 0;
// 初始化4型客车车流量
int type4PassengerFlow = 0;
// 初始化1型货车车流量
int type1TruckFlow = 0;
// 初始化2型货车车流量
int type2TruckFlow = 0;
// 初始化3型货车车流量
int type3TruckFlow = 0;
// 初始化4型货车车流量
int type4TruckFlow = 0;
// 初始化5型货车车流量
int type5TruckFlow = 0;
// 初始化6型货车车流量
int type6TruckFlow = 0;
// 初始化1型专项作业车车流量
int type1SpecialVehicleFlow = 0;
// 初始化2型专项作业车车流量
int type2SpecialVehicleFlow = 0;
// 初始化3型专项作业车车流量
int type3SpecialVehicleFlow = 0;
// 初始化4型专项作业车车流量
int type4SpecialVehicleFlow = 0;
// 初始化5型专项作业车车流量
int type5SpecialVehicleFlow = 0;
// 初始化6型专项作业车车流量
int type6SpecialVehicleFlow = 0;
for (OdsTollEtctuData odsTollEtctuData : list) {
Integer vehicletype = odsTollEtctuData.getVehicletype();
trafficVolume += 1;
if (VehicleTypeEnum.TYPE_1_PASSENGER_CAR.getValue().equals(vehicletype)) {
type1PassengerFlow += 1;
} else if (VehicleTypeEnum.TYPE_2_PASSENGER_CAR.getValue().equals(vehicletype)) {
type2PassengerFlow += 1;
} else if (VehicleTypeEnum.TYPE_3_PASSENGER_CAR.getValue().equals(vehicletype)) {
type3PassengerFlow += 1;
} else if (VehicleTypeEnum.TYPE_4_PASSENGER_CAR.getValue().equals(vehicletype)) {
type4PassengerFlow += 1;
} else if (VehicleTypeEnum.TYPE_1_TRUCK.getValue().equals(vehicletype)) {
type1TruckFlow += 1;
} else if (VehicleTypeEnum.TYPE_2_TRUCK.getValue().equals(vehicletype)) {
type2TruckFlow+= 1;
} else if (VehicleTypeEnum.TYPE_3_TRUCK.getValue().equals(vehicletype)) {
type3TruckFlow += 1;
} else if (VehicleTypeEnum.TYPE_4_TRUCK.getValue().equals(vehicletype)) {
type4TruckFlow += 1;
} else if (VehicleTypeEnum.TYPE_5_TRUCK.getValue().equals(vehicletype)) {
type5TruckFlow += 1;
} else if (VehicleTypeEnum.TYPE_6_TRUCK.getValue().equals(vehicletype)) {
type6TruckFlow += 1;
} else if (VehicleTypeEnum.TYPE_1_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type1SpecialVehicleFlow += 1;
} else if (VehicleTypeEnum.TYPE_2_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type2SpecialVehicleFlow += 1;
} else if (VehicleTypeEnum.TYPE_3_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type3SpecialVehicleFlow += 1;
} else if (VehicleTypeEnum.TYPE_4_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type4SpecialVehicleFlow += 1;
} else if (VehicleTypeEnum.TYPE_5_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type5SpecialVehicleFlow += 1;
} else if (VehicleTypeEnum.TYPE_6_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type6SpecialVehicleFlow += 1;
}
}
// 使用第一个数据项的信息填充汇总统计对象的基本属性
OdsTollEtctuData firstOdsTollEtctuData = list.iterator().next();
// 门架标识
dcGantryStatisticsData.setGantryCode(firstOdsTollEtctuData.getGantryid());
Date date = DateUtil.beginOfHour(firstOdsTollEtctuData.getTranstime());
// 统计时间
dcGantryStatisticsData.setStatisticalDate(date);
// 上报时间
dcGantryStatisticsData.setReportTime(date);
// 统计粒度
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 车流量
dcGantryStatisticsData.setTrafficVolume(trafficVolume);
// 1型客车车流量
dcGantryStatisticsData.setType1PassengerFlow(type1PassengerFlow);
// 2型客车车流量
dcGantryStatisticsData.setType2PassengerFlow(type2PassengerFlow);
// 3型客车车流量
dcGantryStatisticsData.setType3PassengerFlow(type3PassengerFlow);
// 4型客车车流量
dcGantryStatisticsData.setType4PassengerFlow(type4PassengerFlow);
// 1型货车车流量
dcGantryStatisticsData.setType1TruckFlow(type1TruckFlow);
// 2型货车车流量
dcGantryStatisticsData.setType2TruckFlow(type2TruckFlow);
// 3型货车车流量
dcGantryStatisticsData.setType3TruckFlow(type3TruckFlow);
// 4型货车车流量
dcGantryStatisticsData.setType4TruckFlow(type4TruckFlow);
// 5型货车车流量
dcGantryStatisticsData.setType5TruckFlow(type5TruckFlow);
// 6型货车车流量
dcGantryStatisticsData.setType6TruckFlow(type6TruckFlow);
// 1型专项作业车车流量
dcGantryStatisticsData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow);
// 2型专项作业车车流量
dcGantryStatisticsData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow);
// 3型专项作业车车流量
dcGantryStatisticsData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow);
// 4型专项作业车车流量
dcGantryStatisticsData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow);
// 5型专项作业车车流量
dcGantryStatisticsData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow);
// 6型专项作业车车流量
dcGantryStatisticsData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
return dcGantryStatisticsData;
}
}

239
zc-business/src/main/java/com/zc/business/service/impl/OdsTollExpassDataServiceImpl.java

@ -0,0 +1,239 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.zc.business.domain.*;
import com.zc.business.domain.OdsTollExpassData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.enums.VehicleTypeEnum;
import com.zc.business.mapper.OdsTollExpassDataMapper;
import com.zc.business.service.IOdsTollExpassDataService;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 收费站出口流水数据
*/
@Service
@DataSource(value = DataSourceType.SLAVE)//切换数据源
public class OdsTollExpassDataServiceImpl extends ServiceImpl<OdsTollExpassDataMapper, OdsTollExpassData>
implements IOdsTollExpassDataService {
/**
* 获取过去一小时的OdsTollExpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的OdsTollExpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollExpassData> - 过去一小时的OdsTollExpassData数据列表
*/
@Override
public List<OdsTollExpassData> lastHourData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollExpassData> queryWrapper = new LambdaQueryWrapper<>();
// 计算一小时前的时间点
DateTime lastHour = DateUtil.offsetHour(new Date(), -1);
// 设置查询条件,查询时间包含在lastHour内的数据
queryWrapper.between(OdsTollExpassData::getExtime, DateUtil.beginOfHour(lastHour), DateUtil.endOfHour(lastHour));
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 获取当月的OdsTollExpassData数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的OdsTollExpassData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<OdsTollExpassData> - 当月的OdsTollExpassData数据列表
*/
@Override
public List<OdsTollExpassData> currentMonthData() {
// 创建查询条件包装器
LambdaQueryWrapper<OdsTollExpassData> queryWrapper = new LambdaQueryWrapper<>();
// 设置查询条件,查询当前月份的所有数据
queryWrapper.between(OdsTollExpassData::getExtime, DateUtil.beginOfMonth(new Date()), new Date());
// 根据查询条件获取数据列表并返回
return list(queryWrapper);
}
/**
* 计算收费站在指定时间段内的统计数据
*
* @param list 包含收费通行数据的列表不应为null或空
* @return 返回一个包含各收费站统计数据的列表如果输入列表为空则返回空列表
*/
@Override
public List<DcTollStationStatisticsData> calculateTollStationStatistics(List<OdsTollExpassData> list) {
// 检查输入列表是否为空,如果是则直接返回空列表
if (list == null || list.isEmpty()) {
return new ArrayList<>();
}
// 使用Stream API按入口站号和入口时间进行分组
Map<String, Map<String, List<OdsTollExpassData>>> groupedData =
list.stream()
.collect(Collectors.groupingBy(
OdsTollExpassData::getExtollstationid, // 按入口站号分组
Collectors.groupingBy(odsTollExpassData -> DateUtil.beginOfHour(odsTollExpassData.getExtime()).toString()) // 在每个入口站内按入口通行时间进一步分组
));
// 遍历分组后的数据,计算每个收费站每个时间周期内的统计数据
List<DcTollStationStatisticsData> dcTollStationStatisticsDataList = new ArrayList<>();
groupedData.forEach((extollstationid, timeGroupedData) -> timeGroupedData.forEach((entime, dataList) -> dcTollStationStatisticsDataList.add(trafficStatistics(dataList))));
return dcTollStationStatisticsDataList;
}
/**
* 计算交通统计数据
*
* @param list 包含收费过境数据的列表
* @return DcTollStationStatisticsData 对象包含计算后的交通统计数据
*/
private DcTollStationStatisticsData trafficStatistics(List<OdsTollExpassData> list) {
// 创建DcTollStationStatisticsData对象并初始化
DcTollStationStatisticsData dcTollStationStatisticsData = new DcTollStationStatisticsData();
// 初始化车流量总和
int trafficVolume = 0;
// 初始化1型客车车流量
int type1PassengerFlow = 0;
// 初始化2型客车车流量
int type2PassengerFlow = 0;
// 初始化3型客车车流量
int type3PassengerFlow = 0;
// 初始化4型客车车流量
int type4PassengerFlow = 0;
// 初始化1型货车车流量
int type1TruckFlow = 0;
// 初始化2型货车车流量
int type2TruckFlow = 0;
// 初始化3型货车车流量
int type3TruckFlow = 0;
// 初始化4型货车车流量
int type4TruckFlow = 0;
// 初始化5型货车车流量
int type5TruckFlow = 0;
// 初始化6型货车车流量
int type6TruckFlow = 0;
// 初始化1型专项作业车车流量
int type1SpecialVehicleFlow = 0;
// 初始化2型专项作业车车流量
int type2SpecialVehicleFlow = 0;
// 初始化3型专项作业车车流量
int type3SpecialVehicleFlow = 0;
// 初始化4型专项作业车车流量
int type4SpecialVehicleFlow = 0;
// 初始化5型专项作业车车流量
int type5SpecialVehicleFlow = 0;
// 初始化6型专项作业车车流量
int type6SpecialVehicleFlow = 0;
for (OdsTollExpassData odsTollExpassData : list) {
Integer vcount = odsTollExpassData.getVcount();
Integer vehicletype = odsTollExpassData.getExvehicletype();
trafficVolume += vcount;
if (VehicleTypeEnum.TYPE_1_PASSENGER_CAR.getValue().equals(vehicletype)) {
type1PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_PASSENGER_CAR.getValue().equals(vehicletype)) {
type2PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_3_PASSENGER_CAR.getValue().equals(vehicletype)) {
type3PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_PASSENGER_CAR.getValue().equals(vehicletype)) {
type4PassengerFlow += vcount;
} else if (VehicleTypeEnum.TYPE_1_TRUCK.getValue().equals(vehicletype)) {
type1TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_TRUCK.getValue().equals(vehicletype)) {
type2TruckFlow+= vcount;
} else if (VehicleTypeEnum.TYPE_3_TRUCK.getValue().equals(vehicletype)) {
type3TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_TRUCK.getValue().equals(vehicletype)) {
type4TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_5_TRUCK.getValue().equals(vehicletype)) {
type5TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_6_TRUCK.getValue().equals(vehicletype)) {
type6TruckFlow += vcount;
} else if (VehicleTypeEnum.TYPE_1_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type1SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_2_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type2SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_3_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type3SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_4_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type4SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_5_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type5SpecialVehicleFlow += vcount;
} else if (VehicleTypeEnum.TYPE_6_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) {
type6SpecialVehicleFlow += vcount;
}
}
// 使用第一个数据项的信息填充汇总统计对象的基本属性
OdsTollExpassData firstOdsTollEnpassData = list.iterator().next();
// 收费站站点标识
dcTollStationStatisticsData.setTollStationCode(firstOdsTollEnpassData.getExtollstationid());
Date date = DateUtil.beginOfHour(firstOdsTollEnpassData.getExtime());
// 统计时间
dcTollStationStatisticsData.setStatisticalDate(date);
// 上报时间
dcTollStationStatisticsData.setReportTime(date);
// 统计粒度
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 出入类型
dcTollStationStatisticsData.setAccessType(DcTollStationStatisticsData.ENTRANCE);
// 车流量
dcTollStationStatisticsData.setTrafficVolume(trafficVolume);
// 1型客车车流量
dcTollStationStatisticsData.setType1PassengerFlow(type1PassengerFlow);
// 2型客车车流量
dcTollStationStatisticsData.setType2PassengerFlow(type2PassengerFlow);
// 3型客车车流量
dcTollStationStatisticsData.setType3PassengerFlow(type3PassengerFlow);
// 4型客车车流量
dcTollStationStatisticsData.setType4PassengerFlow(type4PassengerFlow);
// 1型货车车流量
dcTollStationStatisticsData.setType1TruckFlow(type1TruckFlow);
// 2型货车车流量
dcTollStationStatisticsData.setType2TruckFlow(type2TruckFlow);
// 3型货车车流量
dcTollStationStatisticsData.setType3TruckFlow(type3TruckFlow);
// 4型货车车流量
dcTollStationStatisticsData.setType4TruckFlow(type4TruckFlow);
// 5型货车车流量
dcTollStationStatisticsData.setType5TruckFlow(type5TruckFlow);
// 6型货车车流量
dcTollStationStatisticsData.setType6TruckFlow(type6TruckFlow);
// 1型专项作业车车流量
dcTollStationStatisticsData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow);
// 2型专项作业车车流量
dcTollStationStatisticsData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow);
// 3型专项作业车车流量
dcTollStationStatisticsData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow);
// 4型专项作业车车流量
dcTollStationStatisticsData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow);
// 5型专项作业车车流量
dcTollStationStatisticsData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow);
// 6型专项作业车车流量
dcTollStationStatisticsData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
return dcTollStationStatisticsData;
}
}

18
zc-business/src/main/java/com/zc/business/service/impl/OdsTollViuDataServiceImpl.java

@ -0,0 +1,18 @@
package com.zc.business.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.annotation.DataSource;
import com.ruoyi.common.enums.DataSourceType;
import com.zc.business.domain.OdsTollViuData;
import com.zc.business.mapper.OdsTollViuDataMapper;
import com.zc.business.service.IOdsTollViuDataService;
import org.springframework.stereotype.Service;
/**
* 门架牌识流水数据
*/
@Service
@DataSource(value = DataSourceType.SLAVE)//切换数据源
public class OdsTollViuDataServiceImpl extends ServiceImpl<OdsTollViuDataMapper, OdsTollViuData>
implements IOdsTollViuDataService {
}

5
zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java

@ -1,6 +1,5 @@
package com.zc.business.statistics.cache;
import com.zc.business.domain.DcTrafficSectionData;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
@ -16,7 +15,7 @@ import java.util.HashSet;
*/
@Getter
@Setter
public abstract class AbstractTrafficStatisticsCache {
public abstract class AbstractTrafficStatisticsCache<T> {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@ -44,7 +43,7 @@ public abstract class AbstractTrafficStatisticsCache {
/**
* 存储具体交通断面数据的列表
*/
private Collection<DcTrafficSectionData> data;
private Collection<T> data;
public AbstractTrafficStatisticsCache() {
this.data = new HashSet<>();

148
zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java

@ -0,0 +1,148 @@
package com.zc.business.statistics.cache.gantry;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
*
* 以天为单位的门架数据缓存类用于存储和管理门架统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class DailyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, DailyTrafficGantryStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = 26 * 60 * 60; // 缓存数据最长保留25小时
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 24 + 1 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private DailyTrafficGantryStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取或新建对应的缓存实例
DailyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 更新统计日期
dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY);
// 移除旧数据
instance.getData().remove(dcGantryStatisticsData);
// 添加数据
instance.getData().add(dcGantryStatisticsData);
}
/**
* 获取或创建对应设备与日期的DailyTrafficGantryStatisticsCache实例
*
* @param dcGantryStatisticsData 门架数据统计定义
* @return 对应的门架数据缓存实例
*/
private static DailyTrafficGantryStatisticsCache getInstance(DcGantryStatisticsData dcGantryStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryStatisticsData), k -> new DailyTrafficGantryStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期来生成一个唯一的缓存键</p>
*
* @param dcGantryStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的门架数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(DailyTrafficGantryStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为指定格式的字符串
*
* @param date 需要格式化的 Date 对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.formatDate(date);
}
}

152
zc-business/src/main/java/com/zc/business/statistics/cache/gantry/MonthlyTrafficGantryStatisticsCache.java

@ -0,0 +1,152 @@
package com.zc.business.statistics.cache.gantry;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
*
* 以月为单位的门架数据缓存类用于存储和管理门架统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class MonthlyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, MonthlyTrafficGantryStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (31 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 31 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private MonthlyTrafficGantryStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取或新建对应的缓存实例
MonthlyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH);
// 更新统计日期
dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH);
// 移除旧数据
instance.getData().remove(dcGantryStatisticsData);
// 添加数据
instance.getData().add(dcGantryStatisticsData);
}
/**
* 获取月门架信息缓存的实例
* <p>
* 根据传入的门架在一定周期内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcGantryStatisticsData 门架统计数据用于生成缓存键
* @return 缓存中的或新创建的月门架统计信息缓存实例
*/
private static MonthlyTrafficGantryStatisticsCache getInstance(DcGantryStatisticsData dcGantryStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryStatisticsData), k -> new MonthlyTrafficGantryStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcGantryStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期和访问类型作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(MonthlyTrafficGantryStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-MM-01" 格式的字符串
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.format(date, "yyyy-MM-01");
}
}

154
zc-business/src/main/java/com/zc/business/statistics/cache/gantry/QuarterlyTrafficGantryStatisticsCache.java

@ -0,0 +1,154 @@
package com.zc.business.statistics.cache.gantry;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 以季度为单位的门架数据缓存类用于存储和管理门架统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class QuarterlyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, QuarterlyTrafficGantryStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (3 * 31 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 3 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private QuarterlyTrafficGantryStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取或新建对应的缓存实例
QuarterlyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER);
// 更新统计日期
dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER);
// 移除旧数据
instance.getData().remove(dcGantryStatisticsData);
// 添加数据
instance.getData().add(dcGantryStatisticsData);
}
/**
* 获取季度门架统计信息缓存的实例
* <p>
* 根据传入的门架在一定周期季度内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcGantryStatisticsData 门架统计数据用于生成缓存键
* @return 缓存中的或新创建的季度门架统计信息缓存实例
*/
private static QuarterlyTrafficGantryStatisticsCache getInstance(DcGantryStatisticsData dcGantryStatisticsData) {
// 根据传入数据生成唯一键,并尝试从缓存中获取或创建新的缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryStatisticsData), k -> new QuarterlyTrafficGantryStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcGantryStatisticsData 门架统计数据对象包含门架编号统计日期和访问类型
* @return 缓存键格式为"门架编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(QuarterlyTrafficGantryStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-MM-01" 格式字符串
*
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串格式为 "yyyy-MM-01"
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.format(date, "yyyy-MM-01");
}
}

148
zc-business/src/main/java/com/zc/business/statistics/cache/gantry/YearlyTrafficGantryStatisticsCache.java

@ -0,0 +1,148 @@
package com.zc.business.statistics.cache.gantry;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 以年为单位的门架数据缓存类用于存储和管理门架统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class YearlyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, YearlyTrafficGantryStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (366 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 4 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private YearlyTrafficGantryStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取或新建对应的缓存实例
YearlyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.YEAR);
// 更新统计日期
dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.YEAR);
// 移除旧数据
instance.getData().remove(dcGantryStatisticsData);
// 添加数据
instance.getData().add(dcGantryStatisticsData);
}
/**
* 获取或创建对应设备与日期的YearlyTrafficSectionStatisticsCache实例
*
* @param DcGantryStatisticsData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static YearlyTrafficGantryStatisticsCache getInstance(DcGantryStatisticsData DcGantryStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(DcGantryStatisticsData), k -> new YearlyTrafficGantryStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期来生成一个唯一的缓存键</p>
*
* @param dcGantryStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) {
// 获取门架站点标识
String gantryCode = dcGantryStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(YearlyTrafficGantryStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-01-01" 格式的字符串
*
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为 "yyyy-01-01" 格式的字符串
return DateUtil.format(date, "yyyy-01-01");
}
}

21
zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java → zc-business/src/main/java/com/zc/business/statistics/cache/section/DailyTrafficSectionStatisticsCache.java

@ -1,9 +1,10 @@
package com.zc.business.statistics.cache;
package com.zc.business.statistics.cache.section;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
@ -18,11 +19,11 @@ import java.util.stream.Collectors;
*/
@Getter
@Setter
public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache {
public class DailyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache<DcTrafficSectionData> {
@Getter
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
private static final Map<String, DailyTrafficStatisticsCache> cache = new ConcurrentHashMap<>();
@Getter
private static final Map<String, DailyTrafficSectionStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = 25 * 60 * 60; // 缓存数据最长保留25小时
@ -31,7 +32,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
private static final int MAX_CAPACITY = 60/5 * (24 + 1) + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private DailyTrafficStatisticsCache() {
private DailyTrafficSectionStatisticsCache() {
}
/**
@ -41,7 +42,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
*/
public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 获取或新建对应的缓存实例
DailyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData);
DailyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
@ -77,14 +78,14 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
}
/**
* 获取或创建对应设备与日期的DcTrafficSectionDataCache实例
* 获取或创建对应设备与日期的DailyTrafficSectionStatisticsCache实例
*
* @param dcTrafficSectionData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static DailyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
private static DailyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new DailyTrafficStatisticsCache());
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new DailyTrafficSectionStatisticsCache());
}
/**
@ -111,7 +112,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(DailyTrafficStatisticsCache::isCacheItemExpire)
.filter(DailyTrafficSectionStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项

19
zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java → zc-business/src/main/java/com/zc/business/statistics/cache/section/MonthlyTrafficSectionStatisticsCache.java

@ -1,9 +1,10 @@
package com.zc.business.statistics.cache;
package com.zc.business.statistics.cache.section;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
@ -17,11 +18,11 @@ import java.util.stream.Collectors;
*/
@Getter
@Setter
public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache {
public class MonthlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache<DcTrafficSectionData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, MonthlyTrafficStatisticsCache> cache = new ConcurrentHashMap<>();
private static final Map<String, MonthlyTrafficSectionStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (31 * 24 + 1);
@ -31,7 +32,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach
// 私有构造函数,确保只能通过静态方法获取实例
private MonthlyTrafficStatisticsCache() {
private MonthlyTrafficSectionStatisticsCache() {
}
/**
@ -41,7 +42,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach
*/
public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 获取或新建对应的缓存实例
MonthlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData);
MonthlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
@ -77,14 +78,14 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach
}
/**
* 获取或创建对应设备与日期的DcTrafficSectionDataCache实例
* 获取或创建对应设备与日期的MonthlyTrafficSectionStatisticsCache实例
*
* @param dcTrafficSectionData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static MonthlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
private static MonthlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new MonthlyTrafficStatisticsCache());
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new MonthlyTrafficSectionStatisticsCache());
}
/**
@ -111,7 +112,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(MonthlyTrafficStatisticsCache::isCacheItemExpire)
.filter(MonthlyTrafficSectionStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项

20
zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java → zc-business/src/main/java/com/zc/business/statistics/cache/section/QuarterlyTrafficSectionStatisticsCache.java

@ -1,14 +1,14 @@
package com.zc.business.statistics.cache;
package com.zc.business.statistics.cache.section;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -20,11 +20,11 @@ import java.util.stream.Collectors;
*/
@Getter
@Setter
public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache {
public class QuarterlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache<DcTrafficSectionData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, QuarterlyTrafficStatisticsCache> cache = new ConcurrentHashMap<>();
private static final Map<String, QuarterlyTrafficSectionStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (3 * 31 * 24 + 1);
@ -34,7 +34,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa
// 私有构造函数,确保只能通过静态方法获取实例
private QuarterlyTrafficStatisticsCache() {
private QuarterlyTrafficSectionStatisticsCache() {
}
/**
@ -44,7 +44,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa
*/
public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 获取或新建对应的缓存实例
QuarterlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData);
QuarterlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
@ -80,14 +80,14 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa
}
/**
* 获取或创建对应设备与日期的DcTrafficSectionDataCache实例
* 获取或创建对应设备与日期的QuarterlyTrafficSectionStatisticsCache实例
*
* @param dcTrafficSectionData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static QuarterlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
private static QuarterlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new QuarterlyTrafficStatisticsCache());
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new QuarterlyTrafficSectionStatisticsCache());
}
/**
@ -114,7 +114,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(QuarterlyTrafficStatisticsCache::isCacheItemExpire)
.filter(QuarterlyTrafficSectionStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项

20
zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java → zc-business/src/main/java/com/zc/business/statistics/cache/section/YearlyTrafficSectionStatisticsCache.java

@ -1,14 +1,14 @@
package com.zc.business.statistics.cache;
package com.zc.business.statistics.cache.section;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@ -20,11 +20,11 @@ import java.util.stream.Collectors;
*/
@Getter
@Setter
public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache {
public class YearlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache<DcTrafficSectionData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, YearlyTrafficStatisticsCache> cache = new ConcurrentHashMap<>();
private static final Map<String, YearlyTrafficSectionStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (366 * 24 + 1);
@ -34,7 +34,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
// 私有构造函数,确保只能通过静态方法获取实例
private YearlyTrafficStatisticsCache() {
private YearlyTrafficSectionStatisticsCache() {
}
/**
@ -44,7 +44,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
*/
public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) {
// 获取或新建对应的缓存实例
YearlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData);
YearlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
@ -80,14 +80,14 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
}
/**
* 获取或创建对应设备与日期的DcTrafficSectionDataCache实例
* 获取或创建对应设备与日期的YearlyTrafficSectionStatisticsCache实例
*
* @param dcTrafficSectionData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static YearlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
private static YearlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new YearlyTrafficStatisticsCache());
return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new YearlyTrafficSectionStatisticsCache());
}
/**
@ -114,7 +114,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(YearlyTrafficStatisticsCache::isCacheItemExpire)
.filter(YearlyTrafficSectionStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项

148
zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/DailyTrafficTollStationStatisticsCache.java

@ -0,0 +1,148 @@
package com.zc.business.statistics.cache.tollstation;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
*
* 以天为单位的交通收费站站点数据缓存类用于存储和管理收费站站点统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class DailyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache<DcTollStationStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, DailyTrafficTollStationStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = 26 * 60 * 60; // 缓存数据最长保留25小时
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 24 + 1 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private DailyTrafficTollStationStatisticsCache() {
}
/**
* 添加收费站站点数据到缓存中
*
* @param dcTollStationStatisticsData 待添加的收费站站点数据
*/
public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取或新建对应的缓存实例
DailyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcTollStationStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 更新统计日期
dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY);
// 移除旧数据
instance.getData().remove(dcTollStationStatisticsData);
// 添加数据
instance.getData().add(dcTollStationStatisticsData);
}
/**
* 获取或创建对应设备与日期的DailyTrafficTollStationStatisticsCache实例
*
* @param dcTollStationStatisticsData 收费站站点数据统计定义
* @return 对应的收费站站点数据缓存实例
*/
private static DailyTrafficTollStationStatisticsCache getInstance(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTollStationStatisticsData), k -> new DailyTrafficTollStationStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合收费站编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcTollStationStatisticsData 收费站统计数据对象包含收费站编号统计日期和访问类型
* @return 缓存键格式为"收费站编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取收费站站点标识
String tollStationCode = dcTollStationStatisticsData.getTollStationCode();
// 格式化统计日期
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
// 组合收费站编号、格式化后的统计日期和访问类型作为缓存键
return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType();
}
/**
* 清除所有过期的收费站站点数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(DailyTrafficTollStationStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为指定格式的字符串
*
* @param date 需要格式化的 Date 对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.formatDate(date);
}
}

152
zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/MonthlyTrafficTollStationStatisticsCache.java

@ -0,0 +1,152 @@
package com.zc.business.statistics.cache.tollstation;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
*
* 以月为单位的交通收费站站点数据缓存类用于存储和管理收费站站点统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class MonthlyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache<DcTollStationStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, MonthlyTrafficTollStationStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (31 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 31 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private MonthlyTrafficTollStationStatisticsCache() {
}
/**
* 添加收费站站点数据到缓存中
*
* @param dcTollStationStatisticsData 待添加的收费站站点数据
*/
public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取或新建对应的缓存实例
MonthlyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcTollStationStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH);
// 更新统计日期
dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH);
// 移除旧数据
instance.getData().remove(dcTollStationStatisticsData);
// 添加数据
instance.getData().add(dcTollStationStatisticsData);
}
/**
* 获取月交通收费站统计信息缓存的实例
* <p>
* 根据传入的收费站在一定周期内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcTollStationStatisticsData 收费站统计数据用于生成缓存键
* @return 缓存中的或新创建的月交通收费站统计信息缓存实例
*/
private static MonthlyTrafficTollStationStatisticsCache getInstance(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTollStationStatisticsData), k -> new MonthlyTrafficTollStationStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合收费站编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcTollStationStatisticsData 收费站统计数据对象包含收费站编号统计日期和访问类型
* @return 缓存键格式为"收费站编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取收费站站点标识
String tollStationCode = dcTollStationStatisticsData.getTollStationCode();
// 格式化统计日期
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
// 组合收费站编号、格式化后的统计日期和访问类型作为缓存键
return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType();
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(MonthlyTrafficTollStationStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-MM-01" 格式的字符串
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.format(date, "yyyy-MM-01");
}
}

154
zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/QuarterlyTrafficTollStationStatisticsCache.java

@ -0,0 +1,154 @@
package com.zc.business.statistics.cache.tollstation;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 以季度为单位的交通收费站站点数据缓存类用于存储和管理收费站站点统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class QuarterlyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache<DcTollStationStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, QuarterlyTrafficTollStationStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (3 * 31 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 3 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private QuarterlyTrafficTollStationStatisticsCache() {
}
/**
* 添加收费站站点数据到缓存中
*
* @param dcTollStationStatisticsData 待添加的收费站站点数据
*/
public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取或新建对应的缓存实例
QuarterlyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcTollStationStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER);
// 更新统计日期
dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER);
// 移除旧数据
instance.getData().remove(dcTollStationStatisticsData);
// 添加数据
instance.getData().add(dcTollStationStatisticsData);
}
/**
* 获取季度交通收费站统计信息缓存的实例
* <p>
* 根据传入的收费站在一定周期季度内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcTollStationStatisticsData 收费站统计数据用于生成缓存键
* @return 缓存中的或新创建的季度交通收费站统计信息缓存实例
*/
private static QuarterlyTrafficTollStationStatisticsCache getInstance(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 根据传入数据生成唯一键,并尝试从缓存中获取或创建新的缓存实例
return cache.computeIfAbsent(generateCacheKey(dcTollStationStatisticsData), k -> new QuarterlyTrafficTollStationStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合收费站编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcTollStationStatisticsData 收费站统计数据对象包含收费站编号统计日期和访问类型
* @return 缓存键格式为"收费站编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取收费站站点标识
String tollStationCode = dcTollStationStatisticsData.getTollStationCode();
// 格式化统计日期
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
// 组合收费站编号、格式化后的统计日期和访问类型作为缓存键
return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType();
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(QuarterlyTrafficTollStationStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-MM-01" 格式字符串
*
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串格式为 "yyyy-MM-01"
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为指定格式的字符串
return DateUtil.format(date, "yyyy-MM-01");
}
}

148
zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/YearlyTrafficTollStationStatisticsCache.java

@ -0,0 +1,148 @@
package com.zc.business.statistics.cache.tollstation;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import lombok.Getter;
import lombok.Setter;
import java.util.Date;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 以年为单位的交通收费站站点数据缓存类用于存储和管理收费站站点统计数据同时提供了数据缓存的有效性管理和清理功能
* @author xiepufeng
*/
@Getter
@Setter
public class YearlyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache<DcTollStationStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, YearlyTrafficTollStationStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (366 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 4 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private YearlyTrafficTollStationStatisticsCache() {
}
/**
* 添加收费站站点数据到缓存中
*
* @param dcTollStationStatisticsData 待添加的收费站站点数据
*/
public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取或新建对应的缓存实例
YearlyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcTollStationStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.YEAR);
// 更新统计日期
dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.YEAR);
// 移除旧数据
instance.getData().remove(dcTollStationStatisticsData);
// 添加数据
instance.getData().add(dcTollStationStatisticsData);
}
/**
* 获取或创建对应设备与日期的YearlyTrafficSectionStatisticsCache实例
*
* @param DcTollStationStatisticsData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static YearlyTrafficTollStationStatisticsCache getInstance(DcTollStationStatisticsData DcTollStationStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(DcTollStationStatisticsData), k -> new YearlyTrafficTollStationStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合收费站编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcTollStationStatisticsData 收费站统计数据对象包含收费站编号统计日期和访问类型
* @return 缓存键格式为"收费站编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) {
// 获取收费站站点标识
String tollStationCode = dcTollStationStatisticsData.getTollStationCode();
// 格式化统计日期
String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate());
// 组合收费站编号、格式化后的统计日期和访问类型作为缓存键
return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType();
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(YearlyTrafficTollStationStatisticsCache::isCacheItemExpire)
.collect(Collectors.toSet());
// 安全地从缓存中删除这些过期项
keysToRemove.forEach(cache::remove);
}
/**
* 检查给定缓存键所对应的缓存项是否已经过期
*
* @param key 缓存key
* @return 如果已过期则返回true否则返回false
*/
private static boolean isCacheItemExpire(String key) {
Date lastAddedTime = cache.get(key).getLastAddedTime();
Date currentTime = DateUtil.date();
long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND);
return betweenSecond > MAX_CACHE_TIME;
}
/**
* Date 类型的日期格式化为 "yyyy-01-01" 格式的字符串
*
* @param date 需要格式化的日期对象
* @return 格式化后的日期字符串
*/
private static String formatDate(Date date) {
// 使用 DateUtil 工具类将 date 格式化为 "yyyy-01-01" 格式的字符串
return DateUtil.format(date, "yyyy-01-01");
}
}

241
zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java

@ -0,0 +1,241 @@
package com.zc.business.statistics.handler;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcGantryStatisticsDataMapper;
import com.zc.business.service.IDcGantryStatisticsDataService;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import com.zc.business.statistics.cache.gantry.DailyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.MonthlyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.QuarterlyTrafficGantryStatisticsCache;
import com.zc.business.statistics.cache.gantry.YearlyTrafficGantryStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
/**
* TrafficGantryStatistics类用于处理门架的统计数据
* @author xiepufeng
*/
@Component
public class TrafficGantryStatistics {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private IDcGantryStatisticsDataService dcGantryStatisticsDataService;
@Resource
private DcGantryStatisticsDataMapper dcGantryStatisticsDataMapper;
/**
* 定义每小时第20分钟执行的任务用于清除过期缓存数据并将缓存中的数据整合后保存至数据库
*/
@Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
public void performHourlyCleanupAndPersist() {
// 添加日门架数据到缓存中
dcGantryStatisticsDataService.lastHourData().forEach(DailyTrafficGantryStatisticsCache::addCacheData);
// 清除已过期的缓存数据
DailyTrafficGantryStatisticsCache.clearExpiredData();
MonthlyTrafficGantryStatisticsCache.clearExpiredData();
QuarterlyTrafficGantryStatisticsCache.clearExpiredData();
YearlyTrafficGantryStatisticsCache.clearExpiredData();
// 整合缓存数据并保存至数据库
// 将缓存中的数据按日统计后保存至数据库
// 添加月门架数据到缓存中
persistAggregatedData(DailyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficGantryStatisticsCache::addCacheData);
// 将缓存中的数据按月统计后保存至数据库
// 添加季度门架数据到缓存中
persistAggregatedData(MonthlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficGantryStatisticsCache::addCacheData);
// 将缓存中的数据按季度统计后保存至数据库
// 添加年门架数据到缓存中
persistAggregatedData(QuarterlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficGantryStatisticsCache::addCacheData);
// 将缓存中的数据按年统计后保存至数据库
persistAggregatedData(YearlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {});
}
/**
* 将缓存中的数据统计后保存至数据库
*/
public void persistAggregatedData(
Map<String, ? extends AbstractTrafficStatisticsCache<DcGantryStatisticsData>> cache,
TrafficDataPeriodTypeEnum periodType,
Consumer<DcGantryStatisticsData> consumer
) {
for (AbstractTrafficStatisticsCache<DcGantryStatisticsData> data : cache.values()) {
// 如果数据已经存储过,则跳过此次处理
if (data.isStored()) {
continue;
}
DcGantryStatisticsData aggregatedData = trafficStatistics(data.getData(), periodType);
if (dcGantryStatisticsDataMapper.insertOrUpdate(aggregatedData)) {
// 设置数据已存储状态
data.setStored(true);
// 调用回调函数
consumer.accept(aggregatedData);
}
}
}
/**
* 对给定的门架统计数据集合进行汇总处理
*
* @param dataCollection 门架统计数据的集合不可为null或空
* @param trafficDataPeriodType 统计数据的时段类型例如小时月等
* @return 汇总后的门架统计数据对象如果输入数据为空则返回null
*/
public DcGantryStatisticsData trafficStatistics(Collection<DcGantryStatisticsData> dataCollection, TrafficDataPeriodTypeEnum trafficDataPeriodType) {
// 判断输入数据是否为空
if (CollectionUtils.isEmpty(dataCollection)) {
return null;
}
// 创建一个汇总统计用的对象
DcGantryStatisticsData aggregatedData = new DcGantryStatisticsData();
// 初始化车流量总和
int trafficVolume = 0;
// 初始化1型客车车流量
int type1PassengerFlow = 0;
// 初始化2型客车车流量
int type2PassengerFlow = 0;
// 初始化3型客车车流量
int type3PassengerFlow = 0;
// 初始化4型客车车流量
int type4PassengerFlow = 0;
// 初始化1型货车车流量
int type1TruckFlow = 0;
// 初始化2型货车车流量
int type2TruckFlow = 0;
// 初始化3型货车车流量
int type3TruckFlow = 0;
// 初始化4型货车车流量
int type4TruckFlow = 0;
// 初始化5型货车车流量
int type5TruckFlow = 0;
// 初始化6型货车车流量
int type6TruckFlow = 0;
// 初始化1型专项作业车车流量
int type1SpecialVehicleFlow = 0;
// 初始化2型专项作业车车流量
int type2SpecialVehicleFlow = 0;
// 初始化3型专项作业车车流量
int type3SpecialVehicleFlow = 0;
// 初始化4型专项作业车车流量
int type4SpecialVehicleFlow = 0;
// 初始化5型专项作业车车流量
int type5SpecialVehicleFlow = 0;
// 初始化6型专项作业车车流量
int type6SpecialVehicleFlow = 0;
// 遍历数据集合,累加各类车辆的车流量
for (DcGantryStatisticsData data: dataCollection) {
// 累加车流量
trafficVolume += data.getTrafficVolume();
// 累加1型客车车流量
type1PassengerFlow += data.getType1PassengerFlow();
// 累加2型客车车流量
type2PassengerFlow += data.getType2PassengerFlow();
// 累加3型客车车流量
type3PassengerFlow += data.getType3PassengerFlow();
// 累加4型客车车流量
type4PassengerFlow += data.getType4PassengerFlow();
// 累加1型货车车流量
type1TruckFlow += data.getType1TruckFlow();
// 累加2型货车车流量
type2TruckFlow += data.getType2TruckFlow();
// 累加3型货车车流量
type3TruckFlow += data.getType3TruckFlow();
// 累加4型货车车流量
type4TruckFlow += data.getType4TruckFlow();
// 累加5型货车车流量
type5TruckFlow += data.getType5TruckFlow();
// 累加6型货车车流量
type6TruckFlow += data.getType6TruckFlow();
// 累加1型专项作业车车流量
type1SpecialVehicleFlow += data.getType1SpecialVehicleFlow();
// 累加2型专项作业车车流量
type2SpecialVehicleFlow += data.getType2SpecialVehicleFlow();
// 累加3型专项作业车车流量
type3SpecialVehicleFlow += data.getType3SpecialVehicleFlow();
// 累加4型专项作业车车流量
type4SpecialVehicleFlow += data.getType4SpecialVehicleFlow();
// 累加5型专项作业车车流量
type5SpecialVehicleFlow += data.getType5SpecialVehicleFlow();
// 累加6型专项作业车车流量
type6SpecialVehicleFlow += data.getType6SpecialVehicleFlow();
}
// 使用第一个数据项的信息填充汇总统计对象的基本属性
DcGantryStatisticsData firstDcTrafficSectionData = dataCollection.iterator().next();
// 设置门架标识
aggregatedData.setGantryCode(firstDcTrafficSectionData.getGantryCode());
// 设置统计时间
aggregatedData.setStatisticalDate(firstDcTrafficSectionData.getStatisticalDate(), trafficDataPeriodType);
// 上报时间
aggregatedData.setReportTime(firstDcTrafficSectionData.getReportTime());
// 时段类型
aggregatedData.setPeriodType(trafficDataPeriodType);
// 车流量
aggregatedData.setTrafficVolume(trafficVolume);
// 1型客车车流量
aggregatedData.setType1PassengerFlow(type1PassengerFlow);
// 2型客车车流量
aggregatedData.setType2PassengerFlow(type2PassengerFlow);
// 3型客车车流量
aggregatedData.setType3PassengerFlow(type3PassengerFlow);
// 4型客车车流量
aggregatedData.setType4PassengerFlow(type4PassengerFlow);
// 1型货车车流量
aggregatedData.setType1TruckFlow(type1TruckFlow);
// 2型货车车流量
aggregatedData.setType2TruckFlow(type2TruckFlow);
// 3型货车车流量
aggregatedData.setType3TruckFlow(type3TruckFlow);
// 4型货车车流量
aggregatedData.setType4TruckFlow(type4TruckFlow);
// 5型货车车流量
aggregatedData.setType5TruckFlow(type5TruckFlow);
// 6型货车车流量
aggregatedData.setType6TruckFlow(type6TruckFlow);
// 1型专项作业车车流量
aggregatedData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow);
// 2型专项作业车车流量
aggregatedData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow);
// 3型专项作业车车流量
aggregatedData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow);
// 4型专项作业车车流量
aggregatedData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow);
// 5型专项作业车车流量
aggregatedData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow);
// 6型专项作业车车流量
aggregatedData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
// 生成主键
aggregatedData.generateUniqueId();
return aggregatedData;
}
}

2
zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java → zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionAnalysis.java

@ -26,7 +26,7 @@ import java.util.stream.Collectors;
* @author xiepufeng
*/
@Component
public class TrafficAnalysis {
public class TrafficSectionAnalysis {
@Resource
private RedisCache redisCache;

31
zc-business/src/main/java/com/zc/business/statistics/handler/TrafficStatistics.java → zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionStatistics.java

@ -11,7 +11,8 @@ import com.zc.business.domain.DcDevice;
import com.zc.business.domain.DcTrafficSectionData;
import com.zc.business.enums.*;
import com.zc.business.mapper.DcTrafficSectionDataMapper;
import com.zc.business.statistics.cache.*;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import com.zc.business.statistics.cache.section.*;
import com.zc.common.core.httpclient.exception.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -25,13 +26,13 @@ import java.util.*;
import java.util.function.Consumer;
/**
* 交通数据统计处理类
* 交通断面数据统计处理类
*
* 该类提供了一系列方法用于对交通数据进行统计分析包括车流量总和平均车速等
* @author xiepufeng
*/
@Component
public class TrafficStatistics {
public class TrafficSectionStatistics {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@ -51,22 +52,22 @@ public class TrafficStatistics {
@Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
public void performHourlyCleanupAndPersist() {
// 清除已过期的缓存数据
DailyTrafficStatisticsCache.clearExpiredData();
MonthlyTrafficStatisticsCache.clearExpiredData();
QuarterlyTrafficStatisticsCache.clearExpiredData();
YearlyTrafficStatisticsCache.clearExpiredData();
DailyTrafficSectionStatisticsCache.clearExpiredData();
MonthlyTrafficSectionStatisticsCache.clearExpiredData();
QuarterlyTrafficSectionStatisticsCache.clearExpiredData();
YearlyTrafficSectionStatisticsCache.clearExpiredData();
// 整合缓存数据并保存至数据库
// 将缓存中的数据按日统计后保存至数据库
// 添加月交通断面数据到缓存中
persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData);
persistAggregatedData(DailyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficSectionStatisticsCache::addCacheData);
// 将缓存中的数据按月统计后保存至数据库
// 添加季度交通断面数据到缓存中
persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData);
persistAggregatedData(MonthlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficSectionStatisticsCache::addCacheData);
// 将缓存中的数据按季度统计后保存至数据库
// 添加年交通断面数据到缓存中
persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData);
persistAggregatedData(QuarterlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficSectionStatisticsCache::addCacheData);
// 将缓存中的数据按年统计后保存至数据库
persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {});
persistAggregatedData(YearlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {});
}
@ -160,7 +161,7 @@ public class TrafficStatistics {
// 如果转换结果非空且不为空列表,则将转换后的数据添加到缓存中
if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) {
// 将转换后的数据添加到缓存中
dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData);
dcTrafficSectionDataList.forEach(DailyTrafficSectionStatisticsCache::addCacheData);
}
});
@ -398,11 +399,11 @@ public class TrafficStatistics {
* 将缓存中的数据统计后保存至数据库
*/
public void persistAggregatedData(
Map<String, ? extends AbstractTrafficStatisticsCache> cache,
Map<String, ? extends AbstractTrafficStatisticsCache<DcTrafficSectionData>> cache,
TrafficDataPeriodTypeEnum periodType,
Consumer<DcTrafficSectionData> consumer
) {
for (AbstractTrafficStatisticsCache data : cache.values()) {
for (AbstractTrafficStatisticsCache<DcTrafficSectionData> data : cache.values()) {
// 如果数据已经存储过,则跳过此次处理
if (data.isStored()) {
continue;
@ -436,7 +437,7 @@ public class TrafficStatistics {
// 初始化车流量总和
int trafficVolume = 0;
// 初始化大车流量
// 初始化大车流量总和
int largeTrafficVolume = 0;
// 初始化计算平均车速所需的分子部分
double numerator = 0;

243
zc-business/src/main/java/com/zc/business/statistics/handler/TrafficTollStationStatistics.java

@ -0,0 +1,243 @@
package com.zc.business.statistics.handler;
import com.zc.business.domain.DcTollStationStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcTollStationStatisticsDataMapper;
import com.zc.business.service.IDcTollStationStatisticsDataService;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import com.zc.business.statistics.cache.tollstation.DailyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.MonthlyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.QuarterlyTrafficTollStationStatisticsCache;
import com.zc.business.statistics.cache.tollstation.YearlyTrafficTollStationStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.Map;
import java.util.function.Consumer;
/**
* TrafficTollStationStatistics类用于处理交通收费站的统计数据
* @author xiepufeng
*/
@Component
public class TrafficTollStationStatistics {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private IDcTollStationStatisticsDataService dcTollStationStatisticsDataService;
@Resource
private DcTollStationStatisticsDataMapper dcTollStationStatisticsDataMapper;
/**
* 定义每小时第20分钟执行的任务用于清除过期缓存数据并将缓存中的数据整合后保存至数据库
*/
@Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
public void performHourlyCleanupAndPersist() {
// 添加日收费站站点数据到缓存中
dcTollStationStatisticsDataService.lastHourData().forEach(DailyTrafficTollStationStatisticsCache::addCacheData);
// 清除已过期的缓存数据
DailyTrafficTollStationStatisticsCache.clearExpiredData();
MonthlyTrafficTollStationStatisticsCache.clearExpiredData();
QuarterlyTrafficTollStationStatisticsCache.clearExpiredData();
YearlyTrafficTollStationStatisticsCache.clearExpiredData();
// 整合缓存数据并保存至数据库
// 将缓存中的数据按日统计后保存至数据库
// 添加月收费站站点数据到缓存中
persistAggregatedData(DailyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficTollStationStatisticsCache::addCacheData);
// 将缓存中的数据按月统计后保存至数据库
// 添加季度收费站站点数据到缓存中
persistAggregatedData(MonthlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficTollStationStatisticsCache::addCacheData);
// 将缓存中的数据按季度统计后保存至数据库
// 添加年收费站站点数据到缓存中
persistAggregatedData(QuarterlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficTollStationStatisticsCache::addCacheData);
// 将缓存中的数据按年统计后保存至数据库
persistAggregatedData(YearlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {});
}
/**
* 将缓存中的数据统计后保存至数据库
*/
public void persistAggregatedData(
Map<String, ? extends AbstractTrafficStatisticsCache<DcTollStationStatisticsData>> cache,
TrafficDataPeriodTypeEnum periodType,
Consumer<DcTollStationStatisticsData> consumer
) {
for (AbstractTrafficStatisticsCache<DcTollStationStatisticsData> data : cache.values()) {
// 如果数据已经存储过,则跳过此次处理
if (data.isStored()) {
continue;
}
DcTollStationStatisticsData aggregatedData = trafficStatistics(data.getData(), periodType);
if (dcTollStationStatisticsDataMapper.insertOrUpdate(aggregatedData)) {
// 设置数据已存储状态
data.setStored(true);
// 调用回调函数
consumer.accept(aggregatedData);
}
}
}
/**
* 对给定的收费站点统计数据集合进行汇总处理
*
* @param dataCollection 收费站点统计数据的集合不可为null或空
* @param trafficDataPeriodType 统计数据的时段类型例如小时月等
* @return 汇总后的收费站点统计数据对象如果输入数据为空则返回null
*/
public DcTollStationStatisticsData trafficStatistics(Collection<DcTollStationStatisticsData> dataCollection, TrafficDataPeriodTypeEnum trafficDataPeriodType) {
// 判断输入数据是否为空
if (CollectionUtils.isEmpty(dataCollection)) {
return null;
}
// 创建一个汇总统计用的对象
DcTollStationStatisticsData aggregatedData = new DcTollStationStatisticsData();
// 初始化车流量总和
int trafficVolume = 0;
// 初始化1型客车车流量
int type1PassengerFlow = 0;
// 初始化2型客车车流量
int type2PassengerFlow = 0;
// 初始化3型客车车流量
int type3PassengerFlow = 0;
// 初始化4型客车车流量
int type4PassengerFlow = 0;
// 初始化1型货车车流量
int type1TruckFlow = 0;
// 初始化2型货车车流量
int type2TruckFlow = 0;
// 初始化3型货车车流量
int type3TruckFlow = 0;
// 初始化4型货车车流量
int type4TruckFlow = 0;
// 初始化5型货车车流量
int type5TruckFlow = 0;
// 初始化6型货车车流量
int type6TruckFlow = 0;
// 初始化1型专项作业车车流量
int type1SpecialVehicleFlow = 0;
// 初始化2型专项作业车车流量
int type2SpecialVehicleFlow = 0;
// 初始化3型专项作业车车流量
int type3SpecialVehicleFlow = 0;
// 初始化4型专项作业车车流量
int type4SpecialVehicleFlow = 0;
// 初始化5型专项作业车车流量
int type5SpecialVehicleFlow = 0;
// 初始化6型专项作业车车流量
int type6SpecialVehicleFlow = 0;
// 遍历数据集合,累加各类车辆的车流量
for (DcTollStationStatisticsData data: dataCollection) {
// 累加车流量
trafficVolume += data.getTrafficVolume();
// 累加1型客车车流量
type1PassengerFlow += data.getType1PassengerFlow();
// 累加2型客车车流量
type2PassengerFlow += data.getType2PassengerFlow();
// 累加3型客车车流量
type3PassengerFlow += data.getType3PassengerFlow();
// 累加4型客车车流量
type4PassengerFlow += data.getType4PassengerFlow();
// 累加1型货车车流量
type1TruckFlow += data.getType1TruckFlow();
// 累加2型货车车流量
type2TruckFlow += data.getType2TruckFlow();
// 累加3型货车车流量
type3TruckFlow += data.getType3TruckFlow();
// 累加4型货车车流量
type4TruckFlow += data.getType4TruckFlow();
// 累加5型货车车流量
type5TruckFlow += data.getType5TruckFlow();
// 累加6型货车车流量
type6TruckFlow += data.getType6TruckFlow();
// 累加1型专项作业车车流量
type1SpecialVehicleFlow += data.getType1SpecialVehicleFlow();
// 累加2型专项作业车车流量
type2SpecialVehicleFlow += data.getType2SpecialVehicleFlow();
// 累加3型专项作业车车流量
type3SpecialVehicleFlow += data.getType3SpecialVehicleFlow();
// 累加4型专项作业车车流量
type4SpecialVehicleFlow += data.getType4SpecialVehicleFlow();
// 累加5型专项作业车车流量
type5SpecialVehicleFlow += data.getType5SpecialVehicleFlow();
// 累加6型专项作业车车流量
type6SpecialVehicleFlow += data.getType6SpecialVehicleFlow();
}
// 使用第一个数据项的信息填充汇总统计对象的基本属性
DcTollStationStatisticsData firstDcTrafficSectionData = dataCollection.iterator().next();
// 设置收费站站点标识
aggregatedData.setTollStationCode(firstDcTrafficSectionData.getTollStationCode());
// 设置统计时间
aggregatedData.setStatisticalDate(firstDcTrafficSectionData.getStatisticalDate(), trafficDataPeriodType);
// 上报时间
aggregatedData.setReportTime(firstDcTrafficSectionData.getReportTime());
// 时段类型
aggregatedData.setPeriodType(trafficDataPeriodType);
// 出入类型
aggregatedData.setAccessType(firstDcTrafficSectionData.getAccessType());
// 车流量
aggregatedData.setTrafficVolume(trafficVolume);
// 1型客车车流量
aggregatedData.setType1PassengerFlow(type1PassengerFlow);
// 2型客车车流量
aggregatedData.setType2PassengerFlow(type2PassengerFlow);
// 3型客车车流量
aggregatedData.setType3PassengerFlow(type3PassengerFlow);
// 4型客车车流量
aggregatedData.setType4PassengerFlow(type4PassengerFlow);
// 1型货车车流量
aggregatedData.setType1TruckFlow(type1TruckFlow);
// 2型货车车流量
aggregatedData.setType2TruckFlow(type2TruckFlow);
// 3型货车车流量
aggregatedData.setType3TruckFlow(type3TruckFlow);
// 4型货车车流量
aggregatedData.setType4TruckFlow(type4TruckFlow);
// 5型货车车流量
aggregatedData.setType5TruckFlow(type5TruckFlow);
// 6型货车车流量
aggregatedData.setType6TruckFlow(type6TruckFlow);
// 1型专项作业车车流量
aggregatedData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow);
// 2型专项作业车车流量
aggregatedData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow);
// 3型专项作业车车流量
aggregatedData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow);
// 4型专项作业车车流量
aggregatedData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow);
// 5型专项作业车车流量
aggregatedData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow);
// 6型专项作业车车流量
aggregatedData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
// 生成主键
aggregatedData.generateUniqueId();
return aggregatedData;
}
}

91
zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml

@ -0,0 +1,91 @@
<?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.zc.business.mapper.DcGantryStatisticsDataMapper">
<!-- 插入或更新交通路段数据 -->
<insert id="insertOrUpdate" parameterType="com.zc.business.domain.DcGantryStatisticsData">
INSERT INTO
dc_gantry_statistics_data
(
id,
gantry_code,
statistical_date,
traffic_volume,
period_type,
create_time,
type1_passenger_flow,
type2_passenger_flow,
type3_passenger_flow,
type4_passenger_flow,
type1_truck_flow,
type2_truck_flow,
type3_truck_flow,
type4_truck_flow,
type5_truck_flow,
type6_truck_flow,
type1_special_vehicle_flow,
type2_special_vehicle_flow,
type3_special_vehicle_flow,
type4_special_vehicle_flow,
type5_special_vehicle_flow,
type6_special_vehicle_flow
)
VALUES
(
#{id},
#{gantryCode},
#{statisticalDate},
#{trafficVolume},
#{periodType},
NOW(),
#{type1PassengerFlow},
#{type2PassengerFlow},
#{type3PassengerFlow},
#{type4PassengerFlow},
#{type1TruckFlow},
#{type2TruckFlow},
#{type3TruckFlow},
#{type4TruckFlow},
#{type5TruckFlow},
#{type6TruckFlow},
#{type1SpecialVehicleFlow},
#{type2SpecialVehicleFlow},
#{type3SpecialVehicleFlow},
#{type4SpecialVehicleFlow},
#{type5SpecialVehicleFlow},
#{type6SpecialVehicleFlow}
)
ON DUPLICATE KEY UPDATE
gantry_code = VALUES(gantry_code),
statistical_date = VALUES(statistical_date),
traffic_volume = VALUES(traffic_volume),
period_type = VALUES(period_type),
update_time = NOW(),
type1_passenger_flow = VALUES(type1_passenger_flow),
type2_passenger_flow = VALUES(type2_passenger_flow),
type3_passenger_flow = VALUES(type3_passenger_flow),
type4_passenger_flow = VALUES(type4_passenger_flow),
type1_truck_flow = VALUES(type1_truck_flow),
type2_truck_flow = VALUES(type2_truck_flow),
type3_truck_flow = VALUES(type3_truck_flow),
type4_truck_flow = VALUES(type4_truck_flow),
type5_truck_flow = VALUES(type5_truck_flow),
type6_truck_flow = VALUES(type6_truck_flow),
type1_special_vehicle_flow = VALUES(type1_special_vehicle_flow),
type2_special_vehicle_flow = VALUES(type2_special_vehicle_flow),
type3_special_vehicle_flow = VALUES(type3_special_vehicle_flow),
type4_special_vehicle_flow = VALUES(type4_special_vehicle_flow),
type5_special_vehicle_flow = VALUES(type5_special_vehicle_flow),
type6_special_vehicle_flow = VALUES(type6_special_vehicle_flow)
</insert>
<select id="getMaxStatisticalDate" resultType="java.util.Date">
SELECT
MAX(statistical_date)
FROM
dc_gantry_statistics_data
</select>
</mapper>

94
zc-business/src/main/resources/mapper/business/DcTollStationStatisticsDataMapper.xml

@ -0,0 +1,94 @@
<?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.zc.business.mapper.DcTollStationStatisticsDataMapper">
<!-- 插入或更新交通路段数据 -->
<insert id="insertOrUpdate" parameterType="com.zc.business.domain.DcTollStationStatisticsData">
INSERT INTO
dc_toll_station_statistics_data
(
id,
toll_station_code,
statistical_date,
traffic_volume,
period_type,
access_type,
create_time,
type1_passenger_flow,
type2_passenger_flow,
type3_passenger_flow,
type4_passenger_flow,
type1_truck_flow,
type2_truck_flow,
type3_truck_flow,
type4_truck_flow,
type5_truck_flow,
type6_truck_flow,
type1_special_vehicle_flow,
type2_special_vehicle_flow,
type3_special_vehicle_flow,
type4_special_vehicle_flow,
type5_special_vehicle_flow,
type6_special_vehicle_flow
)
VALUES
(
#{id},
#{tollStationCode},
#{statisticalDate},
#{trafficVolume},
#{periodType},
#{accessType},
NOW(),
#{type1PassengerFlow},
#{type2PassengerFlow},
#{type3PassengerFlow},
#{type4PassengerFlow},
#{type1TruckFlow},
#{type2TruckFlow},
#{type3TruckFlow},
#{type4TruckFlow},
#{type5TruckFlow},
#{type6TruckFlow},
#{type1SpecialVehicleFlow},
#{type2SpecialVehicleFlow},
#{type3SpecialVehicleFlow},
#{type4SpecialVehicleFlow},
#{type5SpecialVehicleFlow},
#{type6SpecialVehicleFlow}
)
ON DUPLICATE KEY UPDATE
toll_station_code = VALUES(toll_station_code),
statistical_date = VALUES(statistical_date),
traffic_volume = VALUES(traffic_volume),
period_type = VALUES(period_type),
access_type = VALUES(access_type),
update_time = NOW(),
type1_passenger_flow = VALUES(type1_passenger_flow),
type2_passenger_flow = VALUES(type2_passenger_flow),
type3_passenger_flow = VALUES(type3_passenger_flow),
type4_passenger_flow = VALUES(type4_passenger_flow),
type1_truck_flow = VALUES(type1_truck_flow),
type2_truck_flow = VALUES(type2_truck_flow),
type3_truck_flow = VALUES(type3_truck_flow),
type4_truck_flow = VALUES(type4_truck_flow),
type5_truck_flow = VALUES(type5_truck_flow),
type6_truck_flow = VALUES(type6_truck_flow),
type1_special_vehicle_flow = VALUES(type1_special_vehicle_flow),
type2_special_vehicle_flow = VALUES(type2_special_vehicle_flow),
type3_special_vehicle_flow = VALUES(type3_special_vehicle_flow),
type4_special_vehicle_flow = VALUES(type4_special_vehicle_flow),
type5_special_vehicle_flow = VALUES(type5_special_vehicle_flow),
type6_special_vehicle_flow = VALUES(type6_special_vehicle_flow)
</insert>
<select id="getMaxStatisticalDate" resultType="java.util.Date">
SELECT
MAX(statistical_date)
FROM
dc_toll_station_statistics_data
</select>
</mapper>

2
zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml

@ -26,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
<if test="controlResult != null and controlResult !=''">control_result,</if>
<if test="control != null and control !=''">control,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="eventId != null and eventId != ''">#{eventId},</if>
@ -35,6 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
<if test="controlResult != null and controlResult !=''">#{controlResult},</if>
<if test="control != null and control !=''">#{control},</if>
</trim>
</insert>

Loading…
Cancel
Save