Commit 014244de authored by 黄伟铭's avatar 黄伟铭

Merge remote-tracking branch 'origin/master'

parents 98f8907c e872a3e0
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
<modules> <modules>
<module>identity-client-starter</module> <module>identity-client-starter</module>
<module>signal-optimize-service</module> <module>signal-optimize-service</module>
<module>signal-optimize-data-compute</module>
<module>signal-feign-service</module> <module>signal-feign-service</module>
<module>wj-common</module> <module>wj-common</module>
<module>wj-databus</module> <module>wj-databus</module>
......
This diff is collapsed.
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
<id>1.0</id>
<formats>
<format>tar.gz</format>
<format>dir</format>
</formats>
<includeBaseDirectory>true</includeBaseDirectory>
<fileSets>
<!-- for bin -->
<fileSet>
<directory>src/main/bin</directory>
<includes>
<include>*.*</include>
</includes>
<directoryMode>775</directoryMode>
<outputDirectory>bin/</outputDirectory>
</fileSet>
<!-- for configs -->
<fileSet>
<directory>src/main/resources</directory>
<includes>
<include>*.yml</include>
<include>*.xml</include>
</includes>
<outputDirectory>/</outputDirectory>
</fileSet>
<!-- for jar -->
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
</fileSets>
<!-- for lib -->
<dependencySets>
<dependencySet>
<outputDirectory>lib/</outputDirectory>
<scope>runtime</scope>
<excludes>
<exclude>${groupId}:${artifactId}</exclude>
</excludes>
</dependencySet>
</dependencySets>
</assembly>
package net.wanji.opt;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @author
*/
@SpringBootApplication(scanBasePackages = {"net.wanji.opt", "net.wanji.databus", "net.wanji.common",
"net.wanji.opt.controller","net.wanji.opt.synthesis.controller"})
@MapperScan(basePackages = {"net.wanji.opt.dao.mapper", "net.wanji.databus.dao.mapper"})
@EnableTransactionManagement
@EnableScheduling
@EnableAsync
@EnableKafka
public class SignalOptimizeApplication {
public static void main(String[] args) {
SpringApplication.run(SignalOptimizeApplication.class, args);
}
}
package net.wanji.opt.api;
import com.alibaba.fastjson.JSON;
/**
* 统一API响应结果封装
*/
public class ResponseResult {
private int status;
private String message;
private Object content;
private Integer code;
public ResponseResult setCode(ResultCode resultCode) {
this.status = resultCode.status;
return this;
}
public Object getContent() {
return content;
}
public ResponseResult setContent(Object data) {
this.content = data;
return this;
}
@Override
public String toString() {
return JSON.toJSONString(this);
}
public int getStatus() {
return status;
}
public ResponseResult setStatus(int status) {
this.status = status;
return this;
}
public String getMessage() {
return message;
}
public ResponseResult setMessage(String msg) {
this.message = msg;
return this;
}
}
package net.wanji.opt.api;
/**
* 响应码枚举,参考HTTP状态码的语义
*/
public enum ResultCode {
SUCCESS(200),//成功
FAIL(400),//失败
UNAUTHORIZED(401),//未认证(签名错误)
NOT_FOUND(404),//接口不存在
INTERNAL_SERVER_ERROR(500);//服务器内部错误
public int status;
ResultCode(int status) {
this.status = status;
}
}
package net.wanji.opt.api;
/**
* 响应结果生成工具
*/
public class ResultGenerator {
private static final String DEFAULT_SUCCESS_MESSAGE = "success";
public static ResponseResult genSuccessResult() {
return new ResponseResult()
.setCode(ResultCode.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE);
}
public static ResponseResult genSuccessResult(Object data) {
return new ResponseResult()
.setCode(ResultCode.SUCCESS)
.setMessage(DEFAULT_SUCCESS_MESSAGE)
.setContent(data);
}
public static ResponseResult genFailResult(String message) {
return new ResponseResult()
.setCode(ResultCode.FAIL)
.setMessage(message);
}
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "AbnormalDetailBO", description = "异常事件详情")
public class AbnormalDetailBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "小时分钟 HH:mm")
private String hourMinute;
@ApiModelProperty(value = "月日,格式 7/9")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "M/d", timezone = "GMT+8")
private Date problemDate;
@ApiModelProperty(value = "状态 0正常 1 失衡 2 拥堵 3 溢出 4 死锁")
private Integer status ;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "BottomCurveBO", description = "底部曲线图")
public class BottomCurveBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "范围 0路口 1进口道 2方向 3车道")
private Integer scope;
@ApiModelProperty(value = "范围列表")
List<String> scopeList;
@ApiModelProperty(value = "时间粒度(分钟)")
private Integer minutes;
@ApiModelProperty(value = "指标名称")
private String metricCode;
@ApiModelProperty(value = "开始时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
@ApiModelProperty(value = "结束时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "BottomMenuBO", description = "底部曲线图范围下拉列表")
public class BottomMenuBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "范围 1进口道 2方向 3车道")
private Integer scope;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "CrossIdAndIsFirstBO", description = "表格实时推送")
public class CrossIdAndIsFirstBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "是否初次调用 0否 1是")
private Integer isFirstInvoke;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "CrossIdAndTimeSpanBO", description = "路口时段入参")
public class CrossIdAndTimeSpanBO {
@ApiModelProperty(value = "路口编号")
private String crossId;
@ApiModelProperty(value = "时段开始时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
@ApiModelProperty(value = "时段结束时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "CrossNameBO", description = "路口名称")
public class CrossNameBO {
@ApiModelProperty(value = "路口名称")
private String crossName;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "CrossSchemeListBO", description = "路口方案列表入参")
public class CrossSchemeListBO {
@ApiModelProperty(value = "路口编号", required = true)
private String crossId;
@ApiModelProperty(value = "方案编号/名称")
private String schemeNoOrName;
@ApiModelProperty(value = "策略编号 100030均衡调控 100152效率提升 100010安全保障")
private String strategyCode;
@ApiModelProperty(value = "星期:1周一,2周二,3周三,4周四,5周五,6周六,7周日,0特殊日期", required = true)
private Integer weekDay;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
import java.util.List;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "CurveChartBO", description = "方案详情-曲线图")
public class CurveChartBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "时间粒度(分钟)", required = true)
private Integer minutes;
@ApiModelProperty(value = "年月日,格式 2023/7/9")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy/M/d", timezone = "GMT+8")
private Date problemDate;
@ApiModelProperty(value = "开始时间 HH:mm")
private String startTime;
@ApiModelProperty(value = "结束时间 HH:mm")
private String endTime;
@ApiModelProperty(value = "指标编号 1不停车通过率,2一/二/三次及以上停车通过率...")
private String MetricCode;
private List<DirTurn> dirTurnList;
@NoArgsConstructor
@Data
public static class DirTurn {
@ApiModelProperty(value = "进口方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北")
private String dir;
@ApiModelProperty(value = "转向:u掉头;l左转;s直行;r右转")
private String turn;
}
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "GreenwaveDetailBO", description = "绿波详情")
public class GreenwaveDetailBO {
@ApiModelProperty(value = "绿波名称")
private String greenwaveName;
@ApiModelProperty(value = "开始日期 yyyy/MM/dd")
private String startDate;
@ApiModelProperty(value = "结束日期 yyyy/MM/dd")
private String endDate;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "GreenwaveIdAndTimeStampBO", description = "绿波ID和时间戳")
public class GreenwaveIdAndTimeStampBO {
@ApiModelProperty(value = "绿波ID")
private Integer greenwaveId;
@ApiModelProperty(value = "时间戳(秒)")
private Long timeStamp;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "GreenwaveIdBO", description = "绿波ID")
public class GreenwaveIdBO {
@ApiModelProperty(value = "绿波ID")
private Integer greenwaveId;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "GreenwaveNameBO", description = "绿波名称")
public class GreenwaveNameBO {
@ApiModelProperty(value = "绿波名称")
private String greenwaveName;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MainlineCrossEvaluateBO", description = "干线路口评价")
public class MainlineCrossEvaluateBO {
@ApiModelProperty(value = "方案名称")
private String greenwaveName;
@ApiModelProperty(value = "指标名称")
private String metricName;
@ApiModelProperty(value = "路口ID列表")
private List<String> crossIdList;
@ApiModelProperty(value = "分析时段开始时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
@ApiModelProperty(value = "分析时段结束时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
@ApiModelProperty(value = "干线方案时段开始时间 格式 HH:mm")
private String startHourMinute;
@ApiModelProperty(value = "干线方案时段结束时间 格式 HH:mm")
private String endHourMinute;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MainlineSchemeAnalysisBO", description = "干线方案分析")
public class MainlineSchemeAnalysisBO {
@ApiModelProperty(value = "干线名称")
private String name;
@ApiModelProperty(value = "分析时段开始时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
@ApiModelProperty(value = "分析时段结束时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MainlineSchemeEvaluateBO", description = "干线方案评价")
public class MainlineSchemeEvaluateBO {
@ApiModelProperty(value = "绿波名称")
private String greenwaveName;
@ApiModelProperty(value = "方向名称")
private String dirName;
@ApiModelProperty(value = "指标名称")
private String metricName;
@ApiModelProperty(value = "分析时段开始时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime;
@ApiModelProperty(value = "分析时段结束时间 格式 yyyy-MM-dd HH:mm:ss")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime;
@ApiModelProperty(value = "是否用于城市大脑")
private Integer isCityBrain;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MetricsDetailBO", description = "详细指标查询输入参数")
public class MetricsDetailBO {
@ApiModelProperty(value = "路口ID", required = true)
private String crossId;
@ApiModelProperty(value = "进口方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北")
private Integer dir;
@ApiModelProperty(value = "转向:u掉头;l左转;s直行;r右转")
private String turn;
@ApiModelProperty(value = "车道序号,从左车道开始编号11、12、13...")
private Integer laneSort;
@ApiModelProperty(value = "日期 yyyy-MM-dd", required = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
@ApiModelProperty(value = "时间粒度(分钟)", required = true)
private Integer minutes;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "MetricsTurnAndLaneBO", description = "转向级和车道级指标查询输入参数")
public class MetricsTurnAndLaneBO {
@ApiModelProperty(value = "0正常 1失衡 2拥堵 3溢出")
private Integer status;
@ApiModelProperty(value = "路口ID", required = true)
private String crossId;
@ApiModelProperty(value = "进口方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北", required = true)
private Integer dir;
@ApiModelProperty(value = "时间 HH:mm", required = true)
private String hourMinute;
@ApiModelProperty(value = "日期 yyyy-MM-dd")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date day;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "ProblemSchemeBO", description = "问题方案列表接收参数")
public class ProblemSchemeBO {
@ApiModelProperty(value = "路口ID", required = true)
private String crossId;
@ApiModelProperty(value = "开始日期 yyyy-MM-dd")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date startDate;
@ApiModelProperty(value = "结束日期 yyyy-MM-dd")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date endDate;
@ApiModelProperty(value = "开始时间 HH:mm")
String startTime;
@ApiModelProperty(value = "结束时间 HH:mm")
String endTime;
}
package net.wanji.opt.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "SaveGreenwaveStrategyBO", description = "保存子区策略输入参数")
public class SaveGreenwaveStrategyBO {
@ApiModelProperty(value = "绿波ID")
private Integer greenwaveId;
@ApiModelProperty(value = "策略名称")
private List<String> strategyNames;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "SceneMetricsDetailBO", description = "场景评价-详细指标查询输入参数")
public class SceneMetricsDetailBO {
@ApiModelProperty(value = "路口ID", required = true)
private String crossId;
@ApiModelProperty(value = "进口方向:1北;2东北;3东;4东南;5南;6西南;7西;8西北")
private Integer dir;
@ApiModelProperty(value = "转向:u掉头;l左转;s直行;r右转")
private String turn;
@ApiModelProperty(value = "车道序号,从左车道开始编号11、12、13...")
private Integer laneSort;
@ApiModelProperty(value = "日期 yyyy-MM-dd", required = true)
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date date;
@ApiModelProperty(value = "时间粒度(分钟)", required = true)
private Integer minutes;
@ApiModelProperty(value = "指标编号列表", required = true)
List<String> metricCodes;
}
package net.wanji.opt.bo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.Date;
/**
* @author Kent HAN
* @date 2023/6/9 13:52
*/
@Data
@ApiModel(value = "SchemeDetailOverallBO", description = "方案详情-总体评价入参")
public class SchemeDetailOverallBO {
@ApiModelProperty(value = "路口ID")
private String crossId;
@ApiModelProperty(value = "年月日,格式 2023/7/9")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy/M/d", timezone = "GMT+8")
private Date problemDate;
@ApiModelProperty(value = "开始时间 HH:mm")
String startTime;
@ApiModelProperty(value = "结束时间 HH:mm")
String endTime;
@ApiModelProperty(value = "策略编号 100030均衡调控 100152效率提升 100010安全保障")
String strategyCode;
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.databus.dao.mapper.BaseCrossInfoMapper;
import net.wanji.databus.po.BaseCrossInfoPO;
import net.wanji.databus.vo.CrossInfoVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @author duanruiming
* @date 2023/03/14 10:42
*/
@Component
@Order(1)
@Slf4j
public class BaseCrossInfoCache implements CommandLineRunner {
@Resource
private BaseCrossInfoMapper baseCrossInfoMapper;
private static final List<BaseCrossInfoPO> crossInfoList = new ArrayList<>();
/**
* 获取所有路口信息
*
* @return
*/
public List<BaseCrossInfoPO> getCrossInfoCache() {
return crossInfoList;
}
/**
* 通过路口编号查询经纬度
*/
public static String getCoordinateByCrossId(String crossId) {
for (BaseCrossInfoPO baseCrossInfoPO : crossInfoList) {
if (StringUtils.equals(crossId, baseCrossInfoPO.getId())) {
return baseCrossInfoPO.getLocation();
}
}
return null;
}
/**
* 获取所有信控路口的路口编号
*
* @return
*/
public List<String> getIsSignalCrossIdList() {
return crossInfoList.stream().filter(crossInfoPO -> crossInfoPO.getIsSignal() == 1).map(BaseCrossInfoPO::getId).collect(Collectors.toList());
}
public String getCrossName(String crossId) {
for (BaseCrossInfoPO baseCrossInfoPO : crossInfoList) {
if (StringUtils.equals(crossId, baseCrossInfoPO.getId())) {
return baseCrossInfoPO.getName();
}
}
return "";
}
@Override
public void run(String... args) throws Exception {
init();
}
public void init() {
List<BaseCrossInfoPO> baseCrossInfoPOS = baseCrossInfoMapper.selectAll(new CrossInfoVO());
if (!CollectionUtils.isEmpty(baseCrossInfoPOS)) {
List<BaseCrossInfoPO> collect = baseCrossInfoPOS.stream().filter(po -> Objects.equals(po.getIsSignal(), 1)).collect(Collectors.toList());
crossInfoList.addAll(collect);
}
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.databus.dao.entity.CrossPhasePO;
import net.wanji.databus.dao.mapper.BaseCrossPhaseMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* @author duanruiming
* @date 2023/03/06 15:03
*/
@Component
@Slf4j
public class BaseCrossPhaseInfoCache implements CommandLineRunner {
@Resource
BaseCrossPhaseMapper baseCrossPhaseMapper;
/**
* 路口相位缓存
* key:crossId value:List<CrossPhasePO>
*/
private static final Map<String, List<CrossPhasePO>> crossPhaseInfoMap = new HashMap<>();
/**
* 获取所有路口相位列表
* @return
*/
public static Map<String, List<CrossPhasePO>> getCrossPhaseInfoCache() {
return crossPhaseInfoMap;
}
/**
* 通过路口编号获取路口列表
* @param crossId
* @return
*/
public List<CrossPhasePO> getCrossPhaseCacheByCrossId(String crossId) {
if (!crossPhaseInfoMap.isEmpty()) {
return crossPhaseInfoMap.get(crossId);
}
return Collections.emptyList();
}
@Override
public void run(String... args) throws Exception {
init();
}
public void init() {
List<CrossPhasePO> crossPhasePOS = baseCrossPhaseMapper.listCrossPhasePO(new CrossPhasePO());
if (!CollectionUtils.isEmpty(crossPhasePOS)) {
crossPhaseInfoMap.putAll(crossPhasePOS.stream().collect(Collectors.groupingBy(CrossPhasePO::getCrossId)));
}
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.framework.Constants;
import net.wanji.databus.dao.entity.BaseCrossSchemePO;
import net.wanji.databus.dao.mapper.BaseCrossSchemeMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author duanruiming
* @date 2023/03/06 14:30
*/
@Component
@Slf4j
public class BaseCrossSchemeInfoCache implements CommandLineRunner {
@Resource
BaseCrossSchemeMapper baseCrossSchemeMapper;
/**
* key:crossId_id value:CrossSchemePO
*/
private static final Map<String, BaseCrossSchemePO> crossSchemeInfoMap = new HashMap<>();
/**
* 获取所有路口方案信息
* @return
*/
public static Map<String, BaseCrossSchemePO> getCrossSchemeInfosCache() {
return crossSchemeInfoMap;
}
/**
* 通过路口编号_方案id获取方案信息
* @param key: crossId_id
* @return
*/
public BaseCrossSchemePO getSchemePOCache(String key) {
if (!crossSchemeInfoMap.isEmpty()) {
return crossSchemeInfoMap.get(key);
}
return null;
}
@Override
public void run(String... args) throws Exception {
init();
}
private void init() {
try {
long start = System.currentTimeMillis();
List<BaseCrossSchemePO> baseCrossSchemePOS = baseCrossSchemeMapper.listCrossSchemeInfo(Constants.SystemParam.NULL, Constants.SystemParam.NULL, null);
if (!CollectionUtils.isEmpty(baseCrossSchemePOS)) {
baseCrossSchemePOS.forEach(crossSchemePO -> {
String crossId = crossSchemePO.getCrossId();
Integer schemeId = crossSchemePO.getId();
String key = String.join(Constants.SystemParam.SEPARATOR_UNDER_LINE, crossId, String.valueOf(schemeId));
crossSchemeInfoMap.put(key, crossSchemePO);
});
}
long end = System.currentTimeMillis();
log.info("加载路口方案信息到缓存耗时:{}ms,条数据,size={}", (end - start), crossSchemeInfoMap.size());
} catch (Exception e) {
log.info("加载路口方案信息到缓存失败", e);
}
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.enums.ControlModeEnum;
import net.wanji.common.enums.TurnConvertEnum;
import net.wanji.common.enums.WeekEnum;
import net.wanji.common.framework.Constants;
import net.wanji.common.utils.tool.DateUtil;
import net.wanji.opt.dto.*;
import net.wanji.opt.service.CrossSchedulesService;
import net.wanji.opt.service.CrossSchemeService;
import org.springframework.beans.BeanUtils;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author duanruiming
* @date 2023/03/02 10:42
*/
@Component
@Slf4j
public class CrossDirTurnPhaseCache implements CommandLineRunner {
private static final Map<String, CrossPhaseDTO> phaseMap = new HashMap<>();
@Resource
CrossSchedulesService crossSchedulesService;
@Resource
CrossSchemeService crossSchemeService;
/**
* 当前时间路口运行的方案相位转向数据信息
* 统一获取缓存方法
* key: crossId-dir-turn value:CrossPhaseDTO
*
* @return
*/
public Map<String, CrossPhaseDTO> getPhaseDirTurnMap() {
return phaseMap;
}
/**
* 使用前刷新当前缓存,当前缓存获取当前运行路口运行方案的相位车道信息
*/
public void refresh() {
init();
}
@Override
public void run(String... args) {
init();
}
/**
* 初始化相位方向转向缓存
*
* key: crossId-dir-turn value:CrossPhaseDTO
*/
private void init() {
try {
long start = System.currentTimeMillis();
// 获取已执行的调度详情信息
List<CrossSchedulesDTO> schedulesDTOList = crossSchedulesService.listCrossSchedulesDetails(Constants.SystemParam.NULL, Constants.SystemParam.YES);
// 获取当前运行时段信息
List<CrossSectionDTO> sectionInfos = listSection(schedulesDTOList);
// 获取方案详情信息
List<CrossSchemeDTO> schemeDTOList = crossSchemeService.listCrossSchemeDetails(Constants.SystemParam.NULL);
Map<Integer, CrossSchemeDTO> schemeMap = schemeDTOList.stream().collect(Collectors.toMap(CrossSchemeDTO::getId, e -> e));
String key;
String crossId;
Integer dir;
String[] turnArr;
Set<String> turnSet = new HashSet<>();
for (CrossSectionDTO section : sectionInfos) { // 运行时段列表
crossId = section.getCrossId();
CrossSchemeDTO scheme = schemeMap.get(section.getSchemeId()); // 方案信息
if (Objects.isNull(scheme.getPhaseInfos())) {
log.error("{}-路口, {}-方案,方案中相位信息不能为空", crossId, scheme.getId());
continue;
}
for (CrossPhaseDTO phase : scheme.getPhaseInfos()) { // 相位信息列表
if (Objects.isNull(phase.getLightsInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), phase.getId());
continue;
}
for (CrossLightsDTO lights : phase.getLightsInfos()) { // 灯组信息列表
dir = lights.getDir();
if (Objects.isNull(lights.getLaneInfos())) {
log.error("{}-路口, {}-方案,{}-灯组,灯组中车道信息不能为空", crossId, scheme.getId(), lights.getId());
continue;
}
for (LaneInfoDTO lane : lights.getLaneInfos()) {
// 车道转向转换为转向类型,并去重
turnArr = TurnConvertEnum.getCodeByKey(lane.getTurn()).split(Constants.SystemParam.SEPARATOR_UNDER_LINE);
turnSet.addAll(Arrays.stream(turnArr).collect(Collectors.toSet()));
}
for (String turn : turnSet) {
CrossPhaseDTO crossPhaseDTO = new CrossPhaseDTO();
BeanUtils.copyProperties(phase, crossPhaseDTO);
key = crossId + Constants.SystemParam.SEPARATOR_UNDER_LINE + dir + Constants.SystemParam.SEPARATOR_UNDER_LINE + turn;
crossPhaseDTO.setDirType(dir);
crossPhaseDTO.setTurnType(turn);
phaseMap.put(key, crossPhaseDTO);
}
turnSet.clear();
}
}
}
long end = System.currentTimeMillis();
log.info("加载路口当前运行的方案相位转向缓存耗时:{}ms,条数据,size={}", (end - start), phaseMap.size());
} catch (Exception e) {
log.error("相位方向转向缓存初始化失败", e);
}
}
/**
* 获取当前运行时段信息
*
* @param schedulesDTOList 调度列表
* @return
*/
private List<CrossSectionDTO> listSection(List<CrossSchedulesDTO> schedulesDTOList) {
List<CrossSectionDTO> sectionInfos = new ArrayList<>();
LocalTime currentTime = LocalTime.parse(DateUtil.getTime());
int week = DateUtil.getWeek(new Date()) == Constants.SystemParam.ZERO ? WeekEnum.SUNDAY.getCode() : DateUtil.getWeek(new Date()); // 获取当前星期
for (CrossSchedulesDTO schedules : schedulesDTOList) {
if (Objects.equals(schedules.getWeek(), Constants.SystemParam.ZERO) &&
!DateUtil.getDate().equals(schedules.getSpecialDate())) { // 先判断是否为特殊日期,如果特殊日期与当前日期不相同,则跳过
continue;
} else if (schedules.getWeek() != week) {
continue;
}
// 时段信息列表
if (Objects.nonNull(schedules.getSectionInfos())) {
for (CrossSectionDTO section : schedules.getSectionInfos()) {
LocalTime startTime = LocalTime.parse(section.getStartTime());
LocalTime endTime = LocalTime.parse(section.getEndTime());
// 过滤非当前时段数据
if (currentTime.isBefore(startTime) || currentTime.isAfter(endTime)) {
continue;
}
// 过滤控制模式为“非定周期”的数据
if (!ControlModeEnum.FIXED_PERIOD.getCode().equals(section.getControlMode())) {
continue;
}
section.setPlanNo(schedules.getPlanNo());
sectionInfos.add(section);
}
}
}
return sectionInfos;
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.common.framework.Constants;
import net.wanji.common.utils.tool.StringUtils;
import net.wanji.databus.dao.entity.RidInfoEntity;
import net.wanji.databus.dao.mapper.RidInfoMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author duanruiming
* @date 2023/03/07 13:44
*/
@Component
@Slf4j
public class CrossRidInfoCache implements CommandLineRunner {
@Resource
RidInfoMapper ridInfoMapper;
private static final List<RidInfoEntity> ridInfoList = new ArrayList<>();
/**
* 获取所有路口关联列表
* @return
*/
public List<RidInfoEntity> getRidInfoAllList() {
return ridInfoList;
}
/**
* 通过上下游路口编号获取关联路口列表
* @param startCrossId
* @param endCrossId
* @return
*/
public List<RidInfoEntity> getRidInfoListByCrossId(String startCrossId, String endCrossId) {
Stream<RidInfoEntity> stream = ridInfoList.stream();
if (StringUtils.isNotBlank(startCrossId)) {
stream = stream.filter(ridInfoEntity -> StringUtils.equalsIgnoreCase(startCrossId, ridInfoEntity.getStartCrossId()));
}
if (StringUtils.isNotBlank(endCrossId)) {
stream = stream.filter(ridInfoEntity -> StringUtils.equalsIgnoreCase(endCrossId, ridInfoEntity.getEndCrossId()));
}
return stream.collect(Collectors.toList());
}
/**
* 刷新缓存
*/
public void refresh() {
init();
}
@Override
public void run(String... args) throws Exception {
init();
}
public void init() {
List<RidInfoEntity> ridInfoEntities = ridInfoMapper.selectOne(Constants.SystemParam.NULL, Constants.SystemParam.NULL);
if (!CollectionUtils.isEmpty(ridInfoEntities)) {
ridInfoList.addAll(ridInfoEntities);
}
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.databus.dao.entity.GreenCrossDirDTO;
import net.wanji.databus.dao.mapper.GreenwaveCrossMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
/**
* @author duanruiming
* @date 2025/04/01 14:18
*/
@Component
@Slf4j
public class GreenCrossDirInfoCache implements CommandLineRunner {
@Resource
private GreenwaveCrossMapper greenwaveCrossMapper;
public static List<GreenCrossDirDTO> greenCrossDirCache = new ArrayList<>();
@Override
public void run(String... args) throws Exception {
try {
init();
} catch (Exception e) {
log.error("缓存加载失败:", e);
throw new RuntimeException(e);
}
}
public void init() {
try {
List<GreenCrossDirDTO> greenCrossDirDTOS = greenwaveCrossMapper.selectAllGreenCrossDir();
greenCrossDirCache.addAll(greenCrossDirDTOS);
} catch (Exception e) {
log.error("缓存加载失败:", e);
throw new RuntimeException(e);
}
}
}
package net.wanji.opt.cache;
import lombok.extern.slf4j.Slf4j;
import net.wanji.databus.dao.entity.GreenwaveInfoPO;
import net.wanji.databus.dao.mapper.GreenwaveInfoMapper;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author duanruiming
* @date 2024/11/29 11:19
*/
@Component
@Slf4j
public class GreenWaveInfoCache implements CommandLineRunner {
public static final Map<Integer, GreenwaveInfoPO> greenWaveMap = new HashMap<>();
@Resource
private GreenwaveInfoMapper greenwaveInfoMapper;
@Override
public void run(String... args) throws Exception {
try {
List<GreenwaveInfoPO> greenWaveInfoPOS = greenwaveInfoMapper.selectAll();
if (!CollectionUtils.isEmpty(greenWaveInfoPOS)) {
for (GreenwaveInfoPO greenWaveInfoPO : greenWaveInfoPOS) {
greenWaveMap.put(greenWaveInfoPO.getId(), greenWaveInfoPO);
}
}
} catch (Exception e) {
log.error("绿波信息初始化失败:", e);
throw new RuntimeException("绿波信息初始化失败!");
}
}
}
/**
*
*/
package net.wanji.opt.common;
/*
* @author 周士广
* @date 2019年5月29日
*/
import java.math.BigDecimal;
/**
* 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精 确的浮点数运算,包括加减乘除和四舍五入。
*/
public class ArithOfBigDecmial {
// 默认除法运算精度
private static final int DEF_DIV_SCALE = 10;
// 这个类不能实例化
private ArithOfBigDecmial() {
}
/**
* 提供精确的加法运算。
*
* @param v1 被加数
* @param v2 加数
* @return 两个参数的和
*/
public static double add(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
/**
* 提供精确的减法运算。
*
* @param v1 被减数
* @param v2 减数
* @return 两个参数的差
*/
public static double sub(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.subtract(b2).doubleValue();
}
/**
* 提供精确的乘法运算。
*
* @param v1 被乘数
* @param v2 乘数
* @return 两个参数的积
*/
public static double mul(double v1, double v2) {
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
/**
* 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @return 两个参数的商
*/
public static double div(double v1, double v2) {
return div(v1, v2, DEF_DIV_SCALE);
}
/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
/**
* 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
*
* @param v1 被除数
* @param v2 除数
* @param scale 表示表示需要精确到小数点以后几位。
* @return 两个参数的商
*/
public static double div(double v1, double v2, int scale,int roundingMode) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2, scale, roundingMode).doubleValue();
}
/**
* 提供精确的小数位四舍五入处理。
*
* @param v 需要四舍五入的数字
* @param scale 小数点后保留几位
* @return 四舍五入后的结果
*/
public static double round(double v, int scale) {
if (scale < 0) {
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b = new BigDecimal(Double.toString(v));
BigDecimal one = new BigDecimal("1");
return b.divide(one, scale, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
package net.wanji.opt.common;
import com.alibaba.fastjson.JSONArray;
import net.wanji.opt.constant.DirEnum;
import org.apache.commons.lang3.StringUtils;
/**
* @author duanruiming
* @date 2025/04/02 15:44
*/
public class CommonUtils {
/**
* 分隔符:下划线
**/
public static final String SEPARATOR_UNDER_LINE = "_";
public static String getStrJoin(String ... str) {
String join = String.join(SEPARATOR_UNDER_LINE, str);
return join;
}
/**
* 事件发生方向转换为描述
* @param dir
* @return
*/
public static String getEventHappenDirName(String dir){
StringBuilder stringBuilder = new StringBuilder();
if (StringUtils.isNotEmpty(dir)) {
JSONArray dirArr = JSONArray.parseArray(dir);
for (int i = 0; i < dirArr.size(); i++) {
String dirName = DirEnum.getName(dirArr.getString(i));
stringBuilder.append(",");
stringBuilder.append(dirName);
}
}
if (stringBuilder.length()>1){
return stringBuilder.substring(1);
}
return null;
}
}
package net.wanji.opt.common;
public class Constants {
/**
* 系统缩写
*/
public static final String SYSTEM_ABBR = "OPT";
/**
* 相位(灯态)锁定
*/
public static final Integer LOCK_RUNNING_CONTROL = 12;
/**
* 相位(灯态)解锁: 0-多时段控制模式
*/
public static final Integer UNLOCK_RUNNING_CONTROL = 13;
/**
* 恢复正常方案: 0-多时段控制模式
*/
public static final Integer NORMAL_RUNNING_CONTROL = 0;
/**
* 关灯
*/
public static final Integer CLOSE_LIGHT_CONTROL = 1;
/**
* 黄闪
*/
public static final Integer YELLOW_LIGHT_CONTROL = 2;
/**
* 手动全红
*/
public static final Integer ALL_RED_CONTROL = 3;
/**
* 定周期控制
*/
public static final Integer FIXED_CYCLE_MODE = 4;
/**
* 步进控制
*/
public static final Integer STEP_CONTROL = 10;
/**
* 取消步进
*/
public static final Integer CANCEL_STEP_CONTROL = 11;
/**
* 绿波优化查询缓存key
*/
public static final String GREEN_ID_OPT_KEY = "green_opt_";
public static final String GREEN_ID_OPT_CHART_KEY = "green_opt_chart_";
}
\ No newline at end of file
package net.wanji.opt.common;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.net.URLEncoder;
import java.util.List;
/**
* @author duanruiming
* @date 2024/06/13 9:35
*/
@Slf4j
public class ExcelExportUtils {
public static <T> void exportExcel(HttpServletResponse response, String startStr, String endStr,
List<T> dataList, String sheetName, Class<T> clazz) throws Exception {
// 文件名 sheet名称添加日期
String fileName = sheetName.concat(startStr).concat("~").concat(endStr)
.replaceAll(":", " ").replaceAll(" ", "-").concat(".xlsx"); // 注意文件扩展名应为.xlsx,因为你设置了xlsx的MIME类型
// 设置响应头
response.setCharacterEncoding("UTF-8");
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"));
// 使用EasyExcel进行导出
try (ServletOutputStream outputStream = response.getOutputStream()) {
// 解决服务器导出文件没数据问题 核心这个配置开始内存处理模式 inMemory少量数据
//ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz).inMemory(Boolean.TRUE).build();
ExcelWriter excelWriter = EasyExcel.write(outputStream, clazz).build();
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
// 填充数据
excelWriter.write(dataList, writeSheet);
// 刷新并关闭流,不需要重复关闭outputStream
excelWriter.finish();
} catch (Exception e) {
log.error("{}导出excel异常:", fileName, e);
throw new Exception("导出excel异常:" + e.getMessage(), e);
}
}
}
package net.wanji.opt.common;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import net.wanji.opt.dto.LineCongestion;
import net.wanji.opt.dto.PhaseEmptyResult;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.clients.consumer.OffsetAndTimestamp;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.*;
/**
* 从 Kafka 中按时间范围消费数据
*
* @author Kent HAN
* @date 2024/4/16 8:45
*/
@Slf4j
public class KafkaConsumerUtil {
private final KafkaConsumer<String, String> consumer;
private final ObjectMapper objectMapper;
public KafkaConsumerUtil(String bootstrapServers, String groupId) {
Properties props = new Properties();
props.put("bootstrap.servers", bootstrapServers);
props.put("group.id", groupId);
props.put("enable.auto.commit", "true");
props.put("auto.commit.interval.ms", "1000");
props.put("key.deserializer", StringDeserializer.class.getName());
props.put("value.deserializer", StringDeserializer.class.getName());
this.consumer = new KafkaConsumer<>(props);
this.objectMapper = new ObjectMapper();
}
public List<PhaseEmptyResult> consumeEmptyPhaseForTimeRange(
String topic, int partition, long startTime, long endTime) {
List<PhaseEmptyResult> results = new ArrayList<>();
TopicPartition topicPartition = new TopicPartition(topic, partition);
consumer.assign(Collections.singletonList(topicPartition));
HashMap<TopicPartition, Long> timestampToSearch = new HashMap<>();
timestampToSearch.put(topicPartition, startTime);
OffsetAndTimestamp offsetAndTimestamp = consumer.offsetsForTimes(timestampToSearch).get(topicPartition);
if (offsetAndTimestamp == null) {
return results;
}
long startOffset = offsetAndTimestamp.offset();
// 开始消费
consumer.seek(topicPartition, startOffset);
int emptyPollCount = 0; // 记录空轮询次数的计数器
int maxEmptyPolls = 10; // 设置最大空轮询次数
try {
boolean keepConsuming = true;
while (keepConsuming) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
if (records.isEmpty()) {
emptyPollCount++; // 如果没有记录,增加空轮询计数
if (emptyPollCount >= maxEmptyPolls) {
// 如果达到最大空轮询次数,退出循环
break;
}
} else {
emptyPollCount = 0; // 如果有记录,重置空轮询计数器
for (ConsumerRecord<String, String> record : records) {
long recordTime = record.timestamp();
if (recordTime >= startTime && recordTime <= endTime) {
PhaseEmptyResult phaseEmptyResult =
objectMapper.readValue(record.value(), PhaseEmptyResult.class);
results.add(phaseEmptyResult);
} else if (recordTime > endTime) {
keepConsuming = false;
break;
}
}
}
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
} finally {
consumer.close();
}
return results;
}
public List<LineCongestion> consumeLineMetricForTimeRange(
String topic, int partition, long startTime, long endTime) {
List<LineCongestion> results = new ArrayList<>();
TopicPartition topicPartition = new TopicPartition(topic, partition);
consumer.assign(Collections.singletonList(topicPartition));
HashMap<TopicPartition, Long> timestampToSearch = new HashMap<>();
timestampToSearch.put(topicPartition, startTime);
OffsetAndTimestamp offsetAndTimestamp = consumer.offsetsForTimes(timestampToSearch).get(topicPartition);
if (offsetAndTimestamp == null) {
return results;
}
long startOffset = offsetAndTimestamp.offset();
// 开始消费
consumer.seek(topicPartition, startOffset);
int emptyPollCount = 0; // 记录空轮询次数的计数器
int maxEmptyPolls = 10; // 设置最大空轮询次数
try {
boolean keepConsuming = true;
while (keepConsuming) {
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
if (records.isEmpty()) {
emptyPollCount++; // 如果没有记录,增加空轮询计数
if (emptyPollCount >= maxEmptyPolls) {
// 如果达到最大空轮询次数,退出循环
break;
}
} else {
emptyPollCount = 0; // 如果有记录,重置空轮询计数器
for (ConsumerRecord<String, String> record : records) {
long recordTime = record.timestamp();
if (recordTime >= startTime && recordTime <= endTime) {
LineCongestion lineCongestion = JSONObject.parseObject(record.value(), LineCongestion.class);
results.add(lineCongestion);
} else if (recordTime > endTime) {
keepConsuming = false;
break;
}
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
consumer.close();
}
log.info("从kafka中获取干线指标数据 topic:{},startTime:{},endTime:{}");
return results;
}
}
package net.wanji.opt.common;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@EnableAutoConfiguration
public class RedisConfig extends CachingConfigurerSupport {
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.password}")
private String password;
@Value("${spring.redis.database}")
private int database;
@Value("${spring.redis.timeout}")
private int timeout;
@Value("${spring.redis.jedis.pool.max-idle}")
private int maxIdle;
@Value("${spring.redis.jedis.pool.max-active}")
private int maxTotal;
@Value("${spring.redis.jedis.pool.min-idle}")
private int minIdle;
@Value("${spring.redis.jedis.pool.max-wait}")
private long maxWaitMillis;
/**
* 获取Jedis连接工厂
* <p>用于创建Jedis对象</p>
* @return JedisPool
*/
@Bean
public JedisPool redisPoolFactory() {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(maxTotal);
jedisPoolConfig.setMaxIdle(maxIdle);
jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);
jedisPoolConfig.setMinIdle(minIdle);
jedisPoolConfig.setTestOnBorrow(false);
jedisPoolConfig.setTestOnReturn(false);
return new JedisPool(jedisPoolConfig, host, port,timeout, password, database);
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 使用 Jackson 作为序列化器
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer(objectMapper);
template.setValueSerializer(serializer);
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
/**
* 选择redis作为默认缓存工具
* @param redisConnectionFactory
* @return
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
return RedisCacheManager
.builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(redisCacheConfiguration).build();
}
}
\ No newline at end of file
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/11 20:33
*/
@Getter
@AllArgsConstructor
public enum ConsgestionStatusEnum {
AMBLE_STATUS(2, "缓行"),
CONSGESTION_STATUS(3, "拥堵");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (ConsgestionStatusEnum value : ConsgestionStatusEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.getDesc();
}
}
return ConsgestionStatusEnum.AMBLE_STATUS.getDesc();
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.wanji.common.utils.tool.StringUtils;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/03 19:14
* @description
*/
@Getter
@AllArgsConstructor
public enum CrossDirEnum {
//
NORTH(1, "北"),
NORTH_EAST(2, "东北"),
EAST(3, "东"),
SOUTH_EAST(4, "东南"),
SOUTH(5, "南"),
SOUTH_WEST(6,"西南"),
WEST(7,"西"),
NORTH_WEST( 8,"西北");
private Integer dir;
private String dirName;
public static String getDesc(Integer dir) {
for (CrossDirEnum dirEnum : CrossDirEnum.values()) {
if (Objects.equals(dir, dirEnum.getDir())) {
return dirEnum.getDirName();
}
}
return "";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author duanruiming
* @date 2024/12/05 15:20
*/
@Getter
@AllArgsConstructor
public enum CrossOptResultStrategyEnum {
// 优化策略
ZERO(0, "均衡调控"),
ONE(1, "绿灯空放"),
TWO(2, "失衡"),
THREE(3, "溢出");
private int code;
private String desc;
public static String getDesc(int code) {
for (CrossOptResultStrategyEnum value : CrossOptResultStrategyEnum.values()) {
if (code == value.code) {
return value.getDesc();
}
}
return CrossOptResultStrategyEnum.ZERO.getDesc();
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/03 19:14
* @description
*/
@Getter
@AllArgsConstructor
public enum CrossTypeEnum {
//
T_INTERSECTION(1, "丁字口"),
CROSS_INTERSECTION(2, "十字口"),
ROUNDABOUT(3, "环岛"),
IRREGULAR_INTERSECTION(4, "畸形口"),
OVERPASS(5, "立体交叉口"),
RAILWAY_CROSSING(6, "铁路道口"),
OTHER(7, "其他");
private Integer code;
private String description;
public static String getDesc(Integer dir) {
for (CrossTypeEnum dirEnum : CrossTypeEnum.values()) {
if (Objects.equals(dir, dirEnum.getCode())) {
return dirEnum.getDescription();
}
}
return OTHER.description;
}
}
package net.wanji.opt.common.enums;
import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.wanji.common.utils.tool.StringUtils;
/**
* @author duanruiming
* @date 2025/01/07 20:24
*/
@Getter
@AllArgsConstructor
public enum EventInfoTypeEnum {
// 事件类型转化
CROSS_SMOOTH(0, "700","路口畅通"),
PHASE_EMPTY(5, "701","相位空放"),
CROSS_UNBALANCE(1, "702","路口失衡"),
CROSS_OVERFLOW(3, "703","路口溢出"),
// 当前没有
//CROSS_DEADLOCK(4, "704","路口死锁"),
GREEN_SLOW(6, "705","干线缓行"),
GREEN_CONGEST(7, "706","干线拥堵"),
CROSS_CONGEST(2, "707","路口拥堵"),
GREEN_SMOOTH(8, "708","干线畅通");
private Integer optType;
private String eventType;
private String desc;
/**
* 通过事件类型获取优化类型
* @param eventType
* @return
*/
public static Integer getOptType(String eventType) {
for (EventInfoTypeEnum value : EventInfoTypeEnum.values()) {
if (StringUtils.equalsIgnoreCase(eventType, value.getEventType())) {
return value.getOptType();
}
}
return null;
}
/**
* 过事件类型获取优化类型名称
* @param eventType
* @return
*/
public static String getOptDesc(String eventType) {
for (EventInfoTypeEnum value : EventInfoTypeEnum.values()) {
if (StringUtils.equalsIgnoreCase(eventType, value.getEventType())) {
return value.getDesc();
}
}
return null;
}
/**
* 通过optType获取枚举值
* @param optType
* @return
*/
public static EventInfoTypeEnum getEventInfoTypeEnum(int optType) {
for (EventInfoTypeEnum value : EventInfoTypeEnum.values()) {
if (ObjectUtil.equals(value.getOptType(),optType)){
return value;
}
}
return null;
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/31 11:58
*/
@Getter
@AllArgsConstructor
public enum EventStatusEnum {
// 0未处理 1分析中 2优化中 3优化完 4已结束
ZERO(0, "未处理"),
ONE(1, "分析中"),
TWO(2, "优化中"),
THREE(3, "优化完"),
// 信控没有已结束,优化完
FOUR(4, "优化完");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (EventStatusEnum value : EventStatusEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.getDesc();
}
}
// 畅通为未优化
return "未优化";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.wanji.common.utils.tool.StringUtils;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/03 19:14
* @description
*/
@Getter
@AllArgsConstructor
public enum GreenBeltDirEnum {
//
E2W("e2w", "东向西", 3),
W2E("w2e", "西向东", 7),
N2S("n2s", "北向南", 1),
S2N("s2n", "南向北", 5);
private String code;
private String desc;
private Integer inDir;
public static String getDesc(String code) {
for (GreenBeltDirEnum dirEnum : GreenBeltDirEnum.values()) {
if (StringUtils.equalsIgnoreCase(code, dirEnum.getCode())) {
return dirEnum.getDesc();
}
}
return "";
}
public static Integer getInDir(String code) {
for (GreenBeltDirEnum value : GreenBeltDirEnum.values()) {
if (StringUtils.equalsIgnoreCase(code, value.getCode())) {
return value.getInDir();
}
}
return 1;
}
public static String getInDirName(Integer dir) {
for (GreenBeltDirEnum value : GreenBeltDirEnum.values()) {
if (Objects.equals(dir, value.getInDir())) {
return value.getDesc();
}
}
return "";
}
public static String getCode(Integer dir) {
for (GreenBeltDirEnum value : GreenBeltDirEnum.values()) {
if (Objects.equals(dir, value.getInDir())) {
return value.getCode();
}
}
return "";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/10 17:27
*/
@Getter
@AllArgsConstructor
public enum GreenWaveInDirEnum {
E2W(3, "东向西"),
W2E(7, "西向东"),
N2S(1, "北向南"),
S2N(5, "南向北");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (GreenWaveInDirEnum value : GreenWaveInDirEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.desc;
}
}
return "";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/03 19:14
* @description
*/
@Getter
@AllArgsConstructor
public enum LaneTurnEnum {
LEFT_TURN(1, "左转"),
STRAIGHT(2, "直行"),
RIGHT_TURN(3, "右转"),
U_TURN(4, "掉头"),
STRAIGHT_LEFT(5, "直左"),
STRAIGHT_RIGHT(6, "直右"),
STRAIGHT_LEFT_RIGHT(7, "左直右"),
LEFT_RIGHT(8, "左右"),
LEFT_U_TURN(9, "左转掉头"),
STRAIGHT_U_TURN(10, "直行掉头"),
RIGHT_U_TURN(11, "右转掉头"),
LEFT_STRAIGHT_U_TURN(12, "左直掉头"),
STRAIGHT_RIGHT_U_TURN(13, "直右掉头"),
ALL_DIRECTIONS(14, "左直右掉头"),
LEFT_RIGHT_U_TURN(15, "左右掉头");
private Integer code;
private String description;
public static String getDesc(Integer dir) {
for (LaneTurnEnum dirEnum : LaneTurnEnum.values()) {
if (Objects.equals(dir, dirEnum.getCode())) {
return dirEnum.getDescription();
}
}
return STRAIGHT.description;
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/11 20:33
*/
@Getter
@AllArgsConstructor
public enum OptStatusEnum {
ZERO(0, "正常"),
ONE(1, "优化中");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (OptStatusEnum value : OptStatusEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.getDesc();
}
}
return OptStatusEnum.ZERO.getDesc();
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/10 17:27
*/
@Getter
@AllArgsConstructor
public enum PeakNameEnum {
MORNING_PEAK(1, "早高峰"),
EVENING_PEAK(2, "晚高峰"),
ALL_DAY(3, "全天");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (PeakNameEnum value : PeakNameEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.desc;
}
}
return "";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/10 17:27
*/
@Getter
@AllArgsConstructor
public enum RoadLevelEnum {
HIGHWAY(41000, "高速公路"),
NATIONAL_HIGHWAY(42000, "国道"),
URBAN_EXPRESSWAY(43000, "城市快速路"),
URBAN_MAIN_ROAD(44000, "城市主干道"),
URBAN_SECONDARY_ROAD(45000, "城市次干道"),
URBAN_ORDINARY_ROAD(47000, "城市普通道路"),
PROVINCIAL_ROAD(51000, "省道"),
COUNTY_ROAD(52000, "县道"),
TOWN_ROAD(53000, "乡道"),
RURAL_INTERNAL_ROAD(54000, "县乡村内部道路"),
SMALL_ROAD(49, "小路");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (RoadLevelEnum value : RoadLevelEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.desc;
}
}
return URBAN_SECONDARY_ROAD.desc;
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @author duanruiming
* @date 2024/12/05 19:29
*/
@Getter
@AllArgsConstructor
public enum StrategyControlEnum {
ZERO(0, "绿波带", "效率提升"),
ONE(1, "失衡", "均衡调控"),
TWO(2, "溢出", "效率提升"),
THREE(3, "空放", "效率提升"),
FOUR(4, "拥堵", "效率提升"),
FIVE(5, "畅通", "畅通");
private int code;
private String desc;
private String method;
public static String getDesc(int code) {
for (StrategyControlEnum value : StrategyControlEnum.values()) {
if (code == value.getCode()) {
return value.getDesc();
}
}
return StrategyControlEnum.FIVE.getDesc();
}
public static String getMethod(int code) {
for (StrategyControlEnum value : StrategyControlEnum.values()) {
if (code == value.getCode()) {
return value.getMethod();
}
}
return "";
}
}
package net.wanji.opt.common.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Objects;
/**
* @author duanruiming
* @date 2024/12/11 20:33
*/
@Getter
@AllArgsConstructor
public enum WeekEnum {
MON(1, "周一"),
TUE(2, "周二"),
WED(3, "周三"),
THU(4, "周四"),
FRI(5, "周五"),
SAT(6, "周六"),
SUN(7, "周日");
private Integer code;
private String desc;
public static String getDesc(Integer code) {
for (WeekEnum value : WeekEnum.values()) {
if (Objects.equals(code, value.getCode())) {
return value.getDesc();
}
}
return WeekEnum.MON.getDesc();
}
}
package net.wanji.opt.common.exception;
/**
* @author duanruiming
* @date 2023/03/03 13:47
*/
public class OptServiceException extends RuntimeException {
public OptServiceException(Exception e) {
super(e);
}
public OptServiceException(String message) {
super(message);
}
public OptServiceException(String message, Exception e) {
super(message, e);
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment