Browse Source

添加对门架指标数据的统计

develop
xiepufeng 6 months ago
parent
commit
9741800f9f
  1. 2
      ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java
  2. 41
      zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java
  3. 89
      zc-business/src/main/java/com/zc/business/domain/DcGantryMetricsStatisticsData.java
  4. 2
      zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java
  5. 100
      zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java
  6. 2
      zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java
  7. 100
      zc-business/src/main/java/com/zc/business/domain/DcTrafficFlowStatisticsData.java
  8. 92
      zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java
  9. 38
      zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java
  10. 72
      zc-business/src/main/java/com/zc/business/enums/TrafficCompositionRateEnum.java
  11. 5
      zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java
  12. 32
      zc-business/src/main/java/com/zc/business/mapper/DcGantryMetricsStatisticsDataMapper.java
  13. 9
      zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java
  14. 42
      zc-business/src/main/java/com/zc/business/service/IDcGantryMetricsStatisticsDataService.java
  15. 12
      zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java
  16. 2
      zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java
  17. 17
      zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java
  18. 69
      zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java
  19. 14
      zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java
  20. 14
      zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java
  21. 73
      zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java
  22. 272
      zc-business/src/main/java/com/zc/business/service/impl/IDcGantryMetricsStatisticsDataServiceImpl.java
  23. 5
      zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java
  24. 1
      zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java
  25. 147
      zc-business/src/main/java/com/zc/business/statistics/cache/metrics/DailyGantryMetricsStatisticsCache.java
  26. 152
      zc-business/src/main/java/com/zc/business/statistics/cache/metrics/MonthlyGantryMetricsStatisticsCache.java
  27. 154
      zc-business/src/main/java/com/zc/business/statistics/cache/metrics/QuarterlyGantryMetricsStatisticsCache.java
  28. 148
      zc-business/src/main/java/com/zc/business/statistics/cache/metrics/YearlyGantryMetricsStatisticsCache.java
  29. 157
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryMetricsStatistics.java
  30. 7
      zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java
  31. 77
      zc-business/src/main/resources/mapper/business/DcGantryMetricsStatisticsDataMapper.xml
  32. 78
      zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml

2
ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java

@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
*/
public class OkHttp
{
private int timeOut = 5; // 默认超时时间 5
private int timeOut = 10; // 默认超时时间 10
private static okhttp3.OkHttpClient okHttpClient;

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

@ -5,10 +5,7 @@ import com.ruoyi.common.core.domain.AjaxResult;
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 com.zc.business.service.*;
import com.zc.common.core.httpclient.exception.HttpException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@ -41,6 +38,9 @@ public class DcTrafficStatisticsController {
@Autowired
private IDcGantryStatisticsDataService dcGantryStatisticsDataService;
@Autowired
private IDcGantryMetricsStatisticsDataService dcGantryMetricsStatisticsDataService;
/**
* 获取当前交通截面数据
*
@ -117,23 +117,6 @@ public class DcTrafficStatisticsController {
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);
}
/**
* 获取收费站统计数据
@ -173,13 +156,27 @@ public class DcTrafficStatisticsController {
*/
@ApiOperation("获取门架统计数据")
@GetMapping("/history/gantry")
public AjaxResult historyTollStation(DcGantryStatisticsData request){
public AjaxResult historyGantryData(DcGantryStatisticsData request){
// 调用服务层方法,根据请求条件查询历史车门架数据
List<DcGantryStatisticsData> dcStatisticsData = dcGantryStatisticsDataService.gantryData(request);
// 将查询结果封装为成功响应并返回
return AjaxResult.success(dcStatisticsData);
}
/**
* 获取门架指标统计数据
*
* @param request 包含查询条件的请求对象用于筛选历史门架指标统计数据
* @return 返回一个AjaxResult对象其中包含了查询到的门架指标统计数据列表
*/
@ApiOperation("获取门架交通特指数数据")
@GetMapping("/history/gantry-metrics")
public AjaxResult historyGantryMetrics(DcGantryMetricsStatisticsData request){
// 调用服务层方法,根据请求条件查询历史车门架数据
List<DcGantryMetricsStatisticsData> dcStatisticsData = dcGantryMetricsStatisticsDataService.gantryMetricsData(request);
// 将查询结果封装为成功响应并返回
return AjaxResult.success(dcStatisticsData);
}
/********************************************* 智慧高速平接口 **************************************************/

89
zc-business/src/main/java/com/zc/business/domain/DcGantryMetricsStatisticsData.java

@ -0,0 +1,89 @@
package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Objects;
/**
* 门架交通特征指数
* @author xiepufeng
*/
@Getter
@Setter
public class DcGantryMetricsStatisticsData extends DcStatisticsData {
/**
* 门架编号
*/
private String gantryCode;
/**
* 拥堵度
* 1-畅通
* 2-基本畅通
* 3-轻度拥堵
* 4-中度拥堵
* 5-重度拥堵
*/
private Integer crowdingRate;
/**
* 交通组成特征指数大车占比
* 1-
* 2-
* 3-
*/
private Integer trafficCompositionRate;
/**
* 饱和度指数
*/
private Double saturationRate;
/**
* 重写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
DcGantryMetricsStatisticsData that = (DcGantryMetricsStatisticsData) o; // 类型转换
// 使用Objects.equals方法比较对象的各个属性值
return Objects.equals(gantryCode, that.getGantryCode()) &&
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));
}
}

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

@ -12,7 +12,7 @@ import java.util.Objects;
* @author xiepufeng
*/
@Data
public class DcGantryStatisticsData extends DcStatisticsData implements Serializable {
public class DcGantryStatisticsData extends DcTrafficFlowStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;

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

@ -2,16 +2,18 @@ package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.core.domain.BaseEntity;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import lombok.Data;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
@Data
@Getter
@Setter
public class DcStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;
@ -19,6 +21,7 @@ public class DcStatisticsData implements Serializable {
/**
* 主键
*/
@TableId
private String id;
/**
@ -34,17 +37,13 @@ public class DcStatisticsData implements Serializable {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date reportTime;
/**
* 车流量
*/
private Integer trafficVolume;
/**
* 统计粒度
* 1-
* 2-
* 3-
* 4-
* 5-
*/
private Byte periodType;
@ -58,86 +57,6 @@ public class DcStatisticsData implements Serializable {
*/
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;
/**
* 开始时间
*/
@ -152,6 +71,7 @@ public class DcStatisticsData implements Serializable {
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
public void setPeriodType(Byte periodType) {
this.periodType = periodType;
}
@ -171,6 +91,10 @@ public class DcStatisticsData implements Serializable {
*/
public void setStatisticalDate(Date statisticalDate, TrafficDataPeriodTypeEnum typeEnum) {
switch (typeEnum) {
case HOUR:
// 设置为当年的起始日期
this.statisticalDate = DateUtil.beginOfHour(statisticalDate);
break;
case DAY:
// 设置为当天的起始时间
this.statisticalDate = DateUtil.beginOfDay(statisticalDate);

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

@ -12,7 +12,7 @@ import java.util.Objects;
* @author xiepufeng
*/
@Data
public class DcTollStationStatisticsData extends DcStatisticsData implements Serializable {
public class DcTollStationStatisticsData extends DcTrafficFlowStatisticsData implements Serializable {
private static final long serialVersionUID = 1L;

100
zc-business/src/main/java/com/zc/business/domain/DcTrafficFlowStatisticsData.java

@ -0,0 +1,100 @@
package com.zc.business.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Getter;
import lombok.Setter;
import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
@Getter
@Setter
public class DcTrafficFlowStatisticsData extends DcStatisticsData {
private static final long serialVersionUID = 1L;
/**
* 车流量
*/
private Integer trafficVolume;
/**
* 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;
}

92
zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java

@ -1,14 +1,9 @@
package com.zc.business.domain;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import lombok.Data;
import org.apache.commons.codec.digest.DigestUtils;
import java.util.Date;
import java.util.Objects;
/**
@ -16,13 +11,7 @@ import java.util.Objects;
* @author xiepufeng
*/
@Data
public class DcTrafficSectionData {
/**
* 主键
*/
@TableId
private String id;
public class DcTrafficSectionData extends DcStatisticsData {
/**
* 车流量
@ -44,30 +33,11 @@ public class DcTrafficSectionData {
*/
private Long deviceId;
/**
* 统计时间
*/
@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 Byte direction;
/**
* 时段类型
* 1- 2- 3- 4-
*/
private Byte periodType;
/**
* 所在桩号
@ -80,28 +50,6 @@ public class DcTrafficSectionData {
private Integer deviceType;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date createTime;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
private Date updateTime;
public void setPeriodType(Byte periodType) {
this.periodType = periodType;
}
/**
* 设置交通数据的统计周期类型
* @param periodType 统计周期类型的枚举值
*/
public void setPeriodType(TrafficDataPeriodTypeEnum periodType) {
this.periodType = periodType.getCode(); // 将枚举类型转换为代码值存储
}
/**
* 重写equals方法用于比较两个对象是否相等
* @param o 要与当前对象比较的对象
@ -113,9 +61,9 @@ public class DcTrafficSectionData {
if (o == null || getClass() != o.getClass()) return false; // 对象为空或类型不同,返回false
DcTrafficSectionData that = (DcTrafficSectionData) o; // 类型转换
return Objects.equals(deviceId, that.deviceId) &&
Objects.equals(reportTime, that.reportTime) &&
Objects.equals(getReportTime(), that.getReportTime()) &&
Objects.equals(direction, that.direction) &&
Objects.equals(periodType, that.periodType) &&
Objects.equals(getPeriodType(), that.getPeriodType()) &&
Objects.equals(stakeMark, that.stakeMark); // 比较各属性值
}
@ -125,44 +73,16 @@ public class DcTrafficSectionData {
*/
@Override
public int hashCode() {
return Objects.hash(deviceId, reportTime, direction, periodType, stakeMark);
return Objects.hash(deviceId, getReportTime(), direction, getPeriodType(), stakeMark);
}
/**
* 根据给定的统计周期类型和日期设置统计日期为相应周期的起始日期
* @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;
}
}
/**
* 根据设备ID统计时间方向时段类型桩号生成一个唯一ID
*/
public void generateUniqueId() {
String combinedAttributes = deviceId + "_" + DateUtil.format(statisticalDate, "yyyyMMdd_HHmmss") + "_" + direction + "_" + periodType + "_" + stakeMark;
this.id = DigestUtils.md5Hex(combinedAttributes);
String combinedAttributes = deviceId + "_" + DateUtil.format(getStatisticalDate(), "yyyyMMdd_HHmmss") + "_" + direction + "_" + getPeriodType() + "_" + stakeMark;
this.setId(DigestUtils.md5Hex(combinedAttributes));
}
}

38
zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java

@ -12,27 +12,27 @@ public enum ChannelCongestionLevelEnum {
/**
* 表示通道畅通速度阈值为70 km/h
*/
FLOWING(70, 0, "畅通"),
FLOWING(70, 0, 1,"畅通"),
/**
* 表示通道基本畅通速度阈值为50 km/h
*/
BASIC_FLOWING(50, 0, "基本畅通"),
BASIC_FLOWING(50, 0, 2, "基本畅通"),
/**
* 表示通道轻度拥堵速度阈值为40 km/h
*/
LIGHT_CONGESTION(40, 1000, "轻度拥堵"),
LIGHT_CONGESTION(40, 1000, 3, "轻度拥堵"),
/**
* 表示通道中度拥堵速度阈值为20 km/h
*/
MEDIUM_CONGESTION(20, 2000, "中度拥堵"),
MEDIUM_CONGESTION(20, 2000, 4, "中度拥堵"),
/**
* 使用负数作为默认值表示无限小始终小于其他速度阈值表示通道严重拥堵
*/
SEVERE_CONGESTION(-1, 4000, "严重拥堵");
SEVERE_CONGESTION(-1, 4000, 5, "严重拥堵");
/**
* 速度阈值用于判断通道拥挤程度
@ -44,6 +44,11 @@ public enum ChannelCongestionLevelEnum {
*/
private final int defaultCongestionDistance;
/**
* 等级
*/
private final int level;
/**
* 对拥挤度等级的描述
*/
@ -55,9 +60,10 @@ public enum ChannelCongestionLevelEnum {
* @param speedThreshold 速度阈值
* @param description 等级描述
*/
ChannelCongestionLevelEnum(int speedThreshold, int defaultCongestionDistance, String description) {
ChannelCongestionLevelEnum(int speedThreshold, int defaultCongestionDistance, int level, String description) {
this.speedThreshold = speedThreshold;
this.defaultCongestionDistance = defaultCongestionDistance;
this.level = level;
this.description = description;
}
@ -164,4 +170,24 @@ public enum ChannelCongestionLevelEnum {
ChannelCongestionLevelEnum currentLevel = fromSpeed(speed);
return currentLevel == level;
}
/**
* 根据描述获取频道拥堵级别枚举值
* 该方法遍历所有ChannelCongestionLevelEnum的实例通过匹配描述来寻找对应的枚举值
* 如果找不到匹配的枚举值则抛出IllegalArgumentException
*
* @param description 频道拥堵级别的描述字符串
* @return 匹配描述的频道拥堵级别枚举值
* @throws IllegalArgumentException 如果描述不匹配任何枚举值则抛出此异常
*/
public static ChannelCongestionLevelEnum fromDescription(String description) {
for (ChannelCongestionLevelEnum value : values()) {
if (value.getDescription().equals(description)) {
return value;
}
}
throw new IllegalArgumentException("无效的指数值: " + description);
}
}

72
zc-business/src/main/java/com/zc/business/enums/TrafficCompositionRateEnum.java

@ -0,0 +1,72 @@
package com.zc.business.enums;
import lombok.Getter;
/**
* 交通组成特征指数大车占比枚举类
* 包括低高三类
* @author xiepufeng
*/
@Getter
public enum TrafficCompositionRateEnum {
// 低交通组成率
LOW(1, "低"),
// 中交通组成率
MEDIUM(2, "中"),
// 高交通组成率
HIGH(3, "高");
// 码值,代表每个枚举值的唯一标识
private final int code;
// 描述,对每个枚举值的级别进行文字描述
private final String description;
/**
* 构造方法初始化枚举值的码值和描述
*
* @param code 码值
* @param description 描述
*/
TrafficCompositionRateEnum(int code, String description) {
this.code = code;
this.description = description;
}
/**
* 根据码值获取枚举值
*
* @param code 码值
* @return 对应码值的枚举常量
* @throws IllegalArgumentException 如果码值无效则抛出此异常
*/
// 根据索引值获取枚举值,提供一个静态方法
public static TrafficCompositionRateEnum fromCode(int code) {
for (TrafficCompositionRateEnum value : values()) {
if (value.getCode() == code) {
return value;
}
}
throw new IllegalArgumentException("无效的指数值: " + code);
}
/**
* 根据描述字符串获取交通组成比率枚举值
* 该方法遍历所有TrafficCompositionRateEnum枚举常量找到其描述与输入字符串匹配的枚举值
* 如果找到匹配的枚举值则返回该枚举值如果没有匹配的枚举值则抛出IllegalArgumentException异常
* 这个方法的存在是为了提供一种通过枚举的描述而不是枚举名称来获取枚举常量的方法
*
* @param description 描述字符串用于匹配枚举值的描述
* @return 匹配描述的TrafficCompositionRateEnum枚举值
* @throws IllegalArgumentException 如果没有找到匹配的枚举值则抛出此异常
*/
public static TrafficCompositionRateEnum fromDescription(String description) {
for (TrafficCompositionRateEnum value : values()) {
if (value.getDescription().equals(description)) {
return value;
}
}
throw new IllegalArgumentException("无效的指数值: " + description);
}
}

5
zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java

@ -17,7 +17,10 @@ public enum TrafficDataPeriodTypeEnum {
MONTH((byte) 3, "月"),
// 枚举成员:日,关联字节型代码 4 和描述 "日"
DAY((byte) 4, "日");
DAY((byte) 4, "日"),
// 枚举成员:小时,关联字节型代码 4 和描述 "小时"
HOUR((byte) 5, "小时");
// 每个枚举成员的属性:代码,存储对应的字节型数值
private final Byte code;

32
zc-business/src/main/java/com/zc/business/mapper/DcGantryMetricsStatisticsDataMapper.java

@ -0,0 +1,32 @@
package com.zc.business.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* 门架指标数据统计Mapper接口
*
* @author xiepufeng
*/
@Mapper
public interface DcGantryMetricsStatisticsDataMapper extends BaseMapper<DcGantryMetricsStatisticsData> {
/**
* 插入或更新门架指标数据
*
* @param gantryTrafficMetricsData 门架指标数据对象包含需要插入或更新的数据
* @return 返回一个布尔值表示操作是否成功true表示插入或更新成功false表示失败
*/
boolean insertOrUpdate(DcGantryMetricsStatisticsData gantryTrafficMetricsData);
/**
* 批量插入或更新新门架指标数据
*
* @param gantryTrafficMetricsDataList 要插入或更新的新门架指标数据对象列表
* @return 影响的记录数通常对于批量操作这个值可能不准确因为每个 INSERT/UPDATE 可能影响不同的记录数
*/
int insertOrUpdateBatch(List<DcGantryMetricsStatisticsData> gantryTrafficMetricsDataList);
}

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

@ -5,6 +5,7 @@ import com.zc.business.domain.DcGantryStatisticsData;
import org.apache.ibatis.annotations.Mapper;
import java.util.Date;
import java.util.List;
/**
* 这是一个接口的注释用于描述门架数据统计的Mapper
@ -29,5 +30,13 @@ public interface DcGantryStatisticsDataMapper extends BaseMapper<DcGantryStatist
* @return 返回最大的统计日期
*/
Date getMaxStatisticalDate();
/**
* 批量插入或更新门架数据统计
*
* @param dcGantryStatisticsDataList 门架数据统计对象列表包含需要插入或更新的数据
* @return 返回一个整数表示插入或更新操作影响的行数
*/
int insertOrUpdateBatch(List<DcGantryStatisticsData> dcGantryStatisticsDataList);
}

42
zc-business/src/main/java/com/zc/business/service/IDcGantryMetricsStatisticsDataService.java

@ -0,0 +1,42 @@
package com.zc.business.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import java.util.List;
/**
* 门架指标统计服务接口提供处理交通数据的相关方法
* @author xiepufeng
*/
public interface IDcGantryMetricsStatisticsDataService extends IService<DcGantryMetricsStatisticsData> {
/**
* 获取过去一小时的DcGantryMetricsStatisticsData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的DcGantryMetricsStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryMetricsStatisticsData> - 过去一小时的DcGantryMetricsStatisticsData数据列表
*/
List<DcGantryMetricsStatisticsData> lastHourData();
/**
* 获取当月的DcGantryMetricsStatisticsData数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的DcGantryMetricsStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryMetricsStatisticsData> - 当月的DcGantryMetricsStatisticsData数据列表
*/
List<DcGantryMetricsStatisticsData> currentMonthData();
/**
* 根据请求条件获取门架指标统计数据
* @param request 请求条件
* @return 门架指标统计数据列表
*/
List<DcGantryMetricsStatisticsData> gantryMetricsData(DcGantryMetricsStatisticsData request);
}

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

@ -2,8 +2,6 @@ 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;
@ -25,6 +23,16 @@ public interface IDcGantryStatisticsDataService extends IService<DcGantryStatist
*/
List<DcGantryStatisticsData> lastHourData();
/**
* 获取当月的门架统计数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的DcGantryStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryStatisticsData> - 当月的DcGantryStatisticsData数据列表
*/
List<DcGantryStatisticsData> currentMonthData();
/**
* 进行门架数据统计的函数
* 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数用来包含门架统计所需的各项数据

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

@ -1,8 +1,6 @@
package com.zc.business.service;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.extension.service.IService;
import com.zc.business.domain.DcStatisticsData;
import com.zc.business.domain.DcTollStationStatisticsData;
import java.util.List;

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

@ -1,24 +1,15 @@
package com.zc.business.service;
import com.alibaba.fastjson.JSONArray;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import com.zc.business.domain.DcRoadSectionCongestion;
import com.zc.business.domain.DcStatisticsData;
import com.zc.common.core.httpclient.exception.HttpException;
import java.io.IOException;
import java.util.List;
import java.util.Map;
public interface IDcTrafficStatisticsService {
/**
* 根据传入的统计请求数据查询历史累计车流量数据
*
* @param request 包含统计查询条件的DcStatisticsData对象用于指定查询的历史流量数据的细节如时间范围等
* @return 返回一个DcStatisticsData列表
*/
List<DcStatisticsData> historyFlow(DcStatisticsData request);
/**
* 获取在途车辆流量分车型
*/
@ -43,4 +34,10 @@ public interface IDcTrafficStatisticsService {
* 获取车道占有率信息
*/
JSONArray laneOccupancy(String startDate, String endDate) throws HttpException, IOException;
/**
* 获取门架指标数据
*/
JSONArray gantryMetrics(String startTime, String endTime) throws HttpException, IOException;
}

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

@ -4,6 +4,7 @@ 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.DcGantryMetricsStatisticsData;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.domain.OdsTollEtctuData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
@ -15,6 +16,7 @@ import com.zc.business.statistics.cache.gantry.QuarterlyTrafficGantryStatisticsC
import com.zc.business.statistics.cache.gantry.YearlyTrafficGantryStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@ -38,6 +40,12 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl<DcGantryStatisticsDa
@Resource
private IOdsTollEtctuDataService odsTollEtctuDataService;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Resource
private DcGantryStatisticsDataMapper dcGantryStatisticsDataMapper;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通门架缓存
@ -45,10 +53,15 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl<DcGantryStatisticsDa
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通门架缓存
recoveryQuarterlyCache(); // 恢复每季度交通门架缓存
recoveryYearlyCache(); // 恢复每年交通门架缓存
threadPoolTaskExecutor.execute(() -> {
// 获取当月门架数据
List<DcGantryStatisticsData> dcGantryStatisticsDataList = currentMonthData();
recoveryHourlyCache(dcGantryStatisticsDataList); // 恢复小时缓存数据
recoveryDailyCache(dcGantryStatisticsDataList); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通门架缓存
recoveryQuarterlyCache(); // 恢复每季度交通门架缓存
recoveryYearlyCache(); // 恢复每年交通门架缓存
});
}
@ -68,6 +81,22 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl<DcGantryStatisticsDa
return odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList);
}
/**
* 获取当月的门架统计数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的DcGantryStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryStatisticsData> - 当月的DcGantryStatisticsData数据列表
*/
@Override
public List<DcGantryStatisticsData> currentMonthData() {
// 获取门架这个月的数据
List<OdsTollEtctuData> odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData();
return odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList);
}
/**
* 进行门架数据统计的函数
* 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数用来包含门架统计所需的各项数据
@ -87,41 +116,41 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl<DcGantryStatisticsDa
throw new ServiceException("时段类型不能为空");
}
if (request.getGantryCode() == null) {
throw new ServiceException("门架标识不能为空");
}
LambdaQueryWrapper<DcGantryStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
if (request.getStatisticalDate() != null) {
queryWrapper.eq(DcGantryStatisticsData::getStatisticalDate, request.getStatisticalDate());
}
// 如果请求中包含唯一标识符,则根据唯一标识符进行过滤
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());
queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, request.getStartTime(), request.getEndTime());
return list(queryWrapper);
}
/**
* 恢复小时缓存数据的方法
* 该方法首先会获取当前月份的门架入口数据
* 然后分别计算每个门架的统计信息并将这些统计信息添加到每日交通门架指标统计缓存中
*/
private void recoveryHourlyCache(List<DcGantryStatisticsData> dcGantryStatisticsDataList) {
dcGantryStatisticsDataMapper.insertOrUpdateBatch(dcGantryStatisticsDataList);
}
/**
* 恢复日缓存数据的方法
* 该方法首先会获取当前月份的门架入口数据
* 然后分别计算每个门架的统计信息并将这些统计信息添加到每日交通门架统计缓存中
*/
private void recoveryDailyCache() {
// 获取当月门架数据
List<OdsTollEtctuData> odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData();
private void recoveryDailyCache(List<DcGantryStatisticsData> dcGantryStatisticsDataList) {
// 计算每个门架数据的统计信息,并添加到每日门架统计缓存中
odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList).forEach(DailyTrafficGantryStatisticsCache::addCacheData);
dcGantryStatisticsDataList.forEach(DailyTrafficGantryStatisticsCache::addCacheData);
}
/**

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

@ -18,6 +18,7 @@ import com.zc.business.statistics.cache.tollstation.QuarterlyTrafficTollStationS
import com.zc.business.statistics.cache.tollstation.YearlyTrafficTollStationStatisticsCache;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@ -50,16 +51,21 @@ public class DcTollStationStatisticsDataImpl extends ServiceImpl<DcTollStationSt
@Resource
private IOdsTollExpassDataService odsTollExpassDataService;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通收费站站点缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通收费站站点缓存
recoveryQuarterlyCache(); // 恢复每季度交通收费站站点缓存
recoveryYearlyCache(); // 恢复每年交通收费站站点缓存
threadPoolTaskExecutor.execute(() -> {
recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据)
recoveryMonthlyCache(); // 恢复每月交通收费站站点缓存
recoveryQuarterlyCache(); // 恢复每季度交通收费站站点缓存
recoveryYearlyCache(); // 恢复每年交通收费站站点缓存
});
}

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

@ -26,6 +26,7 @@ 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.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
@ -61,16 +62,21 @@ public class DcTrafficSectionStatisticsServiceImpl
@Resource
private TrafficSectionAnalysis trafficSectionAnalysis;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通数据缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
recoveryDailyCache(); // 从es中恢复当月交通数据缓存
recoveryMonthlyCache(); // 恢复每月交通数据缓存
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
recoveryYearlyCache(); // 恢复每年交通数据缓存
threadPoolTaskExecutor.execute(() -> {
recoveryDailyCache(); // 从es中恢复当月交通数据缓存
recoveryMonthlyCache(); // 恢复每月交通数据缓存
recoveryQuarterlyCache(); // 恢复每季度交通数据缓存
recoveryYearlyCache(); // 恢复每年交通数据缓存
});
}
/**

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

@ -1,14 +1,18 @@
package com.zc.business.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zc.business.domain.*;
import com.zc.business.enums.ChannelCongestionLevelEnum;
import com.zc.business.enums.TrafficCompositionRateEnum;
import com.zc.business.service.IDcFacilityService;
import com.zc.business.service.IDcRoadSectionService;
import com.zc.business.service.IDcTrafficStatisticsService;
import com.zc.business.statistics.cache.metrics.DailyGantryMetricsStatisticsCache;
import com.zc.business.utils.StakeMarkUtils;
import com.zc.common.core.httpclient.OkHttp;
import com.zc.common.core.httpclient.exception.HttpException;
@ -17,25 +21,27 @@ import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
@Service
public class DcTrafficStatisticsServiceImpl implements IDcTrafficStatisticsService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
// 智慧高速平台token
private JSONObject token = null;
private final String sysid = "sdgs_it_hs_jihe";
private String baseUrl = "http://10.166.139.16:8080";
private final String baseUrl = "http://10.166.139.16:8080";
@Resource
private IDcFacilityService facilityService;
@ -51,19 +57,6 @@ public class DcTrafficStatisticsServiceImpl implements IDcTrafficStatisticsServi
refreshAccessToken();
}
/**
* 根据传入的统计请求数据查询历史累计车流量数据
*
* @param request 包含统计查询条件的DcStatisticsData对象用于指定查询的历史流量数据的细节如时间范围等
* @return 返回一个DcStatisticsData列表
*/
@Override
public List<DcStatisticsData> historyFlow(DcStatisticsData request) {
// TODO
return null;
}
/**
* 定时刷新访问令牌的函数
* 该方法使用Cron表达式定时执行即每5小时执行一次
@ -476,7 +469,7 @@ public class DcTrafficStatisticsServiceImpl implements IDcTrafficStatisticsServi
*/
@Override
public JSONArray laneOccupancy(String startDate, String endDate) throws HttpException, IOException {
OkHttp okHttp = new OkHttp(10);
OkHttp okHttp = new OkHttp();
RequestParams requestParams = new RequestParams();
@ -509,5 +502,47 @@ public class DcTrafficStatisticsServiceImpl implements IDcTrafficStatisticsServi
return new JSONArray();
}
/**
* 获取门架指标数据
*
* @param startTime 开始时间
* @param endTime 结束时间
*/
@Override
public JSONArray gantryMetrics(String startTime, String endTime) throws HttpException, IOException {
OkHttp okHttp = new OkHttp();
RequestParams requestParams = new RequestParams();
requestParams.put("sysid", sysid);
JSONObject parameters = new JSONObject() {
{
put("start_time", startTime);
put("end_time", endTime);
}
};
requestParams.put("parameters", parameters.toJSONString());
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", getAccessToken());
Response response // 请求响应
= okHttp
.headers(headers)
.url(baseUrl + "/api/dc/query/rd_jihe_d_evaluateindicator") // 请求地址
.data(requestParams) // 请求参数
.post(); // 请求方法
ResponseBody body = response.body();
if (body != null) {
return JSON.parseArray(body.string());
}
return new JSONArray();
}
}

272
zc-business/src/main/java/com/zc/business/service/impl/IDcGantryMetricsStatisticsDataServiceImpl.java

@ -0,0 +1,272 @@
package com.zc.business.service.impl;
import cn.hutool.core.date.DateTime;
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.exception.ServiceException;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import com.zc.business.domain.DcGantryStatisticsData;
import com.zc.business.enums.ChannelCongestionLevelEnum;
import com.zc.business.enums.TrafficCompositionRateEnum;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcGantryMetricsStatisticsDataMapper;
import com.zc.business.service.IDcGantryMetricsStatisticsDataService;
import com.zc.business.service.IDcTrafficStatisticsService;
import com.zc.business.statistics.cache.metrics.DailyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.MonthlyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.QuarterlyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.YearlyGantryMetricsStatisticsCache;
import com.zc.common.core.httpclient.exception.HttpException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 门架指标统计服务实现类负责处理实时设备消息缓存数据定时任务以及数据保存等功能
*
* @author xiepufeng
*/
@Service
public class IDcGantryMetricsStatisticsDataServiceImpl
extends ServiceImpl<DcGantryMetricsStatisticsDataMapper, DcGantryMetricsStatisticsData>
implements IDcGantryMetricsStatisticsDataService {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private IDcTrafficStatisticsService dcTrafficStatisticsService;
@Resource
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Resource
private DcGantryMetricsStatisticsDataMapper gantryMetricsStatisticsDataMapper;
/**
* 初始化方法用于在对象创建后恢复各种周期的交通数据缓存
* 该方法标注了@PostConstruct注解确保在依赖注入完成后调用
*/
@PostConstruct
public void init() {
threadPoolTaskExecutor.execute(() -> {
// 获取当前月份的门架指标数据列表
List<DcGantryMetricsStatisticsData> gantryMetricsStatisticsDataList = currentMonthData();
recoveryHourlyCache(gantryMetricsStatisticsDataList); // 恢复小时缓存数据
recoveryDailyCache(gantryMetricsStatisticsDataList); // 恢复日缓存数据
recoveryMonthlyCache(); // 恢复每月交通门架缓存
recoveryQuarterlyCache(); // 恢复每季度交通门架缓
recoveryYearlyCache(); // 恢复每年交通门架缓存
});
}
/**
* 获取过去一小时的DcGantryMetricsStatisticsData数据列表
*
* <p>此方法不接受任何参数返回一个包含过去一小时所有符合条件的DcGantryMetricsStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryMetricsStatisticsData> - 过去一小时的DcGantryMetricsStatisticsData数据列表
*/
@Override
public List<DcGantryMetricsStatisticsData> lastHourData() {
// 计算一小时前的时间点
DateTime lastHour = DateUtil.offsetHour(new Date(), -1);
String startTime = DateUtil.beginOfHour(lastHour).toString();
String endTime = DateUtil.endOfHour(lastHour).toString();
// 获取当月门架数指标据
JSONArray jsonArray = null;
try {
jsonArray = dcTrafficStatisticsService.gantryMetrics(startTime, endTime);
// 计算每个门架数指标据的统计信息,并添加到每日门架指标统计缓存中
return calculateGantryMetricsStatistics(jsonArray);
} catch (HttpException | IOException e) {
logger.error("获取过去一小时的门架指标数据失败", e);
return new ArrayList<>();
}
}
/**
* 获取当月的门架指标数据列表
*
* <p>此方法不接受任何参数返回一个包含当月所有符合条件的DcGantryMetricsStatisticsData对象的列表
* 该列表可用于进一步的数据分析和处理
*
* @return List<DcGantryMetricsStatisticsData> - 当月的DcGantryMetricsStatisticsData数据列表
*/
@Override
public List<DcGantryMetricsStatisticsData> currentMonthData() {
String startTime = DateUtil.beginOfMonth(new Date()).toString();
String endTime = DateUtil.now();
// 获取当月门架数指标据
JSONArray jsonArray = null;
try {
jsonArray = dcTrafficStatisticsService.gantryMetrics(startTime, endTime);
// 计算每个门架数指标据的统计信息,并添加到每日门架指标统计缓存中
return calculateGantryMetricsStatistics(jsonArray);
} catch (HttpException | IOException e) {
logger.error("获取当月门架指标数据失败", e);
return new ArrayList<>();
}
}
/**
* 根据请求条件获取门架指标统计数据
*
* @param request 请求条件
* @return 门架指标统计数据列表
*/
@Override
public List<DcGantryMetricsStatisticsData> gantryMetricsData(DcGantryMetricsStatisticsData request) {
if (request.getStartTime() == null || request.getEndTime() == null) {
throw new ServiceException("开始时间或结束时间不能为空");
}
if (request.getPeriodType() == null) {
throw new ServiceException("时段类型不能为空");
}
LambdaQueryWrapper<DcGantryMetricsStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
if (request.getStatisticalDate() != null) {
queryWrapper.eq(DcGantryMetricsStatisticsData::getStatisticalDate, request.getStatisticalDate());
}
// 如果请求中包含唯一标识符,则根据唯一标识符进行过滤
if (request.getId() != null) {
queryWrapper.eq(DcGantryMetricsStatisticsData::getId, request.getId());
}
queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, request.getPeriodType());
queryWrapper.eq(DcGantryMetricsStatisticsData::getGantryCode, request.getGantryCode());
queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, request.getStartTime(), request.getEndTime());
return list(queryWrapper);
}
/**
* 恢复小时缓存数据的方法
* 该方法首先会获取当前月份的门架入口数据
* 然后分别计算每个门架的统计信息并将这些统计信息添加到每日交通门架指标统计缓存中
*/
private void recoveryHourlyCache(List<DcGantryMetricsStatisticsData> gantryMetricsStatisticsDataList) {
gantryMetricsStatisticsDataMapper.insertOrUpdateBatch(gantryMetricsStatisticsDataList);
}
/**
* 恢复日缓存数据的方法
* 该方法首先会获取当前月份的门架入口数据
* 然后分别计算每个门架的统计信息并将这些统计信息添加到每日交通门架指标统计缓存中
*/
private void recoveryDailyCache(List<DcGantryMetricsStatisticsData> gantryMetricsStatisticsDataList) {
// 计算每个门架数指标据的统计信息,并添加到每日门架指标统计缓存中
gantryMetricsStatisticsDataList.forEach(DailyGantryMetricsStatisticsCache::addCacheData);
}
/**
* 恢复每月交通门架缓存的方法
* 通过查询当前月份至今的每日交通数据并将其添加到每月门架指标统计缓存中
*/
private void recoveryMonthlyCache() {
// 构建查询条件,查询当前月份至今的每日交通数据
LambdaQueryWrapper<DcGantryMetricsStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.DAY);
queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date());
List<DcGantryMetricsStatisticsData> dcGantryMetricsStatisticsDataList = this.list(queryWrapper);
// 遍历查询结果,将每日数据添加到每月门架指标统计缓存中
dcGantryMetricsStatisticsDataList.forEach(MonthlyGantryMetricsStatisticsCache::addCacheData);
}
/**
* 恢复每季度交通门架缓存的方法
* 通过查询当前季度至今的每月交通数据并将其添加到每季度门架指标统计缓存中
*/
private void recoveryQuarterlyCache() {
// 构建查询条件,查询当前季度至今的每月交通数据
LambdaQueryWrapper<DcGantryMetricsStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH);
queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date());
List<DcGantryMetricsStatisticsData> dcGantryMetricsStatisticsDataList = this.list(queryWrapper);
// 遍历查询结果,将每月数据添加到每季度门架指标统计缓存
dcGantryMetricsStatisticsDataList.forEach(QuarterlyGantryMetricsStatisticsCache::addCacheData);
}
/**
* 恢复每年交通门架缓存的方法
* 通过查询当前年份至今的每季度交通数据并将其添加到每年门架指标统计缓存中
*/
private void recoveryYearlyCache() {
// 构建查询条件,查询当前年份至今的每季度交通数据
LambdaQueryWrapper<DcGantryMetricsStatisticsData> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER);
queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date());
List<DcGantryMetricsStatisticsData> dcGantryMetricsStatisticsDataList = this.list(queryWrapper);
// 遍历查询结果,将每季度数据添加到每年门架指标统计缓存
dcGantryMetricsStatisticsDataList.forEach(YearlyGantryMetricsStatisticsCache::addCacheData);
}
/**
* 计算门架指标数据
*
* @param jsonArray 门架指标数据
* @return 门架指标数据列表
*/
private List<DcGantryMetricsStatisticsData> calculateGantryMetricsStatistics(JSONArray jsonArray) {
if (jsonArray == null || jsonArray.isEmpty()) {
return new ArrayList<>();
}
List<DcGantryMetricsStatisticsData> dcGantryMetricsStatisticsDataList = new ArrayList<>();
jsonArray.forEach(item -> {
DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData = new DcGantryMetricsStatisticsData();
JSONObject jsonObject = (JSONObject) item;
// 门架编号
dcGantryMetricsStatisticsData.setGantryCode(jsonObject.getString("gantry_id"));
// 拥堵等级
dcGantryMetricsStatisticsData.setCrowdingRate(ChannelCongestionLevelEnum.fromDescription(jsonObject.getString("crowding_rate")).getLevel());
// 交通组成特征指数(大车占比)
dcGantryMetricsStatisticsData.setTrafficCompositionRate(TrafficCompositionRateEnum.fromDescription(jsonObject.getString("traffic_composition_rate")).getCode());
// 饱和度
dcGantryMetricsStatisticsData.setSaturationRate(jsonObject.getDouble("saturation_rate"));
// 时间
Date dataHour = DateUtil.date(jsonObject.getLong("data_hour"));
// 上报时间
dcGantryMetricsStatisticsData.setReportTime(dataHour);
// 统计时间
dcGantryMetricsStatisticsData.setStatisticalDate(DateUtil.beginOfHour(dataHour));
// 时间颗粒度(小时)
dcGantryMetricsStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.HOUR);
// 生成唯一ID
dcGantryMetricsStatisticsData.generateUniqueId();
dcGantryMetricsStatisticsDataList.add(dcGantryMetricsStatisticsData);
});
return dcGantryMetricsStatisticsDataList;
}
}

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

@ -201,7 +201,7 @@ public class OdsTollEtctuDataImpl extends ServiceImpl<OdsTollEtctuDataMapper, Od
// 上报时间
dcGantryStatisticsData.setReportTime(date);
// 统计粒度
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.HOUR);
// 车流量
dcGantryStatisticsData.setTrafficVolume(trafficVolume);
@ -238,6 +238,9 @@ public class OdsTollEtctuDataImpl extends ServiceImpl<OdsTollEtctuDataMapper, Od
// 6型专项作业车车流量
dcGantryStatisticsData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow);
// 生成唯一标识
dcGantryStatisticsData.generateUniqueId();
return dcGantryStatisticsData;
}

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

@ -3,7 +3,6 @@ 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;

147
zc-business/src/main/java/com/zc/business/statistics/cache/metrics/DailyGantryMetricsStatisticsCache.java

@ -0,0 +1,147 @@
package com.zc.business.statistics.cache.metrics;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
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 DailyGantryMetricsStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, DailyGantryMetricsStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = 26 * 60 * 60; // 缓存数据最长保留25小时
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 24 + 1 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private DailyGantryMetricsStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryMetricsStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取或新建对应的缓存实例
DailyGantryMetricsStatisticsCache instance = getInstance(dcGantryMetricsStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架指标缓存数据出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryMetricsStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryMetricsStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryMetricsStatisticsData.setReportTime(dcGantryMetricsStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryMetricsStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY);
// 更新统计日期
dcGantryMetricsStatisticsData.setStatisticalDate(dcGantryMetricsStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY);
// 移除旧数据
instance.getData().remove(dcGantryMetricsStatisticsData);
// 添加数据
instance.getData().add(dcGantryMetricsStatisticsData);
}
/**
* 获取或创建对应设备与日期的DailyTrafficGantryStatisticsCache实例
*
* @param dcGantryMetricsStatisticsData 门架数据统计定义
* @return 对应的门架指标缓存数据实例
*/
private static DailyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new DailyGantryMetricsStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期来生成一个唯一的缓存键</p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryMetricsStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的门架指标缓存数据项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(DailyGantryMetricsStatisticsCache::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/metrics/MonthlyGantryMetricsStatisticsCache.java

@ -0,0 +1,152 @@
package com.zc.business.statistics.cache.metrics;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
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 MonthlyGantryMetricsStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, MonthlyGantryMetricsStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (31 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 31 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private MonthlyGantryMetricsStatisticsCache() {
}
/**
* 添加门架指标数据到缓存中
*
* @param dcGantryMetricsStatisticsData 待添加的门架指标数据
*/
public static void addCacheData(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取或新建对应的缓存实例
MonthlyGantryMetricsStatisticsCache instance = getInstance(dcGantryMetricsStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架指标数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryMetricsStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryMetricsStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryMetricsStatisticsData.setReportTime(dcGantryMetricsStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryMetricsStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH);
// 更新统计日期
dcGantryMetricsStatisticsData.setStatisticalDate(dcGantryMetricsStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH);
// 移除旧数据
instance.getData().remove(dcGantryMetricsStatisticsData);
// 添加数据
instance.getData().add(dcGantryMetricsStatisticsData);
}
/**
* 获取月门架信息缓存的实例
* <p>
* 根据传入的门架在一定周期内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据用于生成缓存键
* @return 缓存中的或新创建的月门架统计信息缓存实例
*/
private static MonthlyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new MonthlyGantryMetricsStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryMetricsStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期和访问类型作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(MonthlyGantryMetricsStatisticsCache::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/metrics/QuarterlyGantryMetricsStatisticsCache.java

@ -0,0 +1,154 @@
package com.zc.business.statistics.cache.metrics;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
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 QuarterlyGantryMetricsStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, QuarterlyGantryMetricsStatisticsCache> 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 QuarterlyGantryMetricsStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryMetricsStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取或新建对应的缓存实例
QuarterlyGantryMetricsStatisticsCache instance = getInstance(dcGantryMetricsStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryMetricsStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryMetricsStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryMetricsStatisticsData.setReportTime(dcGantryMetricsStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryMetricsStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER);
// 更新统计日期
dcGantryMetricsStatisticsData.setStatisticalDate(dcGantryMetricsStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER);
// 移除旧数据
instance.getData().remove(dcGantryMetricsStatisticsData);
// 添加数据
instance.getData().add(dcGantryMetricsStatisticsData);
}
/**
* 获取季度门架统计信息缓存的实例
* <p>
* 根据传入的门架在一定周期季度内的统计数据计算并返回对应的缓存实例
* 如果缓存中已存在该实例则直接返回否则创建新的缓存实例并加入缓存
* </p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据用于生成缓存键
* @return 缓存中的或新创建的季度门架统计信息缓存实例
*/
private static QuarterlyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 根据传入数据生成唯一键,并尝试从缓存中获取或创建新的缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new QuarterlyGantryMetricsStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期和访问类型来生成一个唯一的缓存键</p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据对象包含门架编号统计日期和访问类型
* @return 缓存键格式为"门架编号|格式化后的统计日期|访问类型"
*/
public static String generateCacheKey(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取门架标识
String gantryCode = dcGantryMetricsStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(QuarterlyGantryMetricsStatisticsCache::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/metrics/YearlyGantryMetricsStatisticsCache.java

@ -0,0 +1,148 @@
package com.zc.business.statistics.cache.metrics;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
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 YearlyGantryMetricsStatisticsCache extends AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData> {
// 静态缓存容器,使用ConcurrentHashMap保证线程安全
@Getter
private static final Map<String, YearlyGantryMetricsStatisticsCache> cache = new ConcurrentHashMap<>();
// 最大缓存时间(单位:秒)
private static final long MAX_CACHE_TIME = (60 * 60) * (366 * 24 + 2);
// 最大容量限制,防止内存溢出
private static final int MAX_CAPACITY = 4 + 1000; // 缓存的最大条目数
// 私有构造函数,确保只能通过静态方法获取实例
private YearlyGantryMetricsStatisticsCache() {
}
/**
* 添加门架数据到缓存中
*
* @param dcGantryMetricsStatisticsData 待添加的门架数据
*/
public static void addCacheData(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取或新建对应的缓存实例
YearlyGantryMetricsStatisticsCache instance = getInstance(dcGantryMetricsStatisticsData);
// 检查缓存容量是否达到上限
if (instance.getData().size() >= MAX_CAPACITY) {
instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryMetricsStatisticsData.getGantryCode());
return;
}
// 更新最后添加时间
instance.setLastAddedTime(DateUtil.date());
// 更新状态
instance.setStored(false);
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
String key = generateCacheKey(dcGantryMetricsStatisticsData);
// 设置key和thatDay属性(仅在首次添加时)
if (instance.getCacheKey() == null) {
instance.setCacheKey(key);
instance.setStatisticalDateStr(formattedDate);
}
// 更新上报时间
dcGantryMetricsStatisticsData.setReportTime(dcGantryMetricsStatisticsData.getStatisticalDate());
// 更新数据周期类型
dcGantryMetricsStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.YEAR);
// 更新统计日期
dcGantryMetricsStatisticsData.setStatisticalDate(dcGantryMetricsStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.YEAR);
// 移除旧数据
instance.getData().remove(dcGantryMetricsStatisticsData);
// 添加数据
instance.getData().add(dcGantryMetricsStatisticsData);
}
/**
* 获取或创建对应设备与日期的YearlyTrafficSectionStatisticsCache实例
*
* @param dcGantryMetricsStatisticsData 交通断面数据统计定义
* @return 对应的交通断面数据缓存实例
*/
private static YearlyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例
return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new YearlyGantryMetricsStatisticsCache());
}
/**
* 生成缓存键
* <p>此方法通过组合门架编号统计日期来生成一个唯一的缓存键</p>
*
* @param dcGantryMetricsStatisticsData 门架统计数据对象包含门架编号统计日期
* @return 缓存键格式为"门架编号|格式化后的统计日期"
*/
public static String generateCacheKey(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) {
// 获取门架站点标识
String gantryCode = dcGantryMetricsStatisticsData.getGantryCode();
// 格式化统计日期
String formattedDate = formatDate(dcGantryMetricsStatisticsData.getStatisticalDate());
// 组合门架编号、格式化后的统计日期作为缓存键
return gantryCode + "|" + formattedDate;
}
/**
* 清除所有过期的交通断面数据缓存项
*/
public static void clearExpiredData() {
// 使用stream API找出所有过期的数据缓存项键
Set<String> keysToRemove = cache.keySet().stream()
.filter(YearlyGantryMetricsStatisticsCache::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");
}
}

157
zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryMetricsStatistics.java

@ -0,0 +1,157 @@
package com.zc.business.statistics.handler;
import com.zc.business.domain.DcGantryMetricsStatisticsData;
import com.zc.business.enums.TrafficDataPeriodTypeEnum;
import com.zc.business.mapper.DcGantryMetricsStatisticsDataMapper;
import com.zc.business.service.IDcGantryMetricsStatisticsDataService;
import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache;
import com.zc.business.statistics.cache.metrics.DailyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.MonthlyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.QuarterlyGantryMetricsStatisticsCache;
import com.zc.business.statistics.cache.metrics.YearlyGantryMetricsStatisticsCache;
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.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;
/**
* TrafficGantryStatistics类用于处理门架指标的统计数据
* @author xiepufeng
*/
@Component
public class TrafficGantryMetricsStatistics {
// 日志记录器
protected final Logger logger = LoggerFactory.getLogger(this.getClass());
@Resource
private IDcGantryMetricsStatisticsDataService dcGantryMetricsStatisticsDataService;
@Resource
private DcGantryMetricsStatisticsDataMapper dcGantryMetricsStatisticsDataMapper;
/**
* 定义每小时第20分钟执行的任务用于清除过期缓存数据并将缓存中的数据整合后保存至数据库
*/
@Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
public void performHourlyCleanupAndPersist() {
List<DcGantryMetricsStatisticsData> lastHourData = dcGantryMetricsStatisticsDataService.lastHourData();
// 数据库批量插入最近一小时的数据
dcGantryMetricsStatisticsDataMapper.insertOrUpdateBatch(lastHourData);
// 添加日门架指标数据到缓存中
lastHourData.forEach(DailyGantryMetricsStatisticsCache::addCacheData);
// 清除已过期的缓存数据
DailyGantryMetricsStatisticsCache.clearExpiredData();
MonthlyGantryMetricsStatisticsCache.clearExpiredData();
QuarterlyGantryMetricsStatisticsCache.clearExpiredData();
YearlyGantryMetricsStatisticsCache.clearExpiredData();
// 整合缓存数据并保存至数据库
// 将缓存中的数据按日统计后保存至数据库
// 添加月门架指标数据到缓存中
persistAggregatedData(DailyGantryMetricsStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyGantryMetricsStatisticsCache::addCacheData);
// 将缓存中的数据按月统计后保存至数据库
// 添加季度门架指标数据到缓存中
persistAggregatedData(MonthlyGantryMetricsStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyGantryMetricsStatisticsCache::addCacheData);
// 将缓存中的数据按季度统计后保存至数据库
// 添加年门架指标数据到缓存中
persistAggregatedData(QuarterlyGantryMetricsStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyGantryMetricsStatisticsCache::addCacheData);
// 将缓存中的数据按年统计后保存至数据库
persistAggregatedData(YearlyGantryMetricsStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {});
}
/**
* 将缓存中的数据统计后保存至数据库
*/
public void persistAggregatedData(
Map<String, ? extends AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData>> cache,
TrafficDataPeriodTypeEnum periodType,
Consumer<DcGantryMetricsStatisticsData> consumer
) {
for (AbstractTrafficStatisticsCache<DcGantryMetricsStatisticsData> data : cache.values()) {
// 如果数据已经存储过,则跳过此次处理
if (data.isStored()) {
continue;
}
DcGantryMetricsStatisticsData aggregatedData = trafficStatistics(data.getData(), periodType);
if (dcGantryMetricsStatisticsDataMapper.insertOrUpdate(aggregatedData)) {
// 设置数据已存储状态
data.setStored(true);
// 调用回调函数
consumer.accept(aggregatedData);
}
}
}
/**
* 对给定的门架指标统计数据集合进行汇总处理
*
* @param dataCollection 门架指标统计数据的集合不可为null或空
* @param trafficDataPeriodType 统计数据的时段类型例如小时月等
* @return 汇总后的门架指标统计数据对象如果输入数据为空则返回null
*/
public DcGantryMetricsStatisticsData trafficStatistics(Collection<DcGantryMetricsStatisticsData> dataCollection, TrafficDataPeriodTypeEnum trafficDataPeriodType) {
// 判断输入数据是否为空
if (CollectionUtils.isEmpty(dataCollection)) {
return null;
}
// 创建一个汇总统计用的对象
DcGantryMetricsStatisticsData aggregatedData = new DcGantryMetricsStatisticsData();
// 获取拥堵度众数
Integer mostFrequentCrowdingRate = dataCollection.stream()
.collect(Collectors.groupingBy(DcGantryMetricsStatisticsData::getCrowdingRate, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null); // 如果没有数据,则返回null
// 获取交通占比众数
Integer mostFrequentTrafficCompositionRate = dataCollection.stream()
.collect(Collectors.groupingBy(DcGantryMetricsStatisticsData::getTrafficCompositionRate, Collectors.counting()))
.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElse(null); // 如果没有数据,则返回null
// 获取饱和度平均值
Double saturationRateAverage = dataCollection.stream()
.mapToDouble(DcGantryMetricsStatisticsData::getSaturationRate)
.average()
.orElse(0.00); // 如果没有数据,则返回0.0
aggregatedData.setCrowdingRate(mostFrequentCrowdingRate);
aggregatedData.setTrafficCompositionRate(mostFrequentTrafficCompositionRate);
aggregatedData.setSaturationRate(saturationRateAverage);
// 使用第一个数据项的信息填充汇总统计对象的基本属性
DcGantryMetricsStatisticsData firstDcGantryMetricsStatisticsData = dataCollection.iterator().next();
// 设置门架指标标识
aggregatedData.setGantryCode(firstDcGantryMetricsStatisticsData.getGantryCode());
// 设置统计时间
aggregatedData.setStatisticalDate(firstDcGantryMetricsStatisticsData.getStatisticalDate(), trafficDataPeriodType);
// 上报时间
aggregatedData.setReportTime(firstDcGantryMetricsStatisticsData.getReportTime());
// 时段类型
aggregatedData.setPeriodType(trafficDataPeriodType);
// 生成主键
aggregatedData.generateUniqueId();
return aggregatedData;
}
}

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

@ -17,6 +17,7 @@ import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@ -43,8 +44,12 @@ public class TrafficGantryStatistics {
@Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务
public void performHourlyCleanupAndPersist() {
List<DcGantryStatisticsData> lastHourData = dcGantryStatisticsDataService.lastHourData();
// 数据库批量插入最近一小时的数据
dcGantryStatisticsDataMapper.insertOrUpdateBatch(lastHourData);
// 添加日门架数据到缓存中
dcGantryStatisticsDataService.lastHourData().forEach(DailyTrafficGantryStatisticsCache::addCacheData);
lastHourData.forEach(DailyTrafficGantryStatisticsCache::addCacheData);
// 清除已过期的缓存数据
DailyTrafficGantryStatisticsCache.clearExpiredData();

77
zc-business/src/main/resources/mapper/business/DcGantryMetricsStatisticsDataMapper.xml

@ -0,0 +1,77 @@
<?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.DcGantryMetricsStatisticsDataMapper">
<!-- 插入或更新门架指标数据 -->
<insert id="insertOrUpdate" parameterType="com.zc.business.domain.DcGantryMetricsStatisticsData">
INSERT INTO
dc_gantry_metrics_statistics_data
(
id,
gantry_code,
statistical_date,
period_type,
crowding_rate,
traffic_composition_rate,
saturation_rate,
create_time
)
VALUES
(
#{id},
#{gantryCode},
#{statisticalDate},
#{periodType},
#{crowdingRate},
#{trafficCompositionRate},
#{saturationRate},
NOW())
ON DUPLICATE KEY UPDATE
gantry_code = VALUES(gantry_code),
statistical_date = VALUES(statistical_date),
period_type = VALUES(period_type),
crowding_rate = VALUES(crowding_rate),
traffic_composition_rate = VALUES(traffic_composition_rate),
saturation_rate = VALUES(saturation_rate),
update_time = NOW()
</insert>
<insert id="insertOrUpdateBatch" parameterType="list">
INSERT INTO
dc_gantry_metrics_statistics_data
(
id,
gantry_code,
statistical_date,
period_type,
crowding_rate,
traffic_composition_rate,
saturation_rate,
create_time
)
VALUES
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id},
#{item.gantryCode},
#{item.statisticalDate},
#{item.periodType},
#{item.crowdingRate},
#{item.trafficCompositionRate},
#{item.saturationRate},
NOW()
)
</foreach>
ON DUPLICATE KEY UPDATE
gantry_code = VALUES(gantry_code),
statistical_date = VALUES(statistical_date),
period_type = VALUES(period_type),
crowding_rate = VALUES(crowding_rate),
traffic_composition_rate = VALUES(traffic_composition_rate),
saturation_rate = VALUES(saturation_rate),
update_time = NOW()
</insert>
</mapper>

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

@ -81,6 +81,84 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
type6_special_vehicle_flow = VALUES(type6_special_vehicle_flow)
</insert>
<insert id="insertOrUpdateBatch" parameterType="list">
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
<foreach collection="list" item="item" index="index" separator=",">
(
#{item.id},
#{item.gantryCode},
#{item.statisticalDate},
#{item.trafficVolume},
#{item.periodType},
NOW(),
#{item.type1PassengerFlow},
#{item.type2PassengerFlow},
#{item.type3PassengerFlow},
#{item.type4PassengerFlow},
#{item.type1TruckFlow},
#{item.type2TruckFlow},
#{item.type3TruckFlow},
#{item.type4TruckFlow},
#{item.type5TruckFlow},
#{item.type6TruckFlow},
#{item.type1SpecialVehicleFlow},
#{item.type2SpecialVehicleFlow},
#{item.type3SpecialVehicleFlow},
#{item.type4SpecialVehicleFlow},
#{item.type5SpecialVehicleFlow},
#{item.type6SpecialVehicleFlow}
)
</foreach>
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)

Loading…
Cancel
Save