diff --git a/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java b/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java index 6cd209aa..68833dca 100644 --- a/ruoyi-common/src/main/java/com/zc/common/core/httpclient/OkHttp.java +++ b/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; diff --git a/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java b/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java index cb2eb3a4..2749a92c 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcTrafficStatisticsController.java +++ b/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 = 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 dcStatisticsData = dcGantryStatisticsDataService.gantryData(request); // 将查询结果封装为成功响应并返回 return AjaxResult.success(dcStatisticsData); } + /** + * 获取门架指标统计数据 + * + * @param request 包含查询条件的请求对象,用于筛选历史门架指标统计数据 + * @return 返回一个AjaxResult对象,其中包含了查询到的门架指标统计数据列表 + */ + @ApiOperation("获取门架交通特指数数据") + @GetMapping("/history/gantry-metrics") + public AjaxResult historyGantryMetrics(DcGantryMetricsStatisticsData request){ + // 调用服务层方法,根据请求条件查询历史车门架数据 + List dcStatisticsData = dcGantryMetricsStatisticsDataService.gantryMetricsData(request); + // 将查询结果封装为成功响应并返回 + return AjaxResult.success(dcStatisticsData); + } /********************************************* 智慧高速平接口 **************************************************/ diff --git a/zc-business/src/main/java/com/zc/business/domain/DcGantryMetricsStatisticsData.java b/zc-business/src/main/java/com/zc/business/domain/DcGantryMetricsStatisticsData.java new file mode 100644 index 00000000..a49460f1 --- /dev/null +++ b/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)); + } +} diff --git a/zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java b/zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java index fd9432dc..c2ba886c 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java +++ b/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; diff --git a/zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java b/zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java index 789b26e8..76dbdbc9 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java +++ b/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); diff --git a/zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java b/zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java index 965b370f..225360ab 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java +++ b/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; diff --git a/zc-business/src/main/java/com/zc/business/domain/DcTrafficFlowStatisticsData.java b/zc-business/src/main/java/com/zc/business/domain/DcTrafficFlowStatisticsData.java new file mode 100644 index 00000000..2ac79589 --- /dev/null +++ b/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; +} diff --git a/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java b/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java index 5ac4e709..117b3f3d 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcTrafficSectionData.java +++ b/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)); } } diff --git a/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java b/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java index 67e3e584..b0b9c731 100644 --- a/zc-business/src/main/java/com/zc/business/enums/ChannelCongestionLevelEnum.java +++ b/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); + } + + } diff --git a/zc-business/src/main/java/com/zc/business/enums/TrafficCompositionRateEnum.java b/zc-business/src/main/java/com/zc/business/enums/TrafficCompositionRateEnum.java new file mode 100644 index 00000000..4556360d --- /dev/null +++ b/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); + } + +} + diff --git a/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java b/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java index 796e6311..0c1360a8 100644 --- a/zc-business/src/main/java/com/zc/business/enums/TrafficDataPeriodTypeEnum.java +++ b/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; diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcGantryMetricsStatisticsDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcGantryMetricsStatisticsDataMapper.java new file mode 100644 index 00000000..51d4288b --- /dev/null +++ b/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 { + + /** + * 插入或更新门架指标数据。 + * + * @param gantryTrafficMetricsData 门架指标数据对象,包含需要插入或更新的数据。 + * @return 返回一个布尔值,表示操作是否成功。true表示插入或更新成功,false表示失败。 + */ + boolean insertOrUpdate(DcGantryMetricsStatisticsData gantryTrafficMetricsData); + + /** + * 批量插入或更新新门架指标数据 + * + * @param gantryTrafficMetricsDataList 要插入或更新的新门架指标数据对象列表 + * @return 影响的记录数(通常对于批量操作,这个值可能不准确,因为每个 INSERT/UPDATE 可能影响不同的记录数) + */ + int insertOrUpdateBatch(List gantryTrafficMetricsDataList); +} diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java index b7eb758a..ad130f42 100644 --- a/zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java +++ b/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 dcGantryStatisticsDataList); } diff --git a/zc-business/src/main/java/com/zc/business/service/IDcGantryMetricsStatisticsDataService.java b/zc-business/src/main/java/com/zc/business/service/IDcGantryMetricsStatisticsDataService.java new file mode 100644 index 00000000..b6a9a76e --- /dev/null +++ b/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对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的DcGantryMetricsStatisticsData数据列表。 + */ + List lastHourData(); + + /** + * 获取当月的DcGantryMetricsStatisticsData数据列表。 + * + *

此方法不接受任何参数,返回一个包含当月所有符合条件的DcGantryMetricsStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的DcGantryMetricsStatisticsData数据列表。 + */ + List currentMonthData(); + + /** + * 根据请求条件获取门架指标统计数据 + * @param request 请求条件 + * @return 门架指标统计数据列表 + */ + List gantryMetricsData(DcGantryMetricsStatisticsData request); +} diff --git a/zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java b/zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java index e6b95a43..8712f0b2 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java +++ b/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 lastHourData(); + /** + * 获取当月的门架统计数据列表。 + * + *

此方法不接受任何参数,返回一个包含当月所有符合条件的DcGantryStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的DcGantryStatisticsData数据列表。 + */ + List currentMonthData(); + /** * 进行门架数据统计的函数。 * 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数,用来包含门架统计所需的各项数据, diff --git a/zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java b/zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java index eeb3a043..ce64e009 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcTollStationStatisticsDataService.java +++ b/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; diff --git a/zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java b/zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java index cb272e49..f89a5b86 100644 --- a/zc-business/src/main/java/com/zc/business/service/IDcTrafficStatisticsService.java +++ b/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 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; + } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java index ed8d6d38..9102d605 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java +++ b/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 { + // 获取当月门架数据 + List dcGantryStatisticsDataList = currentMonthData(); + recoveryHourlyCache(dcGantryStatisticsDataList); // 恢复小时缓存数据 + recoveryDailyCache(dcGantryStatisticsDataList); // 从数据库中恢复天的缓存数据(当月天的缓存数据) + recoveryMonthlyCache(); // 恢复每月交通门架缓存 + recoveryQuarterlyCache(); // 恢复每季度交通门架缓存 + recoveryYearlyCache(); // 恢复每年交通门架缓存 + }); } @@ -68,6 +81,22 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl此方法不接受任何参数,返回一个包含当月所有符合条件的DcGantryStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的DcGantryStatisticsData数据列表。 + */ + @Override + public List currentMonthData() { + // 获取门架这个月的数据 + List odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData(); + + return odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList); + } + /** * 进行门架数据统计的函数。 * 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数,用来包含门架统计所需的各项数据, @@ -87,41 +116,41 @@ public class DcGantryStatisticsDataImpl extends ServiceImpl 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 dcGantryStatisticsDataList) { + dcGantryStatisticsDataMapper.insertOrUpdateBatch(dcGantryStatisticsDataList); + } + /** * 恢复日缓存数据的方法 * 该方法首先会获取当前月份的门架入口数据, * 然后分别计算每个门架的统计信息,并将这些统计信息添加到每日交通门架统计缓存中。 */ - private void recoveryDailyCache() { - - // 获取当月门架数据 - List odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData(); - + private void recoveryDailyCache(List dcGantryStatisticsDataList) { // 计算每个门架数据的统计信息,并添加到每日门架统计缓存中 - odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList).forEach(DailyTrafficGantryStatisticsCache::addCacheData); + dcGantryStatisticsDataList.forEach(DailyTrafficGantryStatisticsCache::addCacheData); } /** diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java index e8555f5d..62483254 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java +++ b/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 { + recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据) + recoveryMonthlyCache(); // 恢复每月交通收费站站点缓存 + recoveryQuarterlyCache(); // 恢复每季度交通收费站站点缓存 + recoveryYearlyCache(); // 恢复每年交通收费站站点缓存 + }); } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java index e3d0e099..3af297bb 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficSectionStatisticsServiceImpl.java +++ b/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(); // 恢复每年交通数据缓存 + }); } /** diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java index af3b68d7..0acc9c6b 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcTrafficStatisticsServiceImpl.java +++ b/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 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 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(); + } + + } diff --git a/zc-business/src/main/java/com/zc/business/service/impl/IDcGantryMetricsStatisticsDataServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/IDcGantryMetricsStatisticsDataServiceImpl.java new file mode 100644 index 00000000..3c5e1a8c --- /dev/null +++ b/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 + 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 gantryMetricsStatisticsDataList = currentMonthData(); + recoveryHourlyCache(gantryMetricsStatisticsDataList); // 恢复小时缓存数据 + recoveryDailyCache(gantryMetricsStatisticsDataList); // 恢复日缓存数据 + recoveryMonthlyCache(); // 恢复每月交通门架缓存 + recoveryQuarterlyCache(); // 恢复每季度交通门架缓 + recoveryYearlyCache(); // 恢复每年交通门架缓存 + }); + + } + + /** + * 获取过去一小时的DcGantryMetricsStatisticsData数据列表。 + * + *

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的DcGantryMetricsStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的DcGantryMetricsStatisticsData数据列表。 + */ + @Override + public List 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<>(); + } + } + + /** + * 获取当月的门架指标数据列表。 + * + *

此方法不接受任何参数,返回一个包含当月所有符合条件的DcGantryMetricsStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的DcGantryMetricsStatisticsData数据列表。 + */ + @Override + public List 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 gantryMetricsData(DcGantryMetricsStatisticsData request) { + if (request.getStartTime() == null || request.getEndTime() == null) { + throw new ServiceException("开始时间或结束时间不能为空"); + } + + if (request.getPeriodType() == null) { + throw new ServiceException("时段类型不能为空"); + } + + LambdaQueryWrapper 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 gantryMetricsStatisticsDataList) { + gantryMetricsStatisticsDataMapper.insertOrUpdateBatch(gantryMetricsStatisticsDataList); + } + + /** + * 恢复日缓存数据的方法 + * 该方法首先会获取当前月份的门架入口数据, + * 然后分别计算每个门架的统计信息,并将这些统计信息添加到每日交通门架指标统计缓存中。 + */ + private void recoveryDailyCache(List gantryMetricsStatisticsDataList) { + + // 计算每个门架数指标据的统计信息,并添加到每日门架指标统计缓存中 + gantryMetricsStatisticsDataList.forEach(DailyGantryMetricsStatisticsCache::addCacheData); + + } + + /** + * 恢复每月交通门架缓存的方法。 + * 通过查询当前月份至今的每日交通数据,并将其添加到每月门架指标统计缓存中。 + */ + private void recoveryMonthlyCache() { + // 构建查询条件,查询当前月份至今的每日交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.DAY); + queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date()); + List dcGantryMetricsStatisticsDataList = this.list(queryWrapper); + // 遍历查询结果,将每日数据添加到每月门架指标统计缓存中 + dcGantryMetricsStatisticsDataList.forEach(MonthlyGantryMetricsStatisticsCache::addCacheData); + } + + /** + * 恢复每季度交通门架缓存的方法。 + * 通过查询当前季度至今的每月交通数据,并将其添加到每季度门架指标统计缓存中。 + */ + private void recoveryQuarterlyCache() { + // 构建查询条件,查询当前季度至今的每月交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH); + queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date()); + List dcGantryMetricsStatisticsDataList = this.list(queryWrapper); + // 遍历查询结果,将每月数据添加到每季度门架指标统计缓存 + dcGantryMetricsStatisticsDataList.forEach(QuarterlyGantryMetricsStatisticsCache::addCacheData); + } + + /** + * 恢复每年交通门架缓存的方法。 + * 通过查询当前年份至今的每季度交通数据,并将其添加到每年门架指标统计缓存中。 + */ + private void recoveryYearlyCache() { + // 构建查询条件,查询当前年份至今的每季度交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryMetricsStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER); + queryWrapper.between(DcGantryMetricsStatisticsData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date()); + List dcGantryMetricsStatisticsDataList = this.list(queryWrapper); + // 遍历查询结果,将每季度数据添加到每年门架指标统计缓存 + dcGantryMetricsStatisticsDataList.forEach(YearlyGantryMetricsStatisticsCache::addCacheData); + } + + /** + * 计算门架指标数据 + * + * @param jsonArray 门架指标数据 + * @return 门架指标数据列表 + */ + private List calculateGantryMetricsStatistics(JSONArray jsonArray) { + + if (jsonArray == null || jsonArray.isEmpty()) { + return new ArrayList<>(); + } + + List 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; + } + + +} diff --git a/zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java index 9b4bcea5..74f216e3 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/OdsTollEtctuDataImpl.java @@ -201,7 +201,7 @@ public class OdsTollEtctuDataImpl extends ServiceImpl { + + // 静态缓存容器,使用ConcurrentHashMap保证线程安全 + @Getter + private static final Map 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()); + } + + /** + * 生成缓存键。 + *

此方法通过组合门架编号、统计日期来生成一个唯一的缓存键。

+ * + * @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 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); + } +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/MonthlyGantryMetricsStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/MonthlyGantryMetricsStatisticsCache.java new file mode 100644 index 00000000..bec485f7 --- /dev/null +++ b/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 { + + // 静态缓存容器,使用ConcurrentHashMap保证线程安全 + @Getter + private static final Map 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); + } + + /** + * 获取月门架信息缓存的实例。 + *

+ * 根据传入的门架在一定周期(月)内的统计数据,计算并返回对应的缓存实例。 + * 如果缓存中已存在该实例,则直接返回;否则,创建新的缓存实例并加入缓存。 + *

+ * + * @param dcGantryMetricsStatisticsData 门架统计数据,用于生成缓存键。 + * @return 缓存中的或新创建的月门架统计信息缓存实例。 + */ + private static MonthlyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) { + // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 + return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new MonthlyGantryMetricsStatisticsCache()); + } + + /** + * 生成缓存键。 + *

此方法通过组合门架编号、统计日期和访问类型来生成一个唯一的缓存键。

+ * + * @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 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"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/QuarterlyGantryMetricsStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/QuarterlyGantryMetricsStatisticsCache.java new file mode 100644 index 00000000..82848fa4 --- /dev/null +++ b/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 { + + // 静态缓存容器,使用ConcurrentHashMap保证线程安全 + @Getter + private static final Map 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); + } + + /** + * 获取季度门架统计信息缓存的实例。 + *

+ * 根据传入的门架在一定周期(季度)内的统计数据,计算并返回对应的缓存实例。 + * 如果缓存中已存在该实例,则直接返回;否则,创建新的缓存实例并加入缓存。 + *

+ * + * @param dcGantryMetricsStatisticsData 门架统计数据,用于生成缓存键。 + * @return 缓存中的或新创建的季度门架统计信息缓存实例。 + */ + private static QuarterlyGantryMetricsStatisticsCache getInstance(DcGantryMetricsStatisticsData dcGantryMetricsStatisticsData) { + // 根据传入数据生成唯一键,并尝试从缓存中获取或创建新的缓存实例 + return cache.computeIfAbsent(generateCacheKey(dcGantryMetricsStatisticsData), k -> new QuarterlyGantryMetricsStatisticsCache()); + } + + + /** + * 生成缓存键。 + *

此方法通过组合门架编号、统计日期和访问类型来生成一个唯一的缓存键。

+ * + * @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 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"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/YearlyGantryMetricsStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/metrics/YearlyGantryMetricsStatisticsCache.java new file mode 100644 index 00000000..418ecba6 --- /dev/null +++ b/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 { + + // 静态缓存容器,使用ConcurrentHashMap保证线程安全 + @Getter + private static final Map 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()); + } + + /** + * 生成缓存键。 + *

此方法通过组合门架编号、统计日期来生成一个唯一的缓存键。

+ * + * @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 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"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryMetricsStatistics.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryMetricsStatistics.java new file mode 100644 index 00000000..866c5c99 --- /dev/null +++ b/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 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> cache, + TrafficDataPeriodTypeEnum periodType, + Consumer consumer + ) { + for (AbstractTrafficStatisticsCache 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 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; + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java index 38751abf..f309afa6 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java +++ b/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 lastHourData = dcGantryStatisticsDataService.lastHourData(); + // 数据库批量插入最近一小时的数据 + dcGantryStatisticsDataMapper.insertOrUpdateBatch(lastHourData); + // 添加日门架数据到缓存中 - dcGantryStatisticsDataService.lastHourData().forEach(DailyTrafficGantryStatisticsCache::addCacheData); + lastHourData.forEach(DailyTrafficGantryStatisticsCache::addCacheData); // 清除已过期的缓存数据 DailyTrafficGantryStatisticsCache.clearExpiredData(); diff --git a/zc-business/src/main/resources/mapper/business/DcGantryMetricsStatisticsDataMapper.xml b/zc-business/src/main/resources/mapper/business/DcGantryMetricsStatisticsDataMapper.xml new file mode 100644 index 00000000..f72efc5f --- /dev/null +++ b/zc-business/src/main/resources/mapper/business/DcGantryMetricsStatisticsDataMapper.xml @@ -0,0 +1,77 @@ + + + + + + + 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 INTO + dc_gantry_metrics_statistics_data + ( + id, + gantry_code, + statistical_date, + period_type, + crowding_rate, + traffic_composition_rate, + saturation_rate, + create_time + ) + VALUES + + ( + #{item.id}, + #{item.gantryCode}, + #{item.statisticalDate}, + #{item.periodType}, + #{item.crowdingRate}, + #{item.trafficCompositionRate}, + #{item.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() + + + diff --git a/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml b/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml index 851f7fe9..6291aaf0 100644 --- a/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml +++ b/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 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 + + ( + #{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} + ) + + 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) + +