diff --git a/zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java b/zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java index e21493ce..3cfdf97f 100644 --- a/zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java +++ b/zc-business/src/main/java/com/zc/business/controller/DcEmergencyPlansController.java @@ -14,6 +14,8 @@ import io.swagger.annotations.ApiParam; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; + +import java.util.ArrayList; import java.util.List; /** @@ -68,15 +70,16 @@ public class DcEmergencyPlansController extends BaseController { } /** - * 感知事件-根据事件数据查询事件预案列表 + * 交通事件-切换智能发布生成内容 */ -// @ApiOperation("感知事件-根据事件数据查询事件预案列表") - @PreAuthorize("@ss.hasPermi('business:plans:list')") - @PostMapping("/list/warning/type") - public AjaxResult listByEventType(@RequestBody DcWarning dcWarning) { - - List dcEmergencyPlansList = dcEmergencyPlansService.selectDcEmergencyPlansByWarningType(dcWarning); - return AjaxResult.success(dcEmergencyPlansList); + @ApiOperation("交通事件-切换智能发布生成内容") +// @PreAuthorize("@ss.hasPermi('business:plans:list')") + @PostMapping("/list/event/emergencyPlans") + public AjaxResult switchIntelligentPublishingToContent(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { + List dcEmergencyPlansList = new ArrayList<>(); + dcEmergencyPlansList.add(dcEventAnDcEmergencyPlans.getDcEmergencyPlans()); + dcEmergencyPlansService.dispositionDeviceContent(dcEmergencyPlansList,dcEventAnDcEmergencyPlans.getDcEvent()); + return AjaxResult.success(dcEmergencyPlansList.get(0)); } /** @@ -89,25 +92,6 @@ public class DcEmergencyPlansController extends BaseController { return AjaxResult.success(dcEmergencyPlansService.eventBoardConfirm(dcEventAnDcEmergencyPlans)); } - /** - * 感知事件-情报板确认回显原始模板 - */ -// @ApiOperation("感知事件-情报板确认回显原始模板") - @PreAuthorize("@ss.hasPermi('business:plans:list')") - @PostMapping("/warning/board/confirm") - public AjaxResult warningBoardConfirm(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - return AjaxResult.success(dcEmergencyPlansService.warningBoardConfirm(dcEventAnDcEmergencyPlans)); - } - - /** - * 感知事件-情报板自动生成文字 - */ -// @ApiOperation("感知事件-情报板自动生成文字") - @PostMapping("/warning/automatic") - public AjaxResult warningAutomaticGeneration(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - return AjaxResult.success(dcEmergencyPlansService.warningAutomaticGeneration(dcEventAnDcEmergencyPlans)); - } - /** * 交通事件-情报板自动生成文字 */ @@ -145,16 +129,6 @@ public class DcEmergencyPlansController extends BaseController { return AjaxResult.success(dcEmergencyPlansService.selectEventPlanAssocById(assocId)); } - /** - * 感知事件确定 - */ -// @ApiOperation("感知事件确认") - @PreAuthorize("@ss.hasPermi('business:plans:edit')") - @PostMapping("/warning/confirm") - public AjaxResult warningConfirm(@RequestBody DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - return AjaxResult.success(dcEmergencyPlansService.executionWarningConfirmation(dcEventAnDcEmergencyPlans)); - } - /** * 新增事件预案 */ 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 8990ffd0..89327486 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 @@ -1,11 +1,12 @@ package com.zc.business.controller; import com.ruoyi.common.core.domain.AjaxResult; -import com.zc.business.domain.DcCongestedSectionData; -import com.zc.business.domain.DcTrafficMetricsData; -import com.zc.business.domain.DcTrafficSectionData; +import com.zc.business.domain.*; import com.zc.business.request.DcTrafficMetricsDataRequest; import com.zc.business.request.DcTrafficSectionDataRequest; +import com.zc.business.service.IDcGantryStatisticsDataService; +import com.zc.business.service.IDcTollStationStatisticsDataService; +import com.zc.business.service.IDcTrafficSectionStatisticsService; import com.zc.business.service.IDcTrafficStatisticsService; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; @@ -24,9 +25,17 @@ import java.util.List; @RequestMapping("/business/traffic-statistics") public class DcTrafficStatisticsController { + @Autowired + private IDcTrafficSectionStatisticsService dcTrafficSectionStatisticsService; + @Autowired private IDcTrafficStatisticsService dcTrafficStatisticsService; + @Autowired + private IDcTollStationStatisticsDataService dcTollStationStatisticsDataService; + + @Autowired + private IDcGantryStatisticsDataService dcGantryStatisticsDataService; /** * 获取当前交通截面数据 @@ -38,7 +47,7 @@ public class DcTrafficStatisticsController { @GetMapping("/current/sections") public AjaxResult currentSections(DcTrafficSectionDataRequest request){ // 调用服务层方法,查询当前交通截面数据 - List dcTrafficMetricsData = dcTrafficStatisticsService.currentSections(request); + List dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentSections(request); // 将查询结果封装为AjaxResult对象返回 return AjaxResult.success(dcTrafficMetricsData); } @@ -53,7 +62,7 @@ public class DcTrafficStatisticsController { @GetMapping("/history/sections") public AjaxResult historySections(DcTrafficSectionDataRequest request){ // 调用服务层方法,获取历史交通截面的统计数据 - List dcTrafficMetricsData = dcTrafficStatisticsService.historySections(request); + List dcTrafficMetricsData = dcTrafficSectionStatisticsService.historySections(request); // 将获取到的历史交通截面数据封装成AjaxResult对象并返回 return AjaxResult.success(dcTrafficMetricsData); } @@ -69,7 +78,7 @@ public class DcTrafficStatisticsController { @GetMapping("/current/metrics") public AjaxResult currentTrafficMetrics(DcTrafficMetricsDataRequest request){ // 调用服务层方法,获取当前交通指标数据 - List dcTrafficMetricsData = dcTrafficStatisticsService.currentTrafficMetrics(request); + List dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentTrafficMetrics(request); // 将获取到的交通指标数据封装为成功的结果并返回 return AjaxResult.success(dcTrafficMetricsData); } @@ -84,7 +93,7 @@ public class DcTrafficStatisticsController { @GetMapping("/history/metrics") public AjaxResult historyTrafficMetrics(DcTrafficMetricsDataRequest request){ // 调用服务层方法,查询历史交通特征指数数据 - List dcTrafficMetricsDataList = dcTrafficStatisticsService.historyTrafficMetrics(request); + List dcTrafficMetricsDataList = dcTrafficSectionStatisticsService.historyTrafficMetrics(request); // 将查询结果封装成成功响应并返回 return AjaxResult.success(dcTrafficMetricsDataList); } @@ -99,9 +108,58 @@ public class DcTrafficStatisticsController { @GetMapping("/current/congested") public AjaxResult currentCongestedSection(Byte direction){ // 调用服务层方法,获取当前交通指标数据 - List dcTrafficMetricsData = dcTrafficStatisticsService.currentCongestedSection(direction); + List dcTrafficMetricsData = dcTrafficSectionStatisticsService.currentCongestedSection(direction); // 将获取到的交通指标数据封装为成功的结果并返回 return AjaxResult.success(dcTrafficMetricsData); } + /** + * 获取累计车流量 + * + * 本方法用于根据特定条件查询历史车流量数据,通过接收一个包含查询条件的请求对象, + * 调用服务层方法进行数据查询,并将查询结果封装成AjaxResult对象返回。 + * + * @param request 包含查询条件的请求对象,用于获取特定条件下的历史车流量数据。请求对象中可能包含了时间范围、地点等查询条件。 + * @return 返回一个AjaxResult对象,其中包含了查询到的历史车流量数据列表。AjaxResult是本系统中用于封装响应数据的通用对象,其中的success方法用于将查询结果封装为成功响应返回。 + */ + @ApiOperation("获取累计车流量") + @GetMapping("/history/flow") + public AjaxResult historyFlow(DcStatisticsData request){ + // 调用服务层方法,根据请求条件查询历史车流量数据 + List dcStatisticsData = dcTrafficStatisticsService.historyFlow(request); + // 将查询结果封装为成功响应并返回 + return AjaxResult.success(dcStatisticsData); + } + + /** + * 获取收费站统计数据 + * + * @param request 包含查询条件的请求对象,用于筛选历史收费站统计数据 + * @return 返回一个AjaxResult对象,其中包含了查询到的收费站统计数据列表 + */ + @ApiOperation("获取收费站统计数据") + @GetMapping("/history/toll-station") + public AjaxResult historyTollStation(DcTollStationStatisticsData request){ + // 调用服务层方法,根据请求条件查询历史车收费站数据 + List dcStatisticsData = dcTollStationStatisticsDataService.tollStationData(request); + // 将查询结果封装为成功响应并返回 + return AjaxResult.success(dcStatisticsData); + } + + /** + * 获取门架统计数据 + * + * @param request 包含查询条件的请求对象,用于筛选历史门架统计数据 + * @return 返回一个AjaxResult对象,其中包含了查询到的门架统计数据列表 + */ + @ApiOperation("获取门架统计数据") + @GetMapping("/history/gantry") + public AjaxResult historyTollStation(DcGantryStatisticsData request){ + // 调用服务层方法,根据请求条件查询历史车门架数据 + List dcStatisticsData = dcGantryStatisticsDataService.gantryData(request); + // 将查询结果封装为成功响应并返回 + return AjaxResult.success(dcStatisticsData); + } + + } diff --git a/zc-business/src/main/java/com/zc/business/domain/DcFacility.java b/zc-business/src/main/java/com/zc/business/domain/DcFacility.java index 616f2d2f..3feb40e6 100644 --- a/zc-business/src/main/java/com/zc/business/domain/DcFacility.java +++ b/zc-business/src/main/java/com/zc/business/domain/DcFacility.java @@ -23,9 +23,9 @@ public class DcFacility { private String direction; @ApiModelProperty("道路标识") private String roadId; - @ApiModelProperty("设备类型") + @ApiModelProperty("设施类型") private Integer facilityType; - @ApiModelProperty("设备名称") + @ApiModelProperty("设施名称") private String facilityName; @ApiModelProperty("备注") private String remark; @@ -35,5 +35,6 @@ public class DcFacility { private Date createTime; @ApiModelProperty("修改时间") private Date updateTime; - + @ApiModelProperty("设施编号") + private String facilityCode; } 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 new file mode 100644 index 00000000..fd9432dc --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/DcGantryStatisticsData.java @@ -0,0 +1,67 @@ +package com.zc.business.domain; + +import cn.hutool.core.date.DateUtil; +import lombok.Data; +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 门架数据统计实体类 + * @author xiepufeng + */ +@Data +public class DcGantryStatisticsData extends DcStatisticsData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 门架标识 + */ + private String gantryCode; + + /** + * 重写equals方法,用于比较两个DcGantryStatisticsData对象是否相等。 + * + * @param o 要与当前对象比较的对象。 + * @return 如果两个对象相等,则返回true;否则返回false。 + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; // 比较是否为同一对象,是则直接返回true + + if (o == null || getClass() != o.getClass()) return false; // 比较对象是否为空或类型是否不同,是则返回false + + DcGantryStatisticsData that = (DcGantryStatisticsData) o; // 类型转换 + + // 使用Objects.equals方法比较对象的各个属性值 + return Objects.equals(gantryCode, that.gantryCode) && + Objects.equals(getReportTime(), that.getReportTime()) && + Objects.equals(getPeriodType(), that.getPeriodType()); + } + + + /** + * 重写hashCode方法,基于对象的属性生成哈希码。 + * @return 对象的哈希码值。 + */ + @Override + public int hashCode() { + return Objects.hash(gantryCode, getReportTime(), getPeriodType()); + } + + + /** + * 生成唯一标识符。 + * 该方法通过将多个属性结合,然后对结合后的字符串进行MD5哈希处理来生成一个唯一标识符。 + * 结合的属性包括:门架站点标识、统计日期、道路方向、统计粒度和出入类型。 + */ + @Override + public void generateUniqueId() { + // 将多个属性按照指定格式组合成一个字符串 + String combinedAttributes = gantryCode + "_" + DateUtil.format(getStatisticalDate(), "yyyyMMdd_HHmmss") + "_" + getPeriodType(); + // 对组合后的字符串进行MD5哈希处理,生成唯一标识符,并赋值给当前对象的id属性 + this.setId(DigestUtils.md5Hex(combinedAttributes)); + } +} 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 new file mode 100644 index 00000000..789b26e8 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/DcStatisticsData.java @@ -0,0 +1,200 @@ +package com.zc.business.domain; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.annotation.TableField; +import com.fasterxml.jackson.annotation.JsonFormat; +import com.ruoyi.common.core.domain.BaseEntity; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import lombok.Data; +import org.springframework.format.annotation.DateTimeFormat; + +import java.io.Serializable; +import java.util.Date; + +@Data +public class DcStatisticsData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 主键 + */ + private String id; + + /** + * 统计时间 + */ + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + private Date statisticalDate; + + /** + * 上报时间 + */ + @TableField(exist = false) + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8") + private Date reportTime; + + /** + * 车流量 + */ + private Integer trafficVolume; + + /** + * 统计粒度 + * 1-年 + * 2-月 + * 3-季 + * 4-日 + */ + private Byte periodType; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 修改时间 + */ + private Date updateTime; + + /** + * 1型客车车流量 + */ + private Integer type1PassengerFlow; + + /** + * 2型客车车流量 + */ + private Integer type2PassengerFlow; + + /** + * 3型客车车流量 + */ + private Integer type3PassengerFlow; + + /** + * 4型客车车流量 + */ + private Integer type4PassengerFlow; + + /** + * 1型货车车流量 + */ + private Integer type1TruckFlow; + + /** + * 2型货车车流量 + */ + private Integer type2TruckFlow; + + /** + * 3型货车车流量 + */ + private Integer type3TruckFlow; + + /** + * 4型货车车流量 + */ + private Integer type4TruckFlow; + + /** + * 5型货车车流量 + */ + private Integer type5TruckFlow; + + /** + * 6型货车车流量 + */ + private Integer type6TruckFlow; + + /** + * 1型专项作业车车流量 + */ + private Integer type1SpecialVehicleFlow; + + /** + * 2型专项作业车车流量 + */ + private Integer type2SpecialVehicleFlow; + + /** + * 3型专项作业车车流量 + */ + private Integer type3SpecialVehicleFlow; + + /** + * 4型专项作业车车流量 + */ + private Integer type4SpecialVehicleFlow; + + /** + * 5型专项作业车车流量 + */ + private Integer type5SpecialVehicleFlow; + + /** + * 6型专项作业车车流量 + */ + private Integer type6SpecialVehicleFlow; + + /** + * 开始时间 + */ + @TableField(exist = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date startTime; + + /** + * 结束时间 + */ + @TableField(exist = false) + @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") + private Date endTime; + + public void setPeriodType(Byte periodType) { + this.periodType = periodType; + } + + /** + * 设置交通数据的统计周期类型。 + * @param periodType 统计周期类型的枚举值。 + */ + public void setPeriodType(TrafficDataPeriodTypeEnum periodType) { + this.periodType = periodType.getCode(); // 将枚举类型转换为代码值存储 + } + + /** + * 根据给定的统计周期类型和日期,设置统计日期为相应周期的起始日期。 + * @param statisticalDate 原始统计日期。 + * @param typeEnum 统计周期类型。 + */ + public void setStatisticalDate(Date statisticalDate, TrafficDataPeriodTypeEnum typeEnum) { + switch (typeEnum) { + case DAY: + // 设置为当天的起始时间 + this.statisticalDate = DateUtil.beginOfDay(statisticalDate); + break; + case MONTH: + // 设置为当月的起始日期 + this.statisticalDate = DateUtil.beginOfMonth(statisticalDate); + break; + case QUARTER: + // 设置为当季度的起始日期 + this.statisticalDate = DateUtil.beginOfQuarter(statisticalDate); + break; + case YEAR: + // 设置为当年的起始日期 + this.statisticalDate = DateUtil.beginOfYear(statisticalDate); + break; + default: + // 如果不是预定义的周期类型,则不做任何处理 + this.statisticalDate = statisticalDate; + } + } + + public void generateUniqueId() { + + } + +} 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 new file mode 100644 index 00000000..965b370f --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/DcTollStationStatisticsData.java @@ -0,0 +1,80 @@ +package com.zc.business.domain; + +import cn.hutool.core.date.DateUtil; +import lombok.Data; +import org.apache.commons.codec.digest.DigestUtils; + +import java.io.Serializable; +import java.util.Objects; + +/** + * 收费站数据统计实体类 + * @author xiepufeng + */ +@Data +public class DcTollStationStatisticsData extends DcStatisticsData implements Serializable { + + private static final long serialVersionUID = 1L; + + // 入口 + public static final byte ENTRANCE = 1; + // 出口 + public static final byte EXIT = 2; + + /** + * 收费站站点标识 + */ + private String tollStationCode; + + /** + * 出入类型 + * 1-入口 + * 2-出口 + */ + private Byte accessType; + + + /** + * 重写equals方法,用于比较两个DcTollStationStatisticsData对象是否相等。 + * + * @param o 要与当前对象比较的对象。 + * @return 如果两个对象相等,则返回true;否则返回false。 + */ + @Override + public boolean equals(Object o) { + if (this == o) return true; // 比较是否为同一对象,是则直接返回true + + if (o == null || getClass() != o.getClass()) return false; // 比较对象是否为空或类型是否不同,是则返回false + + DcTollStationStatisticsData that = (DcTollStationStatisticsData) o; // 类型转换 + + // 使用Objects.equals方法比较对象的各个属性值 + return Objects.equals(tollStationCode, that.tollStationCode) && + Objects.equals(getReportTime(), that.getReportTime()) && + Objects.equals(getPeriodType(), that.getPeriodType()) && + Objects.equals(accessType, that.accessType); + } + + + /** + * 重写hashCode方法,基于对象的属性生成哈希码。 + * @return 对象的哈希码值。 + */ + @Override + public int hashCode() { + return Objects.hash(tollStationCode, getReportTime(), getPeriodType(), accessType); + } + + /** + * 生成唯一标识符。 + * 该方法通过将多个属性结合,然后对结合后的字符串进行MD5哈希处理来生成一个唯一标识符。 + * 结合的属性包括:收费站站点标识、统计日期、道路方向、统计粒度和出入类型。 + */ + @Override + public void generateUniqueId() { + // 将多个属性按照指定格式组合成一个字符串 + String combinedAttributes = tollStationCode + "_" + DateUtil.format(getStatisticalDate(), "yyyyMMdd_HHmmss") + "_" + getPeriodType() + "_" + accessType; + // 对组合后的字符串进行MD5哈希处理,生成唯一标识符,并赋值给当前对象的id属性 + this.setId(DigestUtils.md5Hex(combinedAttributes)); + } +} diff --git a/zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java b/zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java index 98b5f157..50a71e48 100644 --- a/zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java +++ b/zc-business/src/main/java/com/zc/business/domain/EventPlanAssoc.java @@ -58,4 +58,10 @@ public class EventPlanAssoc { */ @ApiModelProperty("执行操作结果") private String controlResult; + + /** + * 执行操作 + */ + @ApiModelProperty("执行操作") + private String control; } diff --git a/zc-business/src/main/java/com/zc/business/domain/OdsTollEnpassData.java b/zc-business/src/main/java/com/zc/business/domain/OdsTollEnpassData.java new file mode 100644 index 00000000..0e2d3be1 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/OdsTollEnpassData.java @@ -0,0 +1,129 @@ +package com.zc.business.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 收费站入口流水数据 + * @author xiepufeng + */ +@Data +public class OdsTollEnpassData implements Serializable { + + private static final long serialVersionUID = 1L; + + + /** + * 入口通行时间 + */ + private Date entime; + + /** + * 交易流水号(主键) + */ + private String id; + + /** + * 交易编码 + */ + private String transcode; + + /** + * 车道类型 + */ + private Short lanetype; + + /** + * 入口站号 + */ + private String entollstation; + + /** + * 入口车道号 + */ + private String entolllane; + + /** + * 入口站HEX编码 + */ + private String entollstationhex; + + /** + * 入口车道HEX编码 + */ + private String entolllanehex; + + /** + * 入口站号(国标) + */ + private String entollstationid; + + /** + * 入口车道号(国标) + */ + private String entolllaneid; + + /** + * 通行介质类型:1-OBU,2-CPC卡,3-纸券,4-M1卡,9-无通行介质 + */ + private Short mediatype; + + /** + * 过车数量 + */ + private Integer vcount; + + /** + * 车牌号(经过MD5加密) + */ + @TableField("vlp_MD5") + private String vlpMD5; + + /** + * 车牌颜色 + */ + private Short vlpc; + + /** + * 车型:1-一型客车...(具体车型定义请参考注释) + */ + private Integer vehicletype; + + /** + * 车种:0-普通,8-军警,10-紧急...(具体车种定义请参考注释) + */ + private Short vehicleclass; + + /** + * 入口重量 + */ + private String enweight; + + /** + * 轴组信息 + */ + private String axisinfo; + + /** + * 限载总重(KG) + */ + private Integer limitweight; + + /** + * 超限率 + */ + private Short overweightrate; + + /** + * 入口轴数 + */ + private Integer enaxlecount; + + /** + * 通行标识ID + */ + private String passid; +} diff --git a/zc-business/src/main/java/com/zc/business/domain/OdsTollEtctuData.java b/zc-business/src/main/java/com/zc/business/domain/OdsTollEtctuData.java new file mode 100644 index 00000000..e0267dde --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/OdsTollEtctuData.java @@ -0,0 +1,118 @@ +package com.zc.business.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 门架流水数据实体类 + * @author xiepufeng + */ +@Data +public class OdsTollEtctuData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 计费交易时间 + */ + private Date transtime; + + /** + * 门架编号 + */ + private String gantryid; + + /** + * 计费交易编号(主键) + */ + private String tradeid; + + /** + * 门架顺序号 + */ + private Integer gantryordernum; + + /** + * 门架 Hex 值 + */ + private String gantryhex; + + /** + * 对向门架 Hex 值 + */ + private String gantryhexopposite; + + /** + * 通行介质类型:1-OBU,2-CPC卡,3-纸券,4-M1卡,9-无通行介质 + */ + private Integer mediatype; + + /** + * 收费单元编号组 + */ + private String tollintervalid; + + /** + * 车牌号(经过MD5加密) + */ + @TableField("vehicleplate_MD5") + private String vehicleplateMD5; + + /** + * 车型:1-一型客车...(具体车型定义请参考注释) + */ + private Integer vehicletype; + + /** + * 车种:0-普通,8-军警,10-紧急...(具体车种定义请参考注释) + */ + private Integer vehicleclass; + + /** + * 交易类型标识 + */ + private String transtype; + + /** + * 入口车道编号 + */ + private String entolllaneid; + + /** + * 入口站hex字符串 + */ + private String entollstationhex; + + /** + * 入口时间 + */ + private String entime; + + /** + * 入口车道类型 + */ + private String enlanetype; + + /** + * 通行标识ID + */ + private String passid; + + /** + * 交易结果:0-成功,1-失败 + */ + private Integer traderesult; + + /** + * 门架类型:1-路段,2-省界入口,3-省界出口 + */ + private String gantrytype; + + /** + * 计费车型:与车型字段定义相同 + */ + private Integer feevehicletype; +} diff --git a/zc-business/src/main/java/com/zc/business/domain/OdsTollExpassData.java b/zc-business/src/main/java/com/zc/business/domain/OdsTollExpassData.java new file mode 100644 index 00000000..a3bd6312 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/OdsTollExpassData.java @@ -0,0 +1,212 @@ +package com.zc.business.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 收费站出口流水数据实体类 + * @author xiepufeng + */ +@Data +public class OdsTollExpassData implements Serializable { + /** + * 出口时间 + */ + private Date extime; + + /** + * 交易流水号(主键) + */ + private String id; + + /** + * 交易支付方式:1-出口ETC通行,2-出口ETC刷卡通行,11-现金,12-其他第三方账户支付,13-银联卡支付,16-支付宝,17-微信 + */ + private Short transpaytype; + + /** + * 交易编码 + */ + private String transcode; + + /** + * 入口重量 + */ + private Integer enweight; + + /** + * 入口轴数 + */ + private Integer enaxlecount; + + /** + * 入口站号 + */ + private String entollstation; + + /** + * 入口站名称 + */ + private String entollstationname; + + /** + * 入口车道号 + */ + private String entolllane; + + /** + * 入口站HEX编码 + */ + private String entollstationhex; + + /** + * 入口车道HEX编码 + */ + private String entolllanehex; + + /** + * 入口站号(国标) + */ + private String entollstationid; + + /** + * 入口车道号(国标) + */ + private String entolllaneid; + + /** + * 入口时间 + */ + private String entime; + + /** + * 车道类型:1-ETC车道,2-MTC车道,3-混合车道 + */ + private Short lanetype; + + /** + * 出口站号 + */ + private String extollstation; + + /** + * 出口收费站名称 + */ + private String extollstationname; + + /** + * 出口车道号 + */ + private String extolllane; + + /** + * 出口站HEX编码 + */ + private String extollstationhex; + + /** + * 出口车道HEX编码 + */ + private String extolllanehex; + + /** + * 出口站号(国标) + */ + private String extollstationid; + + /** + * 出口车道号(国标) + */ + private String extolllaneid; + + /** + * 通行介质类型:1-OBU,2-CPC卡,3-纸券,4-M1,9-无通行介质 + */ + private Short mediatype; + + /** + * 过车数量 + */ + private Integer vcount; + + /** + * 入口车牌号(经过MD5加密) + */ + @TableField("envlp_MD5") + private String envlpMD5; + + /** + * 入口车牌颜色 + */ + private Short envlpc; + + /** + * 出口车牌号(经过MD5加密) + */ + @TableField("exvlp_MD5") + private String exvlpMD5; + + /** + * 出口车牌颜色 + */ + private Short exvlpc; + + /** + * 入口车型 + */ + private Integer envehicletype; + + /** + * 出口车型 + */ + private Integer exvehicletype; + + /** + * 入口车种 + */ + private Short envehicleclass; + + /** + * 出口车种 + */ + private Short exvehicleclass; + + /** + * 出口重量 + */ + private Integer exweight; + + /** + * 轴组信息 + */ + private String axisinfo; + + /** + * 限载总重(KG) + */ + private Integer limitweight; + + /** + * 超限率 + */ + private Short overweightrate; + + /** + * 轴数 + */ + private Integer axlecount; + + /** + * 交易类型 + */ + private String transtype; + + /** + * 支付类型:1-现金,2-其他第三方账户支付,3-银联卡支付,4-ETC 用户卡,6-支付宝,7-微信 + */ + private Short paytype; + +} diff --git a/zc-business/src/main/java/com/zc/business/domain/OdsTollViuData.java b/zc-business/src/main/java/com/zc/business/domain/OdsTollViuData.java new file mode 100644 index 00000000..eb22d016 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/domain/OdsTollViuData.java @@ -0,0 +1,44 @@ +package com.zc.business.domain; + +import com.baomidou.mybatisplus.annotation.TableField; +import lombok.Data; + +import java.io.Serializable; +import java.util.Date; + +/** + * 门架牌识流水数据实体类 + * @author xiepufeng + */ +@Data +public class OdsTollViuData implements Serializable { + + private static final long serialVersionUID = 1L; + + /** + * 抓拍时间 + */ + private Date pictime; + + /** + * 门架编号 + */ + private String gantryid; + + /** + * 车牌识别流水号(主键) + */ + private String picid; + + /** + * 拍摄位置:1表示车头,0表示车尾 + */ + private Integer shootposition; + + /** + * 车牌号(经过MD5加密) + */ + @TableField("vehicleplate_MD5") + private String vehicleplateMD5; + +} diff --git a/zc-business/src/main/java/com/zc/business/enums/FacilityTypeEnum.java b/zc-business/src/main/java/com/zc/business/enums/FacilityTypeEnum.java new file mode 100644 index 00000000..3111a6c0 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/FacilityTypeEnum.java @@ -0,0 +1,26 @@ +package com.zc.business.enums; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +/** + * 设置类型枚举类 + * @author xiepufeng + */ +@Getter +@AllArgsConstructor +public enum FacilityTypeEnum { + TOLL_STATION(1, "收费站"), + BRIDGE(2, "桥梁"), + INTERCHANGE(3, "互通立交"), + JUNCTION(4, "枢纽立交"), + TUNNEL(5, "隧道"), + SERVICE_AREA(6, "服务区"), + PARKING_AREA(7, "停车区"), + RECOVERY_POST(8, "清障驻点"), + SLOPE(9, "边坡"), + GANTRY(10, "门架"); + + private final int code; + private final String description; +} diff --git a/zc-business/src/main/java/com/zc/business/enums/VehicleTypeEnum.java b/zc-business/src/main/java/com/zc/business/enums/VehicleTypeEnum.java new file mode 100644 index 00000000..fc7cce1a --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/enums/VehicleTypeEnum.java @@ -0,0 +1,61 @@ +package com.zc.business.enums; + +import lombok.Getter; + + +/** + * 车辆类型枚举,用于定义不同类型的车辆及其相关信息。 + * @author xiepufeng + */ +@Getter +public enum VehicleTypeEnum { + // 客车类型 + TYPE_1_PASSENGER_CAR(1, "1型客车"), + TYPE_2_PASSENGER_CAR(2, "2型客车"), + TYPE_3_PASSENGER_CAR(3, "3型客车"), + TYPE_4_PASSENGER_CAR(4, "4型客车"), + // 货车类型 + TYPE_1_TRUCK(11, "1型货车"), + TYPE_2_TRUCK(12, "2型货车"), + TYPE_3_TRUCK(13, "3型货车"), + TYPE_4_TRUCK(14, "4型货车"), + TYPE_5_TRUCK(15,"5型货车"), + TYPE_6_TRUCK(16, "6型货车"), + // 专项作业车类型 + TYPE_1_SPECIAL_PURPOSE_VEHICLE(21, "1型专项作业车"), + TYPE_2_SPECIAL_PURPOSE_VEHICLE(22, "2型专项作业车"), + TYPE_3_SPECIAL_PURPOSE_VEHICLE(23, "3型专项作业车"), + TYPE_4_SPECIAL_PURPOSE_VEHICLE(24, "4型专项作业车"), + TYPE_5_SPECIAL_PURPOSE_VEHICLE(25, "5型专项作业车"), + TYPE_6_SPECIAL_PURPOSE_VEHICLE(26,"6型专项作业车"); + + // 枚举类型的成员变量 + private final Integer value; // 类型代号 + private final String description; // 类型描述 + + /** + * 构造函数,用于初始化车辆类型枚举。 + * @param value 车辆类型的代号。 + * @param description 车辆类型的描述信息。 + */ + VehicleTypeEnum(Integer value, String description) { + this.value = value; + this.description = description; + } + + /** + * 根据描述查找枚举值。 + * @param value 车辆类型的代号。 + * @return 对应的车辆类型枚举。 + * @throws IllegalArgumentException 如果传入的代号无效,则抛出异常。 + */ + public static VehicleTypeEnum fromDescription(Integer value) { + for (VehicleTypeEnum type : VehicleTypeEnum.values()) { + if (type.getValue().equals(value)) { + return type; + } + } + throw new IllegalArgumentException("无效的车辆类型: " + value); + } +} + 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 new file mode 100644 index 00000000..b7eb758a --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/DcGantryStatisticsDataMapper.java @@ -0,0 +1,33 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.DcGantryStatisticsData; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Date; + +/** + * 这是一个接口的注释,用于描述门架数据统计的Mapper。 + * 它继承了BaseMapper接口,并且指定DcGantryStatisticsData作为映射实体。 + * + * @author xiepufeng 这是作者标注,用于指明代码的编写者。 + */ +@Mapper +public interface DcGantryStatisticsDataMapper extends BaseMapper { + + /** + * 插入或更新门架数据统计。 + * + * @param gantryStatisticsData 门架数据统计对象,包含需要插入或更新的数据。 + * @return 返回一个布尔值,表示操作是否成功。true表示插入或更新成功,false表示失败。 + */ + boolean insertOrUpdate(DcGantryStatisticsData gantryStatisticsData); + + /** + * 获取最大的统计日期。 + * + * @return 返回最大的统计日期。 + */ + Date getMaxStatisticalDate(); +} + diff --git a/zc-business/src/main/java/com/zc/business/mapper/DcTollStationStatisticsDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/DcTollStationStatisticsDataMapper.java new file mode 100644 index 00000000..fd3c979f --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/DcTollStationStatisticsDataMapper.java @@ -0,0 +1,33 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.DcTollStationStatisticsData; +import org.apache.ibatis.annotations.Mapper; + +import java.util.Date; + +/** + * 这是一个接口的注释,用于描述收费站数据统计的Mapper。 + * 它继承了BaseMapper接口,并且指定DcTollStationStatisticsData作为映射实体。 + * + * @author xiepufeng 这是作者标注,用于指明代码的编写者。 + */ +@Mapper +public interface DcTollStationStatisticsDataMapper extends BaseMapper { + + /** + * 插入或更新收费站数据统计。 + * + * @param tollStatisticsData 收费站数据统计对象,包含需要插入或更新的数据。 + * @return 返回一个布尔值,表示操作是否成功。true表示插入或更新成功,false表示失败。 + */ + boolean insertOrUpdate(DcTollStationStatisticsData tollStatisticsData); + + /** + * 获取最大的统计日期。 + * + * @return 返回最大的统计日期。 + */ + Date getMaxStatisticalDate(); +} + diff --git a/zc-business/src/main/java/com/zc/business/mapper/OdsTollEnpassDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/OdsTollEnpassDataMapper.java new file mode 100644 index 00000000..f1017b4e --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/OdsTollEnpassDataMapper.java @@ -0,0 +1,13 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.OdsTollEnpassData; +import org.apache.ibatis.annotations.Mapper; +/** + * 收费站入口流水数据Mapper接口 + * + * @author xiepufeng + */ +@Mapper +public interface OdsTollEnpassDataMapper extends BaseMapper { +} diff --git a/zc-business/src/main/java/com/zc/business/mapper/OdsTollEtctuDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/OdsTollEtctuDataMapper.java new file mode 100644 index 00000000..ea2cd154 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/OdsTollEtctuDataMapper.java @@ -0,0 +1,14 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.OdsTollEtctuData; +import org.apache.ibatis.annotations.Mapper; + +/** + * 门架流水数据Mapper接口 + * + * @author xiepufeng + */ +@Mapper +public interface OdsTollEtctuDataMapper extends BaseMapper { +} diff --git a/zc-business/src/main/java/com/zc/business/mapper/OdsTollExpassDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/OdsTollExpassDataMapper.java new file mode 100644 index 00000000..7aed3bff --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/OdsTollExpassDataMapper.java @@ -0,0 +1,15 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.OdsTollEnpassData; +import com.zc.business.domain.OdsTollExpassData; +import org.apache.ibatis.annotations.Mapper; + +/** + * 收费站出口流水数据Mapper接口 + * + * @author xiepufeng + */ +@Mapper +public interface OdsTollExpassDataMapper extends BaseMapper { +} diff --git a/zc-business/src/main/java/com/zc/business/mapper/OdsTollViuDataMapper.java b/zc-business/src/main/java/com/zc/business/mapper/OdsTollViuDataMapper.java new file mode 100644 index 00000000..132a0479 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/mapper/OdsTollViuDataMapper.java @@ -0,0 +1,14 @@ +package com.zc.business.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.zc.business.domain.OdsTollViuData; +import org.apache.ibatis.annotations.Mapper; + +/** + * 门架牌识流水数据Mapper接口 + * + * @author xiepufeng + */ +@Mapper +public interface OdsTollViuDataMapper extends BaseMapper { +} diff --git a/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java b/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java index 59ca08df..030098bd 100644 --- a/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java +++ b/zc-business/src/main/java/com/zc/business/message/device/handler/DeviceMessageHandler.java @@ -57,7 +57,7 @@ public class DeviceMessageHandler { private RedisCache redisCache; @Resource - private IDcTrafficStatisticsService dcTrafficSectionDataService; + private IDcTrafficSectionStatisticsService dcTrafficSectionDataService; @Autowired private IDcMeteorologicalDetectorDataService meteorologicalDetectorDataService; diff --git a/zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java b/zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java index 7a1687a8..3e3e8c92 100644 --- a/zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java +++ b/zc-business/src/main/java/com/zc/business/service/DcEmergencyPlansService.java @@ -44,24 +44,11 @@ public interface DcEmergencyPlansService { */ void dispositionDeviceContent(List list, DcEvent dcEvent); - /** - * 感知事件-根据事件类型查询事件预案 - * - * @param dcWarning 感知事件 - * @return 结果 - */ - List selectDcEmergencyPlansByWarningType(DcWarning dcWarning); - /** * 交通事件-情报板确认回显原始模板 */ Map> eventBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans); - /** - * 感知事件-情报板确认回显原始模板 - */ - Map> warningBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans); - /** * 交通事件确定 * @@ -86,21 +73,6 @@ public interface DcEmergencyPlansService { */ EventPlanAssoc selectEventPlanAssocById(Long id); - /** - * 感知事件确定 - * - * @param dcEventAnDcEmergencyPlans 事件数据 和 事件预案数据 - * @return 结果 - */ - JSONObject executionWarningConfirmation(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans); - - /** - * 感知事件-情报板自动生成 - * @param dcEventAnDcEmergencyPlans - * @return - */ - DcInfoBoardTemplate warningAutomaticGeneration(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans); - /** * 交通事件-情报板自动生成 * @param dcEventAnDcEmergencyPlans 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 new file mode 100644 index 00000000..e6b95a43 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/IDcGantryStatisticsDataService.java @@ -0,0 +1,37 @@ +package com.zc.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.domain.DcStatisticsData; +import com.zc.business.domain.DcTollStationStatisticsData; + +import java.util.List; + +/** + * 门架数据统计接口。该接口扩展了IService接口,用于对DcGantryStatisticsData类型的实体进行数据库操作。 + * 主要用于门架数据的增删改查等统计工作。 + * + * @author xiepufeng 提供者 + */ +public interface IDcGantryStatisticsDataService extends IService { + + /** + * 获取过去一小时的DcGantryStatisticsData数据列表。 + * + *

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

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的DcTollStationStatisticsData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的DcTollStationStatisticsData数据列表。 + */ + List lastHourData(); + + /** + * 收费站数据统计函数 + * + * 通过对传入的统计数据进行处理,计算并返回收费站数据的统计结果列表。 + * + * @param request 包含统计所需参数。 + * @return 返回一个包收费站站数据统计结果的列表。 + */ + List tollStationData(DcTollStationStatisticsData request); +} diff --git a/zc-business/src/main/java/com/zc/business/service/IDcTrafficSectionStatisticsService.java b/zc-business/src/main/java/com/zc/business/service/IDcTrafficSectionStatisticsService.java new file mode 100644 index 00000000..2adf13ce --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/IDcTrafficSectionStatisticsService.java @@ -0,0 +1,66 @@ +package com.zc.business.service; + +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.extension.service.IService; +import com.zc.business.domain.DcCongestedSectionData; +import com.zc.business.domain.DcTrafficMetricsData; +import com.zc.business.domain.DcTrafficSectionData; +import com.zc.business.request.DcTrafficMetricsDataRequest; +import com.zc.business.request.DcTrafficSectionDataRequest; + +import java.util.List; + +/** + * 交通统计服务接口,提供处理交通数据的相关方法 + * @author xiepufeng + */ +public interface IDcTrafficSectionStatisticsService extends IService { + + /** + * 处理实时接收到的一类交流站设备消息,并将其转换为交通断面统计数据对象并缓存。 + * + * @param msg 设备发送的JSON格式实时消息 + */ + void processRealtimeOneStopMessage(JSONObject msg); + + /** + * 根据提供的请求参数获取当前的交通截面数据。 + * + * @param request 包含获取交通截面所需的所有请求参数的对象。 + * @return DcTrafficSectionData 返回一个包含当前交通截面数据列表。 + */ + List currentSections(DcTrafficSectionDataRequest request); + + /** + * 根据提供的请求参数获取历史的交通截面数据。 + * + * @param request 包含获取交通截面所需的所有请求参数的对象。 + * @return DcTrafficSectionData 返回一个包含历史交通截面数据列表。 + */ + List historySections(DcTrafficSectionDataRequest request); + + /** + * 根据提供的请求参数获取当前的交通指标数据。 + * + * @param request 包含获取交通指标所需的所有请求参数的对象。 + * @return DcTrafficMetricsData 返回一个包含当前交通指标数据列表。 + */ + List currentTrafficMetrics(DcTrafficMetricsDataRequest request); + + + /** + * 获取历史交通指标数据列表。 + * + * @param request 包含获取交通指标所需的所有请求参数的对象。 + * @return 返回符合查询条件的历史交通指标数据列表。 + */ + List historyTrafficMetrics(DcTrafficMetricsDataRequest request); + + /** + * 获取当前拥堵路段列表。 + * + * @param direction 交通方向,指定查询哪个方向的拥堵路段。具体方向的定义根据实际业务而定。 + * @return 返回当前拥堵路段列表。 + */ + List currentCongestedSection(Byte direction); +} 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 bb2cae07..1aa57d26 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,66 +1,16 @@ package com.zc.business.service; -import com.alibaba.fastjson.JSONObject; -import com.baomidou.mybatisplus.extension.service.IService; -import com.zc.business.domain.DcCongestedSectionData; -import com.zc.business.domain.DcTrafficMetricsData; -import com.zc.business.domain.DcTrafficSectionData; -import com.zc.business.request.DcTrafficMetricsDataRequest; -import com.zc.business.request.DcTrafficSectionDataRequest; +import com.zc.business.domain.DcStatisticsData; import java.util.List; -/** - * 交通统计服务接口,提供处理交通数据的相关方法 - * @author xiepufeng - */ -public interface IDcTrafficStatisticsService extends IService { +public interface IDcTrafficStatisticsService { /** - * 处理实时接收到的一类交流站设备消息,并将其转换为交通断面统计数据对象并缓存。 + * 根据传入的统计请求数据,查询历史累计车流量数据。 * - * @param msg 设备发送的JSON格式实时消息 + * @param request 包含统计查询条件的DcStatisticsData对象,用于指定查询的历史流量数据的细节,如时间范围等。 + * @return 返回一个DcStatisticsData列表。 */ - void processRealtimeOneStopMessage(JSONObject msg); - - /** - * 根据提供的请求参数获取当前的交通截面数据。 - * - * @param request 包含获取交通截面所需的所有请求参数的对象。 - * @return DcTrafficSectionData 返回一个包含当前交通截面数据列表。 - */ - List currentSections(DcTrafficSectionDataRequest request); - - /** - * 根据提供的请求参数获取历史的交通截面数据。 - * - * @param request 包含获取交通截面所需的所有请求参数的对象。 - * @return DcTrafficSectionData 返回一个包含历史交通截面数据列表。 - */ - List historySections(DcTrafficSectionDataRequest request); - - /** - * 根据提供的请求参数获取当前的交通指标数据。 - * - * @param request 包含获取交通指标所需的所有请求参数的对象。 - * @return DcTrafficMetricsData 返回一个包含当前交通指标数据列表。 - */ - List currentTrafficMetrics(DcTrafficMetricsDataRequest request); - - - /** - * 获取历史交通指标数据列表。 - * - * @param request 包含获取交通指标所需的所有请求参数的对象。 - * @return 返回符合查询条件的历史交通指标数据列表。 - */ - List historyTrafficMetrics(DcTrafficMetricsDataRequest request); - - /** - * 获取当前拥堵路段列表。 - * - * @param direction 交通方向,指定查询哪个方向的拥堵路段。具体方向的定义根据实际业务而定。 - * @return 返回当前拥堵路段列表。 - */ - List currentCongestedSection(Byte direction); + List historyFlow(DcStatisticsData request); } diff --git a/zc-business/src/main/java/com/zc/business/service/IOdsTollEnpassDataService.java b/zc-business/src/main/java/com/zc/business/service/IOdsTollEnpassDataService.java new file mode 100644 index 00000000..f22ca002 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/IOdsTollEnpassDataService.java @@ -0,0 +1,48 @@ +package com.zc.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.domain.OdsTollEnpassData; + +import java.util.List; + +/** + * 入口流水数据服务接口 + * 提供对入口流水数据的增删改查等操作 + * + * @author xiepufeng + * @extends IService 继承自IService接口,指定操作的数据类型为OdsTollEnpassData + */ +public interface IOdsTollEnpassDataService extends IService { + + + /** + * 获取过去一小时的OdsTollEnpassData数据列表。 + * + *

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的OdsTollEnpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的OdsTollEnpassData数据列表。 + */ + List lastHourData(); + + /** + * 获取当月的OdsTollEnpassData数据列表。 + * + *

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

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

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的OdsTollExpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的OdsTollExpassData数据列表。 + */ + List lastHourData(); + + /** + * 获取当月的OdsTollExpassData数据列表。 + * + *

此方法不接受任何参数,返回一个包含当月所有符合条件的OdsTollExpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的OdsTollExpassData数据列表。 + */ + List currentMonthData(); + + /** + * 计算收费站在指定时间段内的统计数据。 + * + * @param list 包含收费通行数据的列表,不应为null或空。 + * @return 返回一个包含各收费站统计数据的列表。如果输入列表为空,则返回空列表。 + */ + List calculateTollStationStatistics(List list); +} + diff --git a/zc-business/src/main/java/com/zc/business/service/IOdsTollViuDataService.java b/zc-business/src/main/java/com/zc/business/service/IOdsTollViuDataService.java new file mode 100644 index 00000000..83952a80 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/IOdsTollViuDataService.java @@ -0,0 +1,11 @@ +package com.zc.business.service; + +import com.baomidou.mybatisplus.extension.service.IService; +import com.zc.business.domain.OdsTollViuData; + +/** + * 门架牌识流水数据 + * @author xiepufeng + */ +public interface IOdsTollViuDataService extends IService { +} diff --git a/zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java index 4784ce2f..2b45a585 100644 --- a/zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcEmergencyPlansServiceImpl.java @@ -29,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ObjectUtils; import javax.annotation.Resource; import java.io.IOException; @@ -170,12 +171,12 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { dcExecuteActions.forEach(dcExecuteAction -> { JSONObject executeConfig = JSON.parseObject(dcExecuteAction.getExecuteConfig()); if (dcExecuteAction.getDeviceType() == DeviceTypeEnum.ROAD_SECTION_VOICE_BROADCASTING.getCode() - && executeConfig.get("operationType").equals("2")) { + && executeConfig.getString("operationType").equals("2")) { // 执行操作中智能发布语音广播 String content = intelligentPublishingOfInformation(dcEvent); updateIntelligentPublishingContent(dcExecuteAction,markArray,dcEvent,content,dcEvent.getDirection()); } else if (dcExecuteAction.getDeviceType() == DeviceTypeEnum.VARIABLE_INFORMATION_FLAG.getCode() - && executeConfig.get("operationType").equals("2")) { + && executeConfig.getString("operationType").equals("2")) { // 执行操作中智能发布情报板 String content = intelligentPublishingOfInformation(dcEvent); updateIntelligentPublishingContent(dcExecuteAction,markArray,dcEvent,content,dcEvent.getDirection()); @@ -255,37 +256,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { dcExecuteAction.setExecuteConfig(executeConfig.toJSONString()); } - /** - * 感知事件 - 根据事件类型查询事件预案 - * - * @param dcWarning 感知事件 - * @return 结果 - */ - @Override - public List selectDcEmergencyPlansByWarningType(DcWarning dcWarning) { - int eventType = ValueConverter.convertValueHost(dcWarning.getWarningType()); - List dcEmergencyPlansList = dcEmergencyPlansMapper.selectDcEmergencyPlansByWarningType(eventType); - - int warningType = Integer.parseInt(dcWarning.getWarningType().toString()); - - if (warningType == WarningTypeEnum.UNUSUAL_WEATHER.getCode()) { - return dcEmergencyPlansList.stream() - .filter(dcEmergencyPlans -> { - String triggerMechanism = dcEmergencyPlans.getTriggerMechanism(); - JSONObject triggerJson = JSONObject.parseObject(triggerMechanism); - String eventSubclass = triggerJson.get("eventSubclass").toString(); - - // 事件--异常天气数据 - String warningSubclass = ValueConverter.convertValueSon(dcWarning.getWarningSubclass()); - - return eventSubclass.equals(warningSubclass); - }) - .collect(Collectors.toList()); - } else { - return dcEmergencyPlansList; - } - } - /** * 交通事件-情报板确认回显原始模板 */ @@ -307,27 +277,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { return getBoardTemplate(dcDevices); } - /** - * 感知事件-情报板确认回显原始模板 - */ - @Override - public Map> warningBoardConfirm(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - // 获取事件数据 - DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning(); - // 方向 - String direction = dcWarning.getDirection(); - // 事件桩号 - dcWarning.setStakeMark(dcWarning.getStakeMark().replace("K", "")); - String[] markArray = dcWarning.getStakeMark().split("\\+"); - if (markArray[1].length() < 3) { - // 不足三位 补零 - markArray[1] = String.format("%0" + 3 + "d", markArray[1]); - } - DcExecuteAction executeAction = dcEventAnDcEmergencyPlans.getDcEmergencyPlans().getExecuteAction(); - List dcDevices = ruleFiltering(executeAction, markArray, direction); - return getBoardTemplate(dcDevices); - } - /** * 情报板设备执行3A功能,获取模板 */ @@ -575,36 +524,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { return eventPlanAssocMapper.selectById(eventPlanAssoc); } - /** - * 感知事件-情报板自动生成 - * - * @param dcEventAnDcEmergencyPlans - * @return - */ - @Override - public DcInfoBoardTemplate warningAutomaticGeneration(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - // 根据感知事件类型等、生成情报板的内容 - DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning(); - DcInfoBoardTemplate dcInfoBoardTemplate = dcEventAnDcEmergencyPlans.getDcInfoBoardTemplate(); - Integer warningType = dcWarning.getWarningType(); - if (warningType.equals(WarningTypeEnum.TRAFFIC_JAM.getCode())) { - // 交通拥堵 - dcInfoBoardTemplate.setContent("前方" + WarningTypeEnum.TRAFFIC_JAM.getInfo() + "请谨慎驾驶"); - } else if (warningType.equals(WarningTypeEnum.NON_MOTOR_VEHICLE.getCode())) { - dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.NON_MOTOR_VEHICLE.getInfo() + "请注意避让"); - } else if (warningType.equals(WarningTypeEnum.PEDESTRIAN.getCode())) { - dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.PEDESTRIAN.getInfo() + "请注意避让"); - } else if (warningType.equals(WarningTypeEnum.FIREWORKS.getCode())) { - dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.FIREWORKS.getInfo() + "请注意避让"); - } else if (warningType.equals(WarningTypeEnum.OUTFALL.getCode())) { - dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.OUTFALL.getInfo() + "请注意避让"); - } else if (warningType.equals(WarningTypeEnum.VEHICLE_CONVERSE_RUNNING.getCode())) { - dcInfoBoardTemplate.setContent("前方出现" + WarningTypeEnum.OUTFALL.getInfo() + "请注意避让"); - } - - return dcInfoBoardTemplate; - } - /** * 交通事件-情报板自动生成 * @@ -669,24 +588,6 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { return content; } - /** - * 感知事件确认 - * - * @param dcEventAnDcEmergencyPlans 事件数据 和 事件预案数据 - * @return - */ - @Override - public JSONObject executionWarningConfirmation(DcEventAnDcEmergencyPlans dcEventAnDcEmergencyPlans) { - // 获取事件数据 - DcWarning dcWarning = dcEventAnDcEmergencyPlans.getDcWarning(); - - // 方向 - String direction = dcWarning.getDirection(); - // 事件编号 - String id = dcWarning.getId(); - return executionConfirmation(dcEventAnDcEmergencyPlans, dcWarning.getStakeMark(), direction, id); - } - /** * 事件确认 * @@ -718,6 +619,30 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { dcEmergencyPlans.getDcExecuteAction() .forEach(dcExecuteAction -> { List dcDevices = ruleFiltering(dcExecuteAction, markArray, direction); + +// // 此代码 是为了防止事件没有匹配到预案,但是还执行了设备控制,并且执行操作中带有智能发布 +// boolean isIdEmpty = ObjectUtils.isEmpty(dcEmergencyPlans.getId()); +// Set targetDeviceTypes = new HashSet<>(Arrays.asList( +// DeviceTypeEnum.ROAD_SECTION_VOICE_BROADCASTING.getCode(), +// DeviceTypeEnum.VARIABLE_INFORMATION_FLAG.getCode() +// )); +// boolean isTargetDeviceType = targetDeviceTypes.contains(dcExecuteAction.getDeviceType()); +// if (isIdEmpty && isTargetDeviceType) { +// String configJson = operationType.equals(1) ? dcExecuteAction.getExecuteConfig() : dcExecuteAction.getRecoverConfig(); +// JSONObject config = JSON.parseObject(configJson); +// if (config.getString("operationType").equals("2")) { +// DcEvent dcEvent = dcEventAnDcEmergencyPlans.getDcEvent(); +// String content = intelligentPublishingOfInformation(dcEvent); +// updateIntelligentPublishingContent( +// dcExecuteAction, +// markArray, +// dcEvent, +// content, +// dcEvent.getDirection() +// ); +// } +// } + JSONObject otherConfig = operationType.equals(1)? JSON.parseObject(dcExecuteAction.getExecuteConfig()): JSON.parseObject(dcExecuteAction.getRecoverConfig()); try { @@ -753,6 +678,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { eventPlanAssoc.setControlDevice(deviceIds.toString().replaceFirst(";", "")); eventPlanAssoc.setControlResult(resultArray.toJSONString()); eventPlanAssoc.setCreateTime(DateUtils.getNowDate()); + eventPlanAssoc.setControl(JSON.toJSONString(dcEmergencyPlans.getDcExecuteAction())); eventPlanAssocMapper.insertEventPlanAssoc(eventPlanAssoc); resultObject.put("eventPlanAssocId",eventPlanAssoc.getId()); @@ -819,7 +745,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { else if (device.getDeviceType().equals(DeviceTypeConstants.VARIABLE_INFORMATION_FLAG.toString())) { if (operationType == 1) { // 情报板发布全流程 - if (otherConfig.get("operationType").equals("2")) { + if (otherConfig.getString("operationType").equals("2")) { JSONArray contentList = JSON.parseArray(otherConfig.get("contentList").toString()); JSONObject foundContent = contentList.stream() .map(content -> JSON.parseObject(content.toString())) @@ -843,7 +769,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { } else { // 恢复操作 - if (otherConfig.get("operationType").equals("2")) { + if (otherConfig.getString("operationType").equals("2")) { // 还原上次 props.put("fileId", "10"); functionId = DeviceFunctionIdConstants.VARIABLE_INFORMATION_FLAG_1B; @@ -869,7 +795,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { params.put("name", "task-event"); params.put("outVVol", "8"); params.put("priority", "1"); - if (otherConfig.get("operationType").equals("2")) { + if (otherConfig.getString("operationType").equals("2")) { // 智能发布 JSONArray contentList = JSON.parseArray(otherConfig.get("contentList").toString()); JSONObject foundContent = contentList.stream() @@ -1169,7 +1095,7 @@ public class DcEmergencyPlansServiceImpl implements DcEmergencyPlansService { }else if (dcExecuteAction.getDeviceType() == DeviceTypeConstants.VARIABLE_INFORMATION_FLAG || dcExecuteAction.getDeviceType() == DeviceTypeConstants.ROAD_SECTION_VOICE_BROADCASTING) { // 情报板/语音广播 - if (jsonObject.get("operationType").equals("1")) { + if (jsonObject.getString("operationType").equals("1")) { config.put("content",jsonObject.get("content")); }else { config.put("operationType","智能发布"); 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 new file mode 100644 index 00000000..ed8d6d38 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcGantryStatisticsDataImpl.java @@ -0,0 +1,170 @@ +package com.zc.business.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.exception.ServiceException; +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.domain.OdsTollEtctuData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.mapper.DcGantryStatisticsDataMapper; +import com.zc.business.service.*; +import com.zc.business.statistics.cache.gantry.DailyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.MonthlyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.QuarterlyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.YearlyTrafficGantryStatisticsCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.Date; +import java.util.List; + +/** + * 门架数据统计服务实现类 + * 该类扩展了ServiceImpl,用于处理DcGantryStatisticsData表的数据库操作。 + * 实现了IDcGantryStatisticsDataService接口,提门架数据的统计方法。 + */ +@Service +public class DcGantryStatisticsDataImpl extends ServiceImpl + implements IDcGantryStatisticsDataService { + + // 日志记录器 + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + // 门架流水数据服务 + @Resource + private IOdsTollEtctuDataService odsTollEtctuDataService; + + + /** + * 初始化方法,用于在对象创建后恢复各种周期的交通门架缓存。 + * 该方法标注了@PostConstruct注解,确保在依赖注入完成后调用。 + */ + @PostConstruct + public void init() { + recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据) + recoveryMonthlyCache(); // 恢复每月交通门架缓存 + recoveryQuarterlyCache(); // 恢复每季度交通门架缓存 + recoveryYearlyCache(); // 恢复每年交通门架缓存 + } + + + /** + * 获取最后一小时的门架统计数据。 + * 该方法首先从ODS(数据操作服务)获取过去一小时的门架数据, + * 然后基于这些数据计算并生成门架的统计信息列表。 + * + * @return List 包含门架统计信息的列表。 + */ + @Override + public List lastHourData() { + + // 获取门架入口过去一小时的通过数据 + List odsTollEnpassDataList = odsTollEtctuDataService.lastHourData(); + + return odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList); + } + + /** + * 进行门架数据统计的函数。 + * 该方法接收一个DcGantryStatisticsData类型的对象作为输入参数,用来包含门架统计所需的各项数据, + * 然后基于这些数据进行统计处理,并返回一个包含统计结果的DcGantryStatisticsData对象列表。 + * + * @param request 包含门架统计所需数据的对象。 + * @return 返回一个DcGantryStatisticsData对象的列表,包含统计后的结果。 + */ + @Override + public List gantryData(DcGantryStatisticsData request) { + + if (request.getStartTime() == null || request.getEndTime() == null) { + throw new ServiceException("开始时间或结束时间不能为空"); + } + + if (request.getPeriodType() == null) { + throw new ServiceException("时段类型不能为空"); + } + + if (request.getGantryCode() == null) { + throw new ServiceException("门架标识不能为空"); + } + + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + + // 如果请求中包含唯一标识符,则根据唯一标识符进行过滤 + if (request.getId() != null) { + queryWrapper.eq(DcGantryStatisticsData::getId, request.getId()); + } + + // 如果请求中包含统计日期,则根据统计日期进行过滤 + if (request.getStatisticalDate() != null) { + queryWrapper.eq(DcGantryStatisticsData::getStatisticalDate, request.getStatisticalDate()); + } + + queryWrapper.eq(DcGantryStatisticsData::getPeriodType, request.getPeriodType()); + queryWrapper.eq(DcGantryStatisticsData::getGantryCode, request.getGantryCode()); + queryWrapper.between(DcGantryStatisticsData::getCreateTime, request.getStartTime(), request.getEndTime()); + + return list(queryWrapper); + } + + /** + * 恢复日缓存数据的方法 + * 该方法首先会获取当前月份的门架入口数据, + * 然后分别计算每个门架的统计信息,并将这些统计信息添加到每日交通门架统计缓存中。 + */ + private void recoveryDailyCache() { + + // 获取当月门架数据 + List odsTollEnpassDataList = odsTollEtctuDataService.currentMonthData(); + + // 计算每个门架数据的统计信息,并添加到每日门架统计缓存中 + odsTollEtctuDataService.calculateGantryStatistics(odsTollEnpassDataList).forEach(DailyTrafficGantryStatisticsCache::addCacheData); + } + + /** + * 恢复每月交通门架缓存的方法。 + * 通过查询当前月份至今的每日交通数据,并将其添加到每月门架统计缓存中。 + */ + private void recoveryMonthlyCache() { + // 构建查询条件,查询当前月份至今的每日交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.DAY); + queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfMonth(new Date()), new Date()); + List dcTrafficSectionDataList = this.list(queryWrapper); + // 遍历查询结果,将每日数据添加到每月门架统计缓存中 + dcTrafficSectionDataList.forEach(MonthlyTrafficGantryStatisticsCache::addCacheData); + } + + /** + * 恢复每季度交通门架缓存的方法。 + * 通过查询当前季度至今的每月交通数据,并将其添加到每季度门架统计缓存中。 + */ + private void recoveryQuarterlyCache() { + // 构建查询条件,查询当前季度至今的每月交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.MONTH); + queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfQuarter(new Date()), new Date()); + List dcTrafficSectionDataList = this.list(queryWrapper); + // 遍历查询结果,将每月数据添加到每季度门架统计缓存 + dcTrafficSectionDataList.forEach(QuarterlyTrafficGantryStatisticsCache::addCacheData); + } + + /** + * 恢复每年交通门架缓存的方法。 + * 通过查询当前年份至今的每季度交通数据,并将其添加到每年门架统计缓存中。 + */ + private void recoveryYearlyCache() { + // 构建查询条件,查询当前年份至今的每季度交通数据 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + queryWrapper.eq(DcGantryStatisticsData::getPeriodType, TrafficDataPeriodTypeEnum.QUARTER); + queryWrapper.between(DcGantryStatisticsData::getStatisticalDate, DateUtil.beginOfYear(new Date()), new Date()); + List dcTrafficSectionDataList = this.list(queryWrapper); + // 遍历查询结果,将每季度数据添加到每年门架统计缓存 + dcTrafficSectionDataList.forEach(YearlyTrafficGantryStatisticsCache::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 new file mode 100644 index 00000000..7d8042ba --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/impl/DcTollStationStatisticsDataImpl.java @@ -0,0 +1,200 @@ +package com.zc.business.service.impl; + +import cn.hutool.core.date.DateUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.exception.ServiceException; +import com.zc.business.domain.*; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.mapper.DcTollStationStatisticsDataMapper; +import com.zc.business.service.IDcTollStationStatisticsDataService; +import com.zc.business.service.IOdsTollEnpassDataService; +import com.zc.business.service.IOdsTollExpassDataService; +import com.zc.business.statistics.cache.tollstation.DailyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.MonthlyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.QuarterlyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.YearlyTrafficTollStationStatisticsCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import javax.annotation.Resource; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * 收费站数据统计服务实现类 + * 该类扩展了ServiceImpl,用于处理DcTollStationStatisticsData表的数据库操作。 + * 实现了IDcTollStationStatisticsDataService接口,提供收费站数据的统计方法。 + */ +@Service +public class DcTollStationStatisticsDataImpl extends ServiceImpl + implements IDcTollStationStatisticsDataService { + + // 日志记录器 + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + // 收费站入口流水数据 + @Resource + private IOdsTollEnpassDataService odsTollEnpassDataService; + + // 出口流水数据 + @Resource + private IOdsTollExpassDataService odsTollExpassDataService; + + /** + * 初始化方法,用于在对象创建后恢复各种周期的交通收费站站点缓存。 + * 该方法标注了@PostConstruct注解,确保在依赖注入完成后调用。 + */ + @PostConstruct + public void init() { + recoveryDailyCache(); // 从数据库中恢复天的缓存数据(当月天的缓存数据) + recoveryMonthlyCache(); // 恢复每月交通收费站站点缓存 + recoveryQuarterlyCache(); // 恢复每季度交通收费站站点缓存 + recoveryYearlyCache(); // 恢复每年交通收费站站点缓存 + } + + + /** + * 获取最后一小时的收费站点统计数据。 + * 该方法首先从ODS(数据操作服务)获取过去一小时的通过(Enpass)和出口(Expass)数据, + * 然后基于这些数据计算并生成收费站点的统计信息列表。 + * + * @return List 包含收费站点统计信息的列表。 + */ + @Override + public List lastHourData() { + + // 获取站点入口过去一小时的通过数据 + List odsTollEnpassDataList = odsTollEnpassDataService.lastHourData(); + // 获取站点出口过去一小时的通过数据 + List odsTollExpassDataList = odsTollExpassDataService.lastHourData(); + + // 初始化用于存放最终统计结果的列表 + List dcTollStationStatisticsDataList = new ArrayList<>(); + + // 根据入口和出口数据计算并添加收费站点的统计信息到结果列表中 + dcTollStationStatisticsDataList.addAll(odsTollEnpassDataService.calculateTollStationStatistics(odsTollEnpassDataList)); + dcTollStationStatisticsDataList.addAll(odsTollExpassDataService.calculateTollStationStatistics(odsTollExpassDataList)); + + return dcTollStationStatisticsDataList; + } + + /** + * 收费站数据统计函数 + *

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

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的OdsTollEnpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的OdsTollEnpassData数据列表。 + */ + @Override + public List lastHourData() { + // 创建查询条件包装器 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 计算一小时前的时间点 + DateTime lastHour = DateUtil.offsetHour(new Date(), -1); + // 设置查询条件,查询时间包含在lastHour内的数据 + queryWrapper.between(OdsTollEnpassData::getEntime, DateUtil.beginOfHour(lastHour), DateUtil.endOfHour(lastHour)); + // 根据查询条件获取数据列表并返回 + return list(queryWrapper); + } + + /** + * 获取当月的OdsTollEnpassData数据列表。 + * + *

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

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

此方法不接受任何参数,返回一个包含过去一小时所有符合条件的OdsTollExpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 过去一小时的OdsTollExpassData数据列表。 + */ + @Override + public List lastHourData() { + // 创建查询条件包装器 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 计算一小时前的时间点 + DateTime lastHour = DateUtil.offsetHour(new Date(), -1); + // 设置查询条件,查询时间包含在lastHour内的数据 + queryWrapper.between(OdsTollExpassData::getExtime, DateUtil.beginOfHour(lastHour), DateUtil.endOfHour(lastHour)); + // 根据查询条件获取数据列表并返回 + return list(queryWrapper); + } + + /** + * 获取当月的OdsTollExpassData数据列表。 + * + *

此方法不接受任何参数,返回一个包含当月所有符合条件的OdsTollExpassData对象的列表。 + * 该列表可用于进一步的数据分析和处理。 + * + * @return List - 当月的OdsTollExpassData数据列表。 + */ + @Override + public List currentMonthData() { + // 创建查询条件包装器 + LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); + // 设置查询条件,查询当前月份的所有数据 + queryWrapper.between(OdsTollExpassData::getExtime, DateUtil.beginOfMonth(new Date()), new Date()); + // 根据查询条件获取数据列表并返回 + return list(queryWrapper); + } + + /** + * 计算收费站在指定时间段内的统计数据。 + * + * @param list 包含收费通行数据的列表,不应为null或空。 + * @return 返回一个包含各收费站统计数据的列表。如果输入列表为空,则返回空列表。 + */ + @Override + public List calculateTollStationStatistics(List list) { + // 检查输入列表是否为空,如果是则直接返回空列表 + if (list == null || list.isEmpty()) { + return new ArrayList<>(); + } + + // 使用Stream API按入口站号和入口时间进行分组 + Map>> groupedData = + list.stream() + .collect(Collectors.groupingBy( + OdsTollExpassData::getExtollstationid, // 按入口站号分组 + Collectors.groupingBy(odsTollExpassData -> DateUtil.beginOfHour(odsTollExpassData.getExtime()).toString()) // 在每个入口站内按入口通行时间进一步分组 + )); + + // 遍历分组后的数据,计算每个收费站每个时间周期内的统计数据 + List dcTollStationStatisticsDataList = new ArrayList<>(); + groupedData.forEach((extollstationid, timeGroupedData) -> timeGroupedData.forEach((entime, dataList) -> dcTollStationStatisticsDataList.add(trafficStatistics(dataList)))); + + return dcTollStationStatisticsDataList; + } + + /** + * 计算交通统计数据 + * + * @param list 包含收费过境数据的列表 + * @return DcTollStationStatisticsData 对象,包含计算后的交通统计数据 + */ + private DcTollStationStatisticsData trafficStatistics(List list) { + + // 创建DcTollStationStatisticsData对象并初始化 + DcTollStationStatisticsData dcTollStationStatisticsData = new DcTollStationStatisticsData(); + + // 初始化车流量总和 + int trafficVolume = 0; + + // 初始化1型客车车流量 + int type1PassengerFlow = 0; + // 初始化2型客车车流量 + int type2PassengerFlow = 0; + // 初始化3型客车车流量 + int type3PassengerFlow = 0; + // 初始化4型客车车流量 + int type4PassengerFlow = 0; + + // 初始化1型货车车流量 + int type1TruckFlow = 0; + // 初始化2型货车车流量 + int type2TruckFlow = 0; + // 初始化3型货车车流量 + int type3TruckFlow = 0; + // 初始化4型货车车流量 + int type4TruckFlow = 0; + // 初始化5型货车车流量 + int type5TruckFlow = 0; + // 初始化6型货车车流量 + int type6TruckFlow = 0; + + // 初始化1型专项作业车车流量 + int type1SpecialVehicleFlow = 0; + // 初始化2型专项作业车车流量 + int type2SpecialVehicleFlow = 0; + // 初始化3型专项作业车车流量 + int type3SpecialVehicleFlow = 0; + // 初始化4型专项作业车车流量 + int type4SpecialVehicleFlow = 0; + // 初始化5型专项作业车车流量 + int type5SpecialVehicleFlow = 0; + // 初始化6型专项作业车车流量 + int type6SpecialVehicleFlow = 0; + + for (OdsTollExpassData odsTollExpassData : list) { + Integer vcount = odsTollExpassData.getVcount(); + Integer vehicletype = odsTollExpassData.getExvehicletype(); + + trafficVolume += vcount; + + if (VehicleTypeEnum.TYPE_1_PASSENGER_CAR.getValue().equals(vehicletype)) { + type1PassengerFlow += vcount; + } else if (VehicleTypeEnum.TYPE_2_PASSENGER_CAR.getValue().equals(vehicletype)) { + type2PassengerFlow += vcount; + } else if (VehicleTypeEnum.TYPE_3_PASSENGER_CAR.getValue().equals(vehicletype)) { + type3PassengerFlow += vcount; + } else if (VehicleTypeEnum.TYPE_4_PASSENGER_CAR.getValue().equals(vehicletype)) { + type4PassengerFlow += vcount; + } else if (VehicleTypeEnum.TYPE_1_TRUCK.getValue().equals(vehicletype)) { + type1TruckFlow += vcount; + } else if (VehicleTypeEnum.TYPE_2_TRUCK.getValue().equals(vehicletype)) { + type2TruckFlow+= vcount; + } else if (VehicleTypeEnum.TYPE_3_TRUCK.getValue().equals(vehicletype)) { + type3TruckFlow += vcount; + } else if (VehicleTypeEnum.TYPE_4_TRUCK.getValue().equals(vehicletype)) { + type4TruckFlow += vcount; + } else if (VehicleTypeEnum.TYPE_5_TRUCK.getValue().equals(vehicletype)) { + type5TruckFlow += vcount; + } else if (VehicleTypeEnum.TYPE_6_TRUCK.getValue().equals(vehicletype)) { + type6TruckFlow += vcount; + } else if (VehicleTypeEnum.TYPE_1_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type1SpecialVehicleFlow += vcount; + } else if (VehicleTypeEnum.TYPE_2_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type2SpecialVehicleFlow += vcount; + } else if (VehicleTypeEnum.TYPE_3_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type3SpecialVehicleFlow += vcount; + } else if (VehicleTypeEnum.TYPE_4_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type4SpecialVehicleFlow += vcount; + } else if (VehicleTypeEnum.TYPE_5_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type5SpecialVehicleFlow += vcount; + } else if (VehicleTypeEnum.TYPE_6_SPECIAL_PURPOSE_VEHICLE.getValue().equals(vehicletype)) { + type6SpecialVehicleFlow += vcount; + } + } + + // 使用第一个数据项的信息填充汇总统计对象的基本属性 + OdsTollExpassData firstOdsTollEnpassData = list.iterator().next(); + + // 收费站站点标识 + dcTollStationStatisticsData.setTollStationCode(firstOdsTollEnpassData.getExtollstationid()); + Date date = DateUtil.beginOfHour(firstOdsTollEnpassData.getExtime()); + // 统计时间 + dcTollStationStatisticsData.setStatisticalDate(date); + // 上报时间 + dcTollStationStatisticsData.setReportTime(date); + // 统计粒度 + dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY); + // 出入类型 + dcTollStationStatisticsData.setAccessType(DcTollStationStatisticsData.ENTRANCE); + + // 车流量 + dcTollStationStatisticsData.setTrafficVolume(trafficVolume); + // 1型客车车流量 + dcTollStationStatisticsData.setType1PassengerFlow(type1PassengerFlow); + // 2型客车车流量 + dcTollStationStatisticsData.setType2PassengerFlow(type2PassengerFlow); + // 3型客车车流量 + dcTollStationStatisticsData.setType3PassengerFlow(type3PassengerFlow); + // 4型客车车流量 + dcTollStationStatisticsData.setType4PassengerFlow(type4PassengerFlow); + // 1型货车车流量 + dcTollStationStatisticsData.setType1TruckFlow(type1TruckFlow); + // 2型货车车流量 + dcTollStationStatisticsData.setType2TruckFlow(type2TruckFlow); + // 3型货车车流量 + dcTollStationStatisticsData.setType3TruckFlow(type3TruckFlow); + // 4型货车车流量 + dcTollStationStatisticsData.setType4TruckFlow(type4TruckFlow); + // 5型货车车流量 + dcTollStationStatisticsData.setType5TruckFlow(type5TruckFlow); + // 6型货车车流量 + dcTollStationStatisticsData.setType6TruckFlow(type6TruckFlow); + // 1型专项作业车车流量 + dcTollStationStatisticsData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow); + // 2型专项作业车车流量 + dcTollStationStatisticsData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow); + // 3型专项作业车车流量 + dcTollStationStatisticsData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow); + // 4型专项作业车车流量 + dcTollStationStatisticsData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow); + // 5型专项作业车车流量 + dcTollStationStatisticsData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow); + // 6型专项作业车车流量 + dcTollStationStatisticsData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow); + + return dcTollStationStatisticsData; + + } +} diff --git a/zc-business/src/main/java/com/zc/business/service/impl/OdsTollViuDataServiceImpl.java b/zc-business/src/main/java/com/zc/business/service/impl/OdsTollViuDataServiceImpl.java new file mode 100644 index 00000000..4135cf79 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/service/impl/OdsTollViuDataServiceImpl.java @@ -0,0 +1,18 @@ +package com.zc.business.service.impl; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.ruoyi.common.annotation.DataSource; +import com.ruoyi.common.enums.DataSourceType; +import com.zc.business.domain.OdsTollViuData; +import com.zc.business.mapper.OdsTollViuDataMapper; +import com.zc.business.service.IOdsTollViuDataService; +import org.springframework.stereotype.Service; + +/** + * 门架牌识流水数据 + */ +@Service +@DataSource(value = DataSourceType.SLAVE)//切换数据源 +public class OdsTollViuDataServiceImpl extends ServiceImpl + implements IOdsTollViuDataService { +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java index c3e02751..eccb0128 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/AbstractTrafficStatisticsCache.java @@ -1,6 +1,5 @@ package com.zc.business.statistics.cache; -import com.zc.business.domain.DcTrafficSectionData; import lombok.Getter; import lombok.Setter; import org.slf4j.Logger; @@ -16,7 +15,7 @@ import java.util.HashSet; */ @Getter @Setter -public abstract class AbstractTrafficStatisticsCache { +public abstract class AbstractTrafficStatisticsCache { // 日志记录器 protected final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -44,7 +43,7 @@ public abstract class AbstractTrafficStatisticsCache { /** * 存储具体交通断面数据的列表 */ - private Collection data; + private Collection data; public AbstractTrafficStatisticsCache() { this.data = new HashSet<>(); diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java new file mode 100644 index 00000000..fb937ea8 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/DailyTrafficGantryStatisticsCache.java @@ -0,0 +1,148 @@ +package com.zc.business.statistics.cache.gantry; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * + * 以天为单位的门架数据缓存类,用于存储和管理门架统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class DailyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 DailyTrafficGantryStatisticsCache() { + } + + /** + * 添加门架数据到缓存中 + * + * @param dcGantryStatisticsData 待添加的门架数据 + */ + public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取或新建对应的缓存实例 + DailyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcGantryStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY); + // 更新统计日期 + dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY); + + // 移除旧数据 + instance.getData().remove(dcGantryStatisticsData); + // 添加数据 + instance.getData().add(dcGantryStatisticsData); + } + + /** + * 获取或创建对应设备与日期的DailyTrafficGantryStatisticsCache实例 + * + * @param dcGantryStatisticsData 门架数据统计定义 + * @return 对应的门架数据缓存实例 + */ + private static DailyTrafficGantryStatisticsCache getInstance(DcGantryStatisticsData dcGantryStatisticsData) { + // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 + return cache.computeIfAbsent(generateCacheKey(dcGantryStatisticsData), k -> new DailyTrafficGantryStatisticsCache()); + } + + /** + * 生成缓存键。 + *

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

+ * + * @param dcGantryStatisticsData 门架统计数据对象,包含门架编号、统计日期 + * @return 缓存键,格式为"门架编号|格式化后的统计日期" + */ + public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取门架标识 + String gantryCode = dcGantryStatisticsData.getGantryCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + + // 组合门架编号、格式化后的统计日期作为缓存键 + return gantryCode + "|" + formattedDate; + } + + /** + * 清除所有过期的门架数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(DailyTrafficGantryStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为指定格式的字符串。 + * + * @param date 需要格式化的 Date 对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为指定格式的字符串 + return DateUtil.formatDate(date); + } +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/MonthlyTrafficGantryStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/MonthlyTrafficGantryStatisticsCache.java new file mode 100644 index 00000000..e6a301b0 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/MonthlyTrafficGantryStatisticsCache.java @@ -0,0 +1,152 @@ +package com.zc.business.statistics.cache.gantry; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * + * 以月为单位的门架数据缓存类,用于存储和管理门架统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class MonthlyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 MonthlyTrafficGantryStatisticsCache() { + } + + /** + * 添加门架数据到缓存中 + * + * @param dcGantryStatisticsData 待添加的门架数据 + */ + public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取或新建对应的缓存实例 + MonthlyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcGantryStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH); + // 更新统计日期 + dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH); + + // 移除旧数据 + instance.getData().remove(dcGantryStatisticsData); + // 添加数据 + instance.getData().add(dcGantryStatisticsData); + } + + /** + * 获取月门架信息缓存的实例。 + *

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

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

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

+ * + * @param dcGantryStatisticsData 门架统计数据对象,包含门架编号、统计日期 + * @return 缓存键,格式为"门架编号|格式化后的统计日期" + */ + public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取门架标识 + String gantryCode = dcGantryStatisticsData.getGantryCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + + // 组合门架编号、格式化后的统计日期和访问类型作为缓存键 + return gantryCode + "|" + formattedDate; + } + + /** + * 清除所有过期的交通断面数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(MonthlyTrafficGantryStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为 "yyyy-MM-01" 格式的字符串。 + * @param date 需要格式化的日期对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为指定格式的字符串 + return DateUtil.format(date, "yyyy-MM-01"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/QuarterlyTrafficGantryStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/QuarterlyTrafficGantryStatisticsCache.java new file mode 100644 index 00000000..90b70d4d --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/gantry/QuarterlyTrafficGantryStatisticsCache.java @@ -0,0 +1,154 @@ +package com.zc.business.statistics.cache.gantry; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 以季度为单位的门架数据缓存类,用于存储和管理门架统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class QuarterlyTrafficGantryStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 QuarterlyTrafficGantryStatisticsCache() { + } + + /** + * 添加门架数据到缓存中 + * + * @param dcGantryStatisticsData 待添加的门架数据 + */ + public static void addCacheData(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取或新建对应的缓存实例 + QuarterlyTrafficGantryStatisticsCache instance = getInstance(dcGantryStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("门架数据缓存出现异常,最大缓存量达到设定上线 {}, 当前门架是 {}", MAX_CAPACITY, dcGantryStatisticsData.getGantryCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcGantryStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcGantryStatisticsData.setReportTime(dcGantryStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcGantryStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER); + // 更新统计日期 + dcGantryStatisticsData.setStatisticalDate(dcGantryStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER); + + // 移除旧数据 + instance.getData().remove(dcGantryStatisticsData); + // 添加数据 + instance.getData().add(dcGantryStatisticsData); + } + + /** + * 获取季度门架统计信息缓存的实例。 + *

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

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

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

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

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

+ * + * @param dcGantryStatisticsData 门架统计数据对象,包含门架编号、统计日期 + * @return 缓存键,格式为"门架编号|格式化后的统计日期" + */ + public static String generateCacheKey(DcGantryStatisticsData dcGantryStatisticsData) { + // 获取门架站点标识 + String gantryCode = dcGantryStatisticsData.getGantryCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcGantryStatisticsData.getStatisticalDate()); + + // 组合门架编号、格式化后的统计日期作为缓存键 + return gantryCode + "|" + formattedDate; + } + + /** + * 清除所有过期的交通断面数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(YearlyTrafficGantryStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为 "yyyy-01-01" 格式的字符串。 + * + * @param date 需要格式化的日期对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为 "yyyy-01-01" 格式的字符串 + return DateUtil.format(date, "yyyy-01-01"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/section/DailyTrafficSectionStatisticsCache.java similarity index 86% rename from zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java rename to zc-business/src/main/java/com/zc/business/statistics/cache/section/DailyTrafficSectionStatisticsCache.java index b3b600da..7917e2ed 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/DailyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/section/DailyTrafficSectionStatisticsCache.java @@ -1,9 +1,10 @@ -package com.zc.business.statistics.cache; +package com.zc.business.statistics.cache.section; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; import lombok.Getter; import lombok.Setter; @@ -18,11 +19,11 @@ import java.util.stream.Collectors; */ @Getter @Setter -public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache { +public class DailyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache { - @Getter // 静态缓存容器,使用ConcurrentHashMap保证线程安全 - private static final Map cache = new ConcurrentHashMap<>(); + @Getter + private static final Map cache = new ConcurrentHashMap<>(); // 最大缓存时间(单位:秒) private static final long MAX_CACHE_TIME = 25 * 60 * 60; // 缓存数据最长保留25小时 @@ -31,7 +32,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache private static final int MAX_CAPACITY = 60/5 * (24 + 1) + 1000; // 缓存的最大条目数 // 私有构造函数,确保只能通过静态方法获取实例 - private DailyTrafficStatisticsCache() { + private DailyTrafficSectionStatisticsCache() { } /** @@ -41,7 +42,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache */ public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) { // 获取或新建对应的缓存实例 - DailyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData); + DailyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData); // 检查缓存容量是否达到上限 if (instance.getData().size() >= MAX_CAPACITY) { @@ -77,14 +78,14 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache } /** - * 获取或创建对应设备与日期的DcTrafficSectionDataCache实例 + * 获取或创建对应设备与日期的DailyTrafficSectionStatisticsCache实例 * * @param dcTrafficSectionData 交通断面数据统计定义 * @return 对应的交通断面数据缓存实例 */ - private static DailyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { + private static DailyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 - return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new DailyTrafficStatisticsCache()); + return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new DailyTrafficSectionStatisticsCache()); } /** @@ -111,7 +112,7 @@ public class DailyTrafficStatisticsCache extends AbstractTrafficStatisticsCache public static void clearExpiredData() { // 使用stream API找出所有过期的数据缓存项键 Set keysToRemove = cache.keySet().stream() - .filter(DailyTrafficStatisticsCache::isCacheItemExpire) + .filter(DailyTrafficSectionStatisticsCache::isCacheItemExpire) .collect(Collectors.toSet()); // 安全地从缓存中删除这些过期项 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/section/MonthlyTrafficSectionStatisticsCache.java similarity index 86% rename from zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java rename to zc-business/src/main/java/com/zc/business/statistics/cache/section/MonthlyTrafficSectionStatisticsCache.java index 28c6f83a..16521794 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/MonthlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/section/MonthlyTrafficSectionStatisticsCache.java @@ -1,9 +1,10 @@ -package com.zc.business.statistics.cache; +package com.zc.business.statistics.cache.section; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; import lombok.Getter; import lombok.Setter; @@ -17,11 +18,11 @@ import java.util.stream.Collectors; */ @Getter @Setter -public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache { +public class MonthlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache { // 静态缓存容器,使用ConcurrentHashMap保证线程安全 @Getter - private static final Map cache = new ConcurrentHashMap<>(); + private static final Map cache = new ConcurrentHashMap<>(); // 最大缓存时间(单位:秒) private static final long MAX_CACHE_TIME = (60 * 60) * (31 * 24 + 1); @@ -31,7 +32,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach // 私有构造函数,确保只能通过静态方法获取实例 - private MonthlyTrafficStatisticsCache() { + private MonthlyTrafficSectionStatisticsCache() { } /** @@ -41,7 +42,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach */ public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) { // 获取或新建对应的缓存实例 - MonthlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData); + MonthlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData); // 检查缓存容量是否达到上限 if (instance.getData().size() >= MAX_CAPACITY) { @@ -77,14 +78,14 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach } /** - * 获取或创建对应设备与日期的DcTrafficSectionDataCache实例 + * 获取或创建对应设备与日期的MonthlyTrafficSectionStatisticsCache实例 * * @param dcTrafficSectionData 交通断面数据统计定义 * @return 对应的交通断面数据缓存实例 */ - private static MonthlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { + private static MonthlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 - return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new MonthlyTrafficStatisticsCache()); + return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new MonthlyTrafficSectionStatisticsCache()); } /** @@ -111,7 +112,7 @@ public class MonthlyTrafficStatisticsCache extends AbstractTrafficStatisticsCach public static void clearExpiredData() { // 使用stream API找出所有过期的数据缓存项键 Set keysToRemove = cache.keySet().stream() - .filter(MonthlyTrafficStatisticsCache::isCacheItemExpire) + .filter(MonthlyTrafficSectionStatisticsCache::isCacheItemExpire) .collect(Collectors.toSet()); // 安全地从缓存中删除这些过期项 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/section/QuarterlyTrafficSectionStatisticsCache.java similarity index 86% rename from zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java rename to zc-business/src/main/java/com/zc/business/statistics/cache/section/QuarterlyTrafficSectionStatisticsCache.java index ea3bc677..648a40c1 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/QuarterlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/section/QuarterlyTrafficSectionStatisticsCache.java @@ -1,14 +1,14 @@ -package com.zc.business.statistics.cache; +package com.zc.business.statistics.cache.section; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; import lombok.Getter; import lombok.Setter; import java.util.Date; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -20,11 +20,11 @@ import java.util.stream.Collectors; */ @Getter @Setter -public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache { +public class QuarterlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache { // 静态缓存容器,使用ConcurrentHashMap保证线程安全 @Getter - private static final Map cache = new ConcurrentHashMap<>(); + private static final Map cache = new ConcurrentHashMap<>(); // 最大缓存时间(单位:秒) private static final long MAX_CACHE_TIME = (60 * 60) * (3 * 31 * 24 + 1); @@ -34,7 +34,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa // 私有构造函数,确保只能通过静态方法获取实例 - private QuarterlyTrafficStatisticsCache() { + private QuarterlyTrafficSectionStatisticsCache() { } /** @@ -44,7 +44,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa */ public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) { // 获取或新建对应的缓存实例 - QuarterlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData); + QuarterlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData); // 检查缓存容量是否达到上限 if (instance.getData().size() >= MAX_CAPACITY) { @@ -80,14 +80,14 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa } /** - * 获取或创建对应设备与日期的DcTrafficSectionDataCache实例 + * 获取或创建对应设备与日期的QuarterlyTrafficSectionStatisticsCache实例 * * @param dcTrafficSectionData 交通断面数据统计定义 * @return 对应的交通断面数据缓存实例 */ - private static QuarterlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { + private static QuarterlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 - return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new QuarterlyTrafficStatisticsCache()); + return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new QuarterlyTrafficSectionStatisticsCache()); } /** @@ -114,7 +114,7 @@ public class QuarterlyTrafficStatisticsCache extends AbstractTrafficStatisticsCa public static void clearExpiredData() { // 使用stream API找出所有过期的数据缓存项键 Set keysToRemove = cache.keySet().stream() - .filter(QuarterlyTrafficStatisticsCache::isCacheItemExpire) + .filter(QuarterlyTrafficSectionStatisticsCache::isCacheItemExpire) .collect(Collectors.toSet()); // 安全地从缓存中删除这些过期项 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/section/YearlyTrafficSectionStatisticsCache.java similarity index 86% rename from zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java rename to zc-business/src/main/java/com/zc/business/statistics/cache/section/YearlyTrafficSectionStatisticsCache.java index 7b5147f4..3bc26b52 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/cache/YearlyTrafficStatisticsCache.java +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/section/YearlyTrafficSectionStatisticsCache.java @@ -1,14 +1,14 @@ -package com.zc.business.statistics.cache; +package com.zc.business.statistics.cache.section; import cn.hutool.core.date.DateUnit; import cn.hutool.core.date.DateUtil; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; import lombok.Getter; import lombok.Setter; import java.util.Date; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -20,11 +20,11 @@ import java.util.stream.Collectors; */ @Getter @Setter -public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache { +public class YearlyTrafficSectionStatisticsCache extends AbstractTrafficStatisticsCache { // 静态缓存容器,使用ConcurrentHashMap保证线程安全 @Getter - private static final Map cache = new ConcurrentHashMap<>(); + private static final Map cache = new ConcurrentHashMap<>(); // 最大缓存时间(单位:秒) private static final long MAX_CACHE_TIME = (60 * 60) * (366 * 24 + 1); @@ -34,7 +34,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache // 私有构造函数,确保只能通过静态方法获取实例 - private YearlyTrafficStatisticsCache() { + private YearlyTrafficSectionStatisticsCache() { } /** @@ -44,7 +44,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache */ public static void addCacheData(DcTrafficSectionData dcTrafficSectionData) { // 获取或新建对应的缓存实例 - YearlyTrafficStatisticsCache instance = getInstance(dcTrafficSectionData); + YearlyTrafficSectionStatisticsCache instance = getInstance(dcTrafficSectionData); // 检查缓存容量是否达到上限 if (instance.getData().size() >= MAX_CAPACITY) { @@ -80,14 +80,14 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache } /** - * 获取或创建对应设备与日期的DcTrafficSectionDataCache实例 + * 获取或创建对应设备与日期的YearlyTrafficSectionStatisticsCache实例 * * @param dcTrafficSectionData 交通断面数据统计定义 * @return 对应的交通断面数据缓存实例 */ - private static YearlyTrafficStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { + private static YearlyTrafficSectionStatisticsCache getInstance(DcTrafficSectionData dcTrafficSectionData) { // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 - return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new YearlyTrafficStatisticsCache()); + return cache.computeIfAbsent(generateCacheKey(dcTrafficSectionData), k -> new YearlyTrafficSectionStatisticsCache()); } /** @@ -114,7 +114,7 @@ public class YearlyTrafficStatisticsCache extends AbstractTrafficStatisticsCache public static void clearExpiredData() { // 使用stream API找出所有过期的数据缓存项键 Set keysToRemove = cache.keySet().stream() - .filter(YearlyTrafficStatisticsCache::isCacheItemExpire) + .filter(YearlyTrafficSectionStatisticsCache::isCacheItemExpire) .collect(Collectors.toSet()); // 安全地从缓存中删除这些过期项 diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/DailyTrafficTollStationStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/DailyTrafficTollStationStatisticsCache.java new file mode 100644 index 00000000..ff8bdd69 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/DailyTrafficTollStationStatisticsCache.java @@ -0,0 +1,148 @@ +package com.zc.business.statistics.cache.tollstation; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * + * 以天为单位的交通收费站站点数据缓存类,用于存储和管理收费站站点统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class DailyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 DailyTrafficTollStationStatisticsCache() { + } + + /** + * 添加收费站站点数据到缓存中 + * + * @param dcTollStationStatisticsData 待添加的收费站站点数据 + */ + public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取或新建对应的缓存实例 + DailyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcTollStationStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.DAY); + // 更新统计日期 + dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.DAY); + + // 移除旧数据 + instance.getData().remove(dcTollStationStatisticsData); + // 添加数据 + instance.getData().add(dcTollStationStatisticsData); + } + + /** + * 获取或创建对应设备与日期的DailyTrafficTollStationStatisticsCache实例 + * + * @param dcTollStationStatisticsData 收费站站点数据统计定义 + * @return 对应的收费站站点数据缓存实例 + */ + private static DailyTrafficTollStationStatisticsCache getInstance(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 使用toKey方法生成唯一键,并根据此键计算并返回缓存实例 + return cache.computeIfAbsent(generateCacheKey(dcTollStationStatisticsData), k -> new DailyTrafficTollStationStatisticsCache()); + } + + + /** + * 生成缓存键。 + *

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

+ * + * @param dcTollStationStatisticsData 收费站统计数据对象,包含收费站编号、统计日期和访问类型 + * @return 缓存键,格式为"收费站编号|格式化后的统计日期|访问类型" + */ + public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取收费站站点标识 + String tollStationCode = dcTollStationStatisticsData.getTollStationCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + + // 组合收费站编号、格式化后的统计日期和访问类型作为缓存键 + return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType(); + } + + /** + * 清除所有过期的收费站站点数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(DailyTrafficTollStationStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为指定格式的字符串。 + * + * @param date 需要格式化的 Date 对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为指定格式的字符串 + return DateUtil.formatDate(date); + } +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/MonthlyTrafficTollStationStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/MonthlyTrafficTollStationStatisticsCache.java new file mode 100644 index 00000000..c303f2c7 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/MonthlyTrafficTollStationStatisticsCache.java @@ -0,0 +1,152 @@ +package com.zc.business.statistics.cache.tollstation; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * + * 以月为单位的交通收费站站点数据缓存类,用于存储和管理收费站站点统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class MonthlyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 MonthlyTrafficTollStationStatisticsCache() { + } + + /** + * 添加收费站站点数据到缓存中 + * + * @param dcTollStationStatisticsData 待添加的收费站站点数据 + */ + public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取或新建对应的缓存实例 + MonthlyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcTollStationStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.MONTH); + // 更新统计日期 + dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.MONTH); + + // 移除旧数据 + instance.getData().remove(dcTollStationStatisticsData); + // 添加数据 + instance.getData().add(dcTollStationStatisticsData); + } + + /** + * 获取月交通收费站统计信息缓存的实例。 + *

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

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

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

+ * + * @param dcTollStationStatisticsData 收费站统计数据对象,包含收费站编号、统计日期和访问类型 + * @return 缓存键,格式为"收费站编号|格式化后的统计日期|访问类型" + */ + public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取收费站站点标识 + String tollStationCode = dcTollStationStatisticsData.getTollStationCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + + // 组合收费站编号、格式化后的统计日期和访问类型作为缓存键 + return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType(); + } + + /** + * 清除所有过期的交通断面数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(MonthlyTrafficTollStationStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为 "yyyy-MM-01" 格式的字符串。 + * @param date 需要格式化的日期对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为指定格式的字符串 + return DateUtil.format(date, "yyyy-MM-01"); + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/QuarterlyTrafficTollStationStatisticsCache.java b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/QuarterlyTrafficTollStationStatisticsCache.java new file mode 100644 index 00000000..a1e76b70 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/cache/tollstation/QuarterlyTrafficTollStationStatisticsCache.java @@ -0,0 +1,154 @@ +package com.zc.business.statistics.cache.tollstation; + +import cn.hutool.core.date.DateUnit; +import cn.hutool.core.date.DateUtil; +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import lombok.Getter; +import lombok.Setter; + +import java.util.Date; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * 以季度为单位的交通收费站站点数据缓存类,用于存储和管理收费站站点统计数据,同时提供了数据缓存的有效性管理和清理功能。 + * @author xiepufeng + */ +@Getter +@Setter +public class QuarterlyTrafficTollStationStatisticsCache extends AbstractTrafficStatisticsCache { + + // 静态缓存容器,使用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 QuarterlyTrafficTollStationStatisticsCache() { + } + + /** + * 添加收费站站点数据到缓存中 + * + * @param dcTollStationStatisticsData 待添加的收费站站点数据 + */ + public static void addCacheData(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取或新建对应的缓存实例 + QuarterlyTrafficTollStationStatisticsCache instance = getInstance(dcTollStationStatisticsData); + + // 检查缓存容量是否达到上限 + if (instance.getData().size() >= MAX_CAPACITY) { + instance.getLogger().error("收费站站点数据缓存出现异常,最大缓存量达到设定上线 {}, 当前收费站点是 {}", MAX_CAPACITY, dcTollStationStatisticsData.getTollStationCode()); + return; + } + + // 更新最后添加时间 + instance.setLastAddedTime(DateUtil.date()); + + // 更新状态 + instance.setStored(false); + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + String key = generateCacheKey(dcTollStationStatisticsData); + + // 设置key和thatDay属性(仅在首次添加时) + if (instance.getCacheKey() == null) { + instance.setCacheKey(key); + instance.setStatisticalDateStr(formattedDate); + } + + // 更新上报时间 + dcTollStationStatisticsData.setReportTime(dcTollStationStatisticsData.getStatisticalDate()); + // 更新数据周期类型 + dcTollStationStatisticsData.setPeriodType(TrafficDataPeriodTypeEnum.QUARTER); + // 更新统计日期 + dcTollStationStatisticsData.setStatisticalDate(dcTollStationStatisticsData.getStatisticalDate(), TrafficDataPeriodTypeEnum.QUARTER); + + // 移除旧数据 + instance.getData().remove(dcTollStationStatisticsData); + // 添加数据 + instance.getData().add(dcTollStationStatisticsData); + } + + /** + * 获取季度交通收费站统计信息缓存的实例。 + *

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

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

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

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

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

+ * + * @param dcTollStationStatisticsData 收费站统计数据对象,包含收费站编号、统计日期和访问类型 + * @return 缓存键,格式为"收费站编号|格式化后的统计日期|访问类型" + */ + public static String generateCacheKey(DcTollStationStatisticsData dcTollStationStatisticsData) { + // 获取收费站站点标识 + String tollStationCode = dcTollStationStatisticsData.getTollStationCode(); + + // 格式化统计日期 + String formattedDate = formatDate(dcTollStationStatisticsData.getStatisticalDate()); + + // 组合收费站编号、格式化后的统计日期和访问类型作为缓存键 + return tollStationCode + "|" + formattedDate + "|" + dcTollStationStatisticsData.getAccessType(); + } + + /** + * 清除所有过期的交通断面数据缓存项 + */ + public static void clearExpiredData() { + // 使用stream API找出所有过期的数据缓存项键 + Set keysToRemove = cache.keySet().stream() + .filter(YearlyTrafficTollStationStatisticsCache::isCacheItemExpire) + .collect(Collectors.toSet()); + + // 安全地从缓存中删除这些过期项 + keysToRemove.forEach(cache::remove); + } + + /** + * 检查给定缓存键所对应的缓存项是否已经过期 + * + * @param key 缓存key + * @return 如果已过期则返回true,否则返回false + */ + private static boolean isCacheItemExpire(String key) { + Date lastAddedTime = cache.get(key).getLastAddedTime(); + Date currentTime = DateUtil.date(); + long betweenSecond = DateUtil.between(lastAddedTime, currentTime, DateUnit.SECOND); + return betweenSecond > MAX_CACHE_TIME; + } + + /** + * 将 Date 类型的日期格式化为 "yyyy-01-01" 格式的字符串。 + * + * @param date 需要格式化的日期对象。 + * @return 格式化后的日期字符串。 + */ + private static String formatDate(Date date) { + // 使用 DateUtil 工具类将 date 格式化为 "yyyy-01-01" 格式的字符串 + return DateUtil.format(date, "yyyy-01-01"); + } + +} 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 new file mode 100644 index 00000000..38751abf --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficGantryStatistics.java @@ -0,0 +1,241 @@ +package com.zc.business.statistics.handler; + +import com.zc.business.domain.DcGantryStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.mapper.DcGantryStatisticsDataMapper; +import com.zc.business.service.IDcGantryStatisticsDataService; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import com.zc.business.statistics.cache.gantry.DailyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.MonthlyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.QuarterlyTrafficGantryStatisticsCache; +import com.zc.business.statistics.cache.gantry.YearlyTrafficGantryStatisticsCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Map; +import java.util.function.Consumer; + +/** + * TrafficGantryStatistics类用于处理门架的统计数据。 + * @author xiepufeng + */ +@Component +public class TrafficGantryStatistics { + + // 日志记录器 + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource + private IDcGantryStatisticsDataService dcGantryStatisticsDataService; + + @Resource + private DcGantryStatisticsDataMapper dcGantryStatisticsDataMapper; + + + /** + * 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 + */ + @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务 + public void performHourlyCleanupAndPersist() { + + // 添加日门架数据到缓存中 + dcGantryStatisticsDataService.lastHourData().forEach(DailyTrafficGantryStatisticsCache::addCacheData); + + // 清除已过期的缓存数据 + DailyTrafficGantryStatisticsCache.clearExpiredData(); + MonthlyTrafficGantryStatisticsCache.clearExpiredData(); + QuarterlyTrafficGantryStatisticsCache.clearExpiredData(); + YearlyTrafficGantryStatisticsCache.clearExpiredData(); + + // 整合缓存数据并保存至数据库 + // 将缓存中的数据按日统计后保存至数据库 + // 添加月门架数据到缓存中 + persistAggregatedData(DailyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficGantryStatisticsCache::addCacheData); + // 将缓存中的数据按月统计后保存至数据库 + // 添加季度门架数据到缓存中 + persistAggregatedData(MonthlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficGantryStatisticsCache::addCacheData); + // 将缓存中的数据按季度统计后保存至数据库 + // 添加年门架数据到缓存中 + persistAggregatedData(QuarterlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficGantryStatisticsCache::addCacheData); + // 将缓存中的数据按年统计后保存至数据库 + persistAggregatedData(YearlyTrafficGantryStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); + } + + /** + * 将缓存中的数据统计后保存至数据库。 + */ + public void persistAggregatedData( + Map> cache, + TrafficDataPeriodTypeEnum periodType, + Consumer consumer + ) { + for (AbstractTrafficStatisticsCache data : cache.values()) { + // 如果数据已经存储过,则跳过此次处理 + if (data.isStored()) { + continue; + } + DcGantryStatisticsData aggregatedData = trafficStatistics(data.getData(), periodType); + if (dcGantryStatisticsDataMapper.insertOrUpdate(aggregatedData)) { + // 设置数据已存储状态 + data.setStored(true); + // 调用回调函数 + consumer.accept(aggregatedData); + } + } + } + + /** + * 对给定的门架统计数据集合进行汇总处理。 + * + * @param dataCollection 门架统计数据的集合,不可为null或空。 + * @param trafficDataPeriodType 统计数据的时段类型,例如:小时、日、月等。 + * @return 汇总后的门架统计数据对象,如果输入数据为空则返回null。 + */ + public DcGantryStatisticsData trafficStatistics(Collection dataCollection, TrafficDataPeriodTypeEnum trafficDataPeriodType) { + + // 判断输入数据是否为空 + if (CollectionUtils.isEmpty(dataCollection)) { + return null; + } + + // 创建一个汇总统计用的对象 + DcGantryStatisticsData aggregatedData = new DcGantryStatisticsData(); + + // 初始化车流量总和 + int trafficVolume = 0; + + // 初始化1型客车车流量 + int type1PassengerFlow = 0; + // 初始化2型客车车流量 + int type2PassengerFlow = 0; + // 初始化3型客车车流量 + int type3PassengerFlow = 0; + // 初始化4型客车车流量 + int type4PassengerFlow = 0; + + // 初始化1型货车车流量 + int type1TruckFlow = 0; + // 初始化2型货车车流量 + int type2TruckFlow = 0; + // 初始化3型货车车流量 + int type3TruckFlow = 0; + // 初始化4型货车车流量 + int type4TruckFlow = 0; + // 初始化5型货车车流量 + int type5TruckFlow = 0; + // 初始化6型货车车流量 + int type6TruckFlow = 0; + + // 初始化1型专项作业车车流量 + int type1SpecialVehicleFlow = 0; + // 初始化2型专项作业车车流量 + int type2SpecialVehicleFlow = 0; + // 初始化3型专项作业车车流量 + int type3SpecialVehicleFlow = 0; + // 初始化4型专项作业车车流量 + int type4SpecialVehicleFlow = 0; + // 初始化5型专项作业车车流量 + int type5SpecialVehicleFlow = 0; + // 初始化6型专项作业车车流量 + int type6SpecialVehicleFlow = 0; + + + // 遍历数据集合,累加各类车辆的车流量 + for (DcGantryStatisticsData data: dataCollection) { + // 累加车流量 + trafficVolume += data.getTrafficVolume(); + // 累加1型客车车流量 + type1PassengerFlow += data.getType1PassengerFlow(); + // 累加2型客车车流量 + type2PassengerFlow += data.getType2PassengerFlow(); + // 累加3型客车车流量 + type3PassengerFlow += data.getType3PassengerFlow(); + // 累加4型客车车流量 + type4PassengerFlow += data.getType4PassengerFlow(); + + // 累加1型货车车流量 + type1TruckFlow += data.getType1TruckFlow(); + // 累加2型货车车流量 + type2TruckFlow += data.getType2TruckFlow(); + // 累加3型货车车流量 + type3TruckFlow += data.getType3TruckFlow(); + // 累加4型货车车流量 + type4TruckFlow += data.getType4TruckFlow(); + // 累加5型货车车流量 + type5TruckFlow += data.getType5TruckFlow(); + // 累加6型货车车流量 + type6TruckFlow += data.getType6TruckFlow(); + + // 累加1型专项作业车车流量 + type1SpecialVehicleFlow += data.getType1SpecialVehicleFlow(); + // 累加2型专项作业车车流量 + type2SpecialVehicleFlow += data.getType2SpecialVehicleFlow(); + // 累加3型专项作业车车流量 + type3SpecialVehicleFlow += data.getType3SpecialVehicleFlow(); + // 累加4型专项作业车车流量 + type4SpecialVehicleFlow += data.getType4SpecialVehicleFlow(); + // 累加5型专项作业车车流量 + type5SpecialVehicleFlow += data.getType5SpecialVehicleFlow(); + // 累加6型专项作业车车流量 + type6SpecialVehicleFlow += data.getType6SpecialVehicleFlow(); + } + + // 使用第一个数据项的信息填充汇总统计对象的基本属性 + DcGantryStatisticsData firstDcTrafficSectionData = dataCollection.iterator().next(); + + // 设置门架标识 + aggregatedData.setGantryCode(firstDcTrafficSectionData.getGantryCode()); + // 设置统计时间 + aggregatedData.setStatisticalDate(firstDcTrafficSectionData.getStatisticalDate(), trafficDataPeriodType); + // 上报时间 + aggregatedData.setReportTime(firstDcTrafficSectionData.getReportTime()); + // 时段类型 + aggregatedData.setPeriodType(trafficDataPeriodType); + + // 车流量 + aggregatedData.setTrafficVolume(trafficVolume); + // 1型客车车流量 + aggregatedData.setType1PassengerFlow(type1PassengerFlow); + // 2型客车车流量 + aggregatedData.setType2PassengerFlow(type2PassengerFlow); + // 3型客车车流量 + aggregatedData.setType3PassengerFlow(type3PassengerFlow); + // 4型客车车流量 + aggregatedData.setType4PassengerFlow(type4PassengerFlow); + // 1型货车车流量 + aggregatedData.setType1TruckFlow(type1TruckFlow); + // 2型货车车流量 + aggregatedData.setType2TruckFlow(type2TruckFlow); + // 3型货车车流量 + aggregatedData.setType3TruckFlow(type3TruckFlow); + // 4型货车车流量 + aggregatedData.setType4TruckFlow(type4TruckFlow); + // 5型货车车流量 + aggregatedData.setType5TruckFlow(type5TruckFlow); + // 6型货车车流量 + aggregatedData.setType6TruckFlow(type6TruckFlow); + // 1型专项作业车车流量 + aggregatedData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow); + // 2型专项作业车车流量 + aggregatedData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow); + // 3型专项作业车车流量 + aggregatedData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow); + // 4型专项作业车车流量 + aggregatedData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow); + // 5型专项作业车车流量 + aggregatedData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow); + // 6型专项作业车车流量 + aggregatedData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow); + + // 生成主键 + aggregatedData.generateUniqueId(); + + return aggregatedData; + } + +} diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionAnalysis.java similarity index 99% rename from zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java rename to zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionAnalysis.java index e0bbe6b2..ccc0b476 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficAnalysis.java +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionAnalysis.java @@ -26,7 +26,7 @@ import java.util.stream.Collectors; * @author xiepufeng */ @Component -public class TrafficAnalysis { +public class TrafficSectionAnalysis { @Resource private RedisCache redisCache; diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficStatistics.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionStatistics.java similarity index 94% rename from zc-business/src/main/java/com/zc/business/statistics/handler/TrafficStatistics.java rename to zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionStatistics.java index f67fdf28..ac27d0af 100644 --- a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficStatistics.java +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficSectionStatistics.java @@ -11,7 +11,8 @@ import com.zc.business.domain.DcDevice; import com.zc.business.domain.DcTrafficSectionData; import com.zc.business.enums.*; import com.zc.business.mapper.DcTrafficSectionDataMapper; -import com.zc.business.statistics.cache.*; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import com.zc.business.statistics.cache.section.*; import com.zc.common.core.httpclient.exception.HttpException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,13 +26,13 @@ import java.util.*; import java.util.function.Consumer; /** - * 交通数据统计处理类。 + * 交通断面数据统计处理类。 * * 该类提供了一系列方法用于对交通数据进行统计分析,包括车流量总和、平均车速等。 * @author xiepufeng */ @Component -public class TrafficStatistics { +public class TrafficSectionStatistics { // 日志记录器 protected final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -51,22 +52,22 @@ public class TrafficStatistics { @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务 public void performHourlyCleanupAndPersist() { // 清除已过期的缓存数据 - DailyTrafficStatisticsCache.clearExpiredData(); - MonthlyTrafficStatisticsCache.clearExpiredData(); - QuarterlyTrafficStatisticsCache.clearExpiredData(); - YearlyTrafficStatisticsCache.clearExpiredData(); + DailyTrafficSectionStatisticsCache.clearExpiredData(); + MonthlyTrafficSectionStatisticsCache.clearExpiredData(); + QuarterlyTrafficSectionStatisticsCache.clearExpiredData(); + YearlyTrafficSectionStatisticsCache.clearExpiredData(); // 整合缓存数据并保存至数据库 // 将缓存中的数据按日统计后保存至数据库 // 添加月交通断面数据到缓存中 - persistAggregatedData(DailyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficStatisticsCache::addCacheData); + persistAggregatedData(DailyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficSectionStatisticsCache::addCacheData); // 将缓存中的数据按月统计后保存至数据库 // 添加季度交通断面数据到缓存中 - persistAggregatedData(MonthlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficStatisticsCache::addCacheData); + persistAggregatedData(MonthlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficSectionStatisticsCache::addCacheData); // 将缓存中的数据按季度统计后保存至数据库 // 添加年交通断面数据到缓存中 - persistAggregatedData(QuarterlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficStatisticsCache::addCacheData); + persistAggregatedData(QuarterlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficSectionStatisticsCache::addCacheData); // 将缓存中的数据按年统计后保存至数据库 - persistAggregatedData(YearlyTrafficStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); + persistAggregatedData(YearlyTrafficSectionStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); } @@ -160,7 +161,7 @@ public class TrafficStatistics { // 如果转换结果非空且不为空列表,则将转换后的数据添加到缓存中 if (dcTrafficSectionDataList != null && !dcTrafficSectionDataList.isEmpty()) { // 将转换后的数据添加到缓存中 - dcTrafficSectionDataList.forEach(DailyTrafficStatisticsCache::addCacheData); + dcTrafficSectionDataList.forEach(DailyTrafficSectionStatisticsCache::addCacheData); } }); @@ -398,11 +399,11 @@ public class TrafficStatistics { * 将缓存中的数据统计后保存至数据库。 */ public void persistAggregatedData( - Map cache, + Map> cache, TrafficDataPeriodTypeEnum periodType, Consumer consumer ) { - for (AbstractTrafficStatisticsCache data : cache.values()) { + for (AbstractTrafficStatisticsCache data : cache.values()) { // 如果数据已经存储过,则跳过此次处理 if (data.isStored()) { continue; @@ -436,7 +437,7 @@ public class TrafficStatistics { // 初始化车流量总和 int trafficVolume = 0; - // 初始化最大车流量 + // 初始化大车车流量总和 int largeTrafficVolume = 0; // 初始化计算平均车速所需的分子部分 double numerator = 0; diff --git a/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficTollStationStatistics.java b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficTollStationStatistics.java new file mode 100644 index 00000000..be259328 --- /dev/null +++ b/zc-business/src/main/java/com/zc/business/statistics/handler/TrafficTollStationStatistics.java @@ -0,0 +1,243 @@ +package com.zc.business.statistics.handler; + +import com.zc.business.domain.DcTollStationStatisticsData; +import com.zc.business.enums.TrafficDataPeriodTypeEnum; +import com.zc.business.mapper.DcTollStationStatisticsDataMapper; +import com.zc.business.service.IDcTollStationStatisticsDataService; +import com.zc.business.statistics.cache.AbstractTrafficStatisticsCache; +import com.zc.business.statistics.cache.tollstation.DailyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.MonthlyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.QuarterlyTrafficTollStationStatisticsCache; +import com.zc.business.statistics.cache.tollstation.YearlyTrafficTollStationStatisticsCache; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import javax.annotation.Resource; +import java.util.Collection; +import java.util.Map; +import java.util.function.Consumer; + +/** + * TrafficTollStationStatistics类用于处理交通收费站的统计数据。 + * @author xiepufeng + */ +@Component +public class TrafficTollStationStatistics { + + // 日志记录器 + protected final Logger logger = LoggerFactory.getLogger(this.getClass()); + + @Resource + private IDcTollStationStatisticsDataService dcTollStationStatisticsDataService; + + @Resource + private DcTollStationStatisticsDataMapper dcTollStationStatisticsDataMapper; + + + /** + * 定义每小时第20分钟执行的任务,用于清除过期缓存数据并将缓存中的数据整合后保存至数据库。 + */ + @Scheduled(cron = "0 20 * * * ?") // 每小时的20分整点执行该任务 + public void performHourlyCleanupAndPersist() { + + // 添加日收费站站点数据到缓存中 + dcTollStationStatisticsDataService.lastHourData().forEach(DailyTrafficTollStationStatisticsCache::addCacheData); + + // 清除已过期的缓存数据 + DailyTrafficTollStationStatisticsCache.clearExpiredData(); + MonthlyTrafficTollStationStatisticsCache.clearExpiredData(); + QuarterlyTrafficTollStationStatisticsCache.clearExpiredData(); + YearlyTrafficTollStationStatisticsCache.clearExpiredData(); + + // 整合缓存数据并保存至数据库 + // 将缓存中的数据按日统计后保存至数据库 + // 添加月收费站站点数据到缓存中 + persistAggregatedData(DailyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.DAY, MonthlyTrafficTollStationStatisticsCache::addCacheData); + // 将缓存中的数据按月统计后保存至数据库 + // 添加季度收费站站点数据到缓存中 + persistAggregatedData(MonthlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.MONTH, QuarterlyTrafficTollStationStatisticsCache::addCacheData); + // 将缓存中的数据按季度统计后保存至数据库 + // 添加年收费站站点数据到缓存中 + persistAggregatedData(QuarterlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.QUARTER, YearlyTrafficTollStationStatisticsCache::addCacheData); + // 将缓存中的数据按年统计后保存至数据库 + persistAggregatedData(YearlyTrafficTollStationStatisticsCache.getCache(), TrafficDataPeriodTypeEnum.YEAR, (a) -> {}); + } + + /** + * 将缓存中的数据统计后保存至数据库。 + */ + public void persistAggregatedData( + Map> cache, + TrafficDataPeriodTypeEnum periodType, + Consumer consumer + ) { + for (AbstractTrafficStatisticsCache data : cache.values()) { + // 如果数据已经存储过,则跳过此次处理 + if (data.isStored()) { + continue; + } + DcTollStationStatisticsData aggregatedData = trafficStatistics(data.getData(), periodType); + if (dcTollStationStatisticsDataMapper.insertOrUpdate(aggregatedData)) { + // 设置数据已存储状态 + data.setStored(true); + // 调用回调函数 + consumer.accept(aggregatedData); + } + } + } + + /** + * 对给定的收费站点统计数据集合进行汇总处理。 + * + * @param dataCollection 收费站点统计数据的集合,不可为null或空。 + * @param trafficDataPeriodType 统计数据的时段类型,例如:小时、日、月等。 + * @return 汇总后的收费站点统计数据对象,如果输入数据为空则返回null。 + */ + public DcTollStationStatisticsData trafficStatistics(Collection dataCollection, TrafficDataPeriodTypeEnum trafficDataPeriodType) { + + // 判断输入数据是否为空 + if (CollectionUtils.isEmpty(dataCollection)) { + return null; + } + + // 创建一个汇总统计用的对象 + DcTollStationStatisticsData aggregatedData = new DcTollStationStatisticsData(); + + // 初始化车流量总和 + int trafficVolume = 0; + + // 初始化1型客车车流量 + int type1PassengerFlow = 0; + // 初始化2型客车车流量 + int type2PassengerFlow = 0; + // 初始化3型客车车流量 + int type3PassengerFlow = 0; + // 初始化4型客车车流量 + int type4PassengerFlow = 0; + + // 初始化1型货车车流量 + int type1TruckFlow = 0; + // 初始化2型货车车流量 + int type2TruckFlow = 0; + // 初始化3型货车车流量 + int type3TruckFlow = 0; + // 初始化4型货车车流量 + int type4TruckFlow = 0; + // 初始化5型货车车流量 + int type5TruckFlow = 0; + // 初始化6型货车车流量 + int type6TruckFlow = 0; + + // 初始化1型专项作业车车流量 + int type1SpecialVehicleFlow = 0; + // 初始化2型专项作业车车流量 + int type2SpecialVehicleFlow = 0; + // 初始化3型专项作业车车流量 + int type3SpecialVehicleFlow = 0; + // 初始化4型专项作业车车流量 + int type4SpecialVehicleFlow = 0; + // 初始化5型专项作业车车流量 + int type5SpecialVehicleFlow = 0; + // 初始化6型专项作业车车流量 + int type6SpecialVehicleFlow = 0; + + + // 遍历数据集合,累加各类车辆的车流量 + for (DcTollStationStatisticsData data: dataCollection) { + // 累加车流量 + trafficVolume += data.getTrafficVolume(); + // 累加1型客车车流量 + type1PassengerFlow += data.getType1PassengerFlow(); + // 累加2型客车车流量 + type2PassengerFlow += data.getType2PassengerFlow(); + // 累加3型客车车流量 + type3PassengerFlow += data.getType3PassengerFlow(); + // 累加4型客车车流量 + type4PassengerFlow += data.getType4PassengerFlow(); + + // 累加1型货车车流量 + type1TruckFlow += data.getType1TruckFlow(); + // 累加2型货车车流量 + type2TruckFlow += data.getType2TruckFlow(); + // 累加3型货车车流量 + type3TruckFlow += data.getType3TruckFlow(); + // 累加4型货车车流量 + type4TruckFlow += data.getType4TruckFlow(); + // 累加5型货车车流量 + type5TruckFlow += data.getType5TruckFlow(); + // 累加6型货车车流量 + type6TruckFlow += data.getType6TruckFlow(); + + // 累加1型专项作业车车流量 + type1SpecialVehicleFlow += data.getType1SpecialVehicleFlow(); + // 累加2型专项作业车车流量 + type2SpecialVehicleFlow += data.getType2SpecialVehicleFlow(); + // 累加3型专项作业车车流量 + type3SpecialVehicleFlow += data.getType3SpecialVehicleFlow(); + // 累加4型专项作业车车流量 + type4SpecialVehicleFlow += data.getType4SpecialVehicleFlow(); + // 累加5型专项作业车车流量 + type5SpecialVehicleFlow += data.getType5SpecialVehicleFlow(); + // 累加6型专项作业车车流量 + type6SpecialVehicleFlow += data.getType6SpecialVehicleFlow(); + } + + // 使用第一个数据项的信息填充汇总统计对象的基本属性 + DcTollStationStatisticsData firstDcTrafficSectionData = dataCollection.iterator().next(); + + // 设置收费站站点标识 + aggregatedData.setTollStationCode(firstDcTrafficSectionData.getTollStationCode()); + // 设置统计时间 + aggregatedData.setStatisticalDate(firstDcTrafficSectionData.getStatisticalDate(), trafficDataPeriodType); + // 上报时间 + aggregatedData.setReportTime(firstDcTrafficSectionData.getReportTime()); + // 时段类型 + aggregatedData.setPeriodType(trafficDataPeriodType); + // 出入类型 + aggregatedData.setAccessType(firstDcTrafficSectionData.getAccessType()); + + // 车流量 + aggregatedData.setTrafficVolume(trafficVolume); + // 1型客车车流量 + aggregatedData.setType1PassengerFlow(type1PassengerFlow); + // 2型客车车流量 + aggregatedData.setType2PassengerFlow(type2PassengerFlow); + // 3型客车车流量 + aggregatedData.setType3PassengerFlow(type3PassengerFlow); + // 4型客车车流量 + aggregatedData.setType4PassengerFlow(type4PassengerFlow); + // 1型货车车流量 + aggregatedData.setType1TruckFlow(type1TruckFlow); + // 2型货车车流量 + aggregatedData.setType2TruckFlow(type2TruckFlow); + // 3型货车车流量 + aggregatedData.setType3TruckFlow(type3TruckFlow); + // 4型货车车流量 + aggregatedData.setType4TruckFlow(type4TruckFlow); + // 5型货车车流量 + aggregatedData.setType5TruckFlow(type5TruckFlow); + // 6型货车车流量 + aggregatedData.setType6TruckFlow(type6TruckFlow); + // 1型专项作业车车流量 + aggregatedData.setType1SpecialVehicleFlow(type1SpecialVehicleFlow); + // 2型专项作业车车流量 + aggregatedData.setType2SpecialVehicleFlow(type2SpecialVehicleFlow); + // 3型专项作业车车流量 + aggregatedData.setType3SpecialVehicleFlow(type3SpecialVehicleFlow); + // 4型专项作业车车流量 + aggregatedData.setType4SpecialVehicleFlow(type4SpecialVehicleFlow); + // 5型专项作业车车流量 + aggregatedData.setType5SpecialVehicleFlow(type5SpecialVehicleFlow); + // 6型专项作业车车流量 + aggregatedData.setType6SpecialVehicleFlow(type6SpecialVehicleFlow); + + // 生成主键 + aggregatedData.generateUniqueId(); + + return aggregatedData; + } + +} diff --git a/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml b/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml new file mode 100644 index 00000000..851f7fe9 --- /dev/null +++ b/zc-business/src/main/resources/mapper/business/DcGantryStatisticsDataMapper.xml @@ -0,0 +1,91 @@ + + + + + + + INSERT INTO + dc_gantry_statistics_data + ( + id, + gantry_code, + statistical_date, + traffic_volume, + period_type, + create_time, + type1_passenger_flow, + type2_passenger_flow, + type3_passenger_flow, + type4_passenger_flow, + type1_truck_flow, + type2_truck_flow, + type3_truck_flow, + type4_truck_flow, + type5_truck_flow, + type6_truck_flow, + type1_special_vehicle_flow, + type2_special_vehicle_flow, + type3_special_vehicle_flow, + type4_special_vehicle_flow, + type5_special_vehicle_flow, + type6_special_vehicle_flow + ) + VALUES + ( + #{id}, + #{gantryCode}, + #{statisticalDate}, + #{trafficVolume}, + #{periodType}, + NOW(), + #{type1PassengerFlow}, + #{type2PassengerFlow}, + #{type3PassengerFlow}, + #{type4PassengerFlow}, + #{type1TruckFlow}, + #{type2TruckFlow}, + #{type3TruckFlow}, + #{type4TruckFlow}, + #{type5TruckFlow}, + #{type6TruckFlow}, + #{type1SpecialVehicleFlow}, + #{type2SpecialVehicleFlow}, + #{type3SpecialVehicleFlow}, + #{type4SpecialVehicleFlow}, + #{type5SpecialVehicleFlow}, + #{type6SpecialVehicleFlow} + ) + ON DUPLICATE KEY UPDATE + gantry_code = VALUES(gantry_code), + statistical_date = VALUES(statistical_date), + traffic_volume = VALUES(traffic_volume), + period_type = VALUES(period_type), + update_time = NOW(), + type1_passenger_flow = VALUES(type1_passenger_flow), + type2_passenger_flow = VALUES(type2_passenger_flow), + type3_passenger_flow = VALUES(type3_passenger_flow), + type4_passenger_flow = VALUES(type4_passenger_flow), + type1_truck_flow = VALUES(type1_truck_flow), + type2_truck_flow = VALUES(type2_truck_flow), + type3_truck_flow = VALUES(type3_truck_flow), + type4_truck_flow = VALUES(type4_truck_flow), + type5_truck_flow = VALUES(type5_truck_flow), + type6_truck_flow = VALUES(type6_truck_flow), + type1_special_vehicle_flow = VALUES(type1_special_vehicle_flow), + type2_special_vehicle_flow = VALUES(type2_special_vehicle_flow), + type3_special_vehicle_flow = VALUES(type3_special_vehicle_flow), + type4_special_vehicle_flow = VALUES(type4_special_vehicle_flow), + type5_special_vehicle_flow = VALUES(type5_special_vehicle_flow), + type6_special_vehicle_flow = VALUES(type6_special_vehicle_flow) + + + + + diff --git a/zc-business/src/main/resources/mapper/business/DcTollStationStatisticsDataMapper.xml b/zc-business/src/main/resources/mapper/business/DcTollStationStatisticsDataMapper.xml new file mode 100644 index 00000000..32e63fa5 --- /dev/null +++ b/zc-business/src/main/resources/mapper/business/DcTollStationStatisticsDataMapper.xml @@ -0,0 +1,94 @@ + + + + + + + INSERT INTO + dc_toll_station_statistics_data + ( + id, + toll_station_code, + statistical_date, + traffic_volume, + period_type, + access_type, + create_time, + type1_passenger_flow, + type2_passenger_flow, + type3_passenger_flow, + type4_passenger_flow, + type1_truck_flow, + type2_truck_flow, + type3_truck_flow, + type4_truck_flow, + type5_truck_flow, + type6_truck_flow, + type1_special_vehicle_flow, + type2_special_vehicle_flow, + type3_special_vehicle_flow, + type4_special_vehicle_flow, + type5_special_vehicle_flow, + type6_special_vehicle_flow + ) + VALUES + ( + #{id}, + #{tollStationCode}, + #{statisticalDate}, + #{trafficVolume}, + #{periodType}, + #{accessType}, + NOW(), + #{type1PassengerFlow}, + #{type2PassengerFlow}, + #{type3PassengerFlow}, + #{type4PassengerFlow}, + #{type1TruckFlow}, + #{type2TruckFlow}, + #{type3TruckFlow}, + #{type4TruckFlow}, + #{type5TruckFlow}, + #{type6TruckFlow}, + #{type1SpecialVehicleFlow}, + #{type2SpecialVehicleFlow}, + #{type3SpecialVehicleFlow}, + #{type4SpecialVehicleFlow}, + #{type5SpecialVehicleFlow}, + #{type6SpecialVehicleFlow} + ) + ON DUPLICATE KEY UPDATE + toll_station_code = VALUES(toll_station_code), + statistical_date = VALUES(statistical_date), + traffic_volume = VALUES(traffic_volume), + period_type = VALUES(period_type), + access_type = VALUES(access_type), + update_time = NOW(), + type1_passenger_flow = VALUES(type1_passenger_flow), + type2_passenger_flow = VALUES(type2_passenger_flow), + type3_passenger_flow = VALUES(type3_passenger_flow), + type4_passenger_flow = VALUES(type4_passenger_flow), + type1_truck_flow = VALUES(type1_truck_flow), + type2_truck_flow = VALUES(type2_truck_flow), + type3_truck_flow = VALUES(type3_truck_flow), + type4_truck_flow = VALUES(type4_truck_flow), + type5_truck_flow = VALUES(type5_truck_flow), + type6_truck_flow = VALUES(type6_truck_flow), + type1_special_vehicle_flow = VALUES(type1_special_vehicle_flow), + type2_special_vehicle_flow = VALUES(type2_special_vehicle_flow), + type3_special_vehicle_flow = VALUES(type3_special_vehicle_flow), + type4_special_vehicle_flow = VALUES(type4_special_vehicle_flow), + type5_special_vehicle_flow = VALUES(type5_special_vehicle_flow), + type6_special_vehicle_flow = VALUES(type6_special_vehicle_flow) + + + + + diff --git a/zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml b/zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml index c0c79aca..c56e0a2c 100644 --- a/zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml +++ b/zc-business/src/main/resources/mapper/business/EventPlanAssocMapper.xml @@ -26,6 +26,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" create_time, update_time, control_result, + control, #{eventId}, @@ -35,6 +36,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{createTime}, #{updateTime}, #{controlResult}, + #{control},