@@ -17,11 +17,10 @@ public class GeneratorCodeConfig { | |||||
private static final String PATH_YYD = "/Users/wendy/coding/java/car-manage/ningda-car-api/src/main/java"; | private static final String PATH_YYD = "/Users/wendy/coding/java/car-manage/ningda-car-api/src/main/java"; | ||||
private static final String PATH_LS = "/Users/qinxianyun/Documents/qin/ningdatech/secret-file-manage/ningda-api/src/main/java"; | private static final String PATH_LS = "/Users/qinxianyun/Documents/qin/ningdatech/secret-file-manage/ningda-api/src/main/java"; | ||||
private static final String PATH_ZPF = "D:\\ningda\\car-manage\\ningda-car-api\\src\\main\\java"; | private static final String PATH_ZPF = "D:\\ningda\\car-manage\\ningda-car-api\\src\\main\\java"; | ||||
private static final String PATH_CMM = "D:\\work\\shuiniche\\car-manage\\ningda-car-api\\src\\main\\java"; | |||||
//private static final String PATH_CMM = "D:\\work\\shuiniche\\car-manage\\ningda-car-api\\src\\main\\java"; | |||||
private static final String PATH_CMM = "D:\\work\\yw-road\\nd-yw-road\\ningda-yw-api\\src\\main\\java"; | |||||
private static final String URL = "jdbc:mysql://47.98.125.47:3306/nd_cement_truck_dev?" + | |||||
"useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&" + | |||||
"useSSL=false&serverTimezone=Asia/Shanghai"; | |||||
private static final String URL = "jdbc:mysql://47.98.125.47:3306/nd-yw-road?serverTimezone=Asia/Shanghai&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false"; | |||||
private static final String USER_NAME = "root"; | private static final String USER_NAME = "root"; | ||||
private static final String PASSWORD = "NingdaKeji123!"; | private static final String PASSWORD = "NingdaKeji123!"; | ||||
@@ -60,7 +59,7 @@ public class GeneratorCodeConfig { | |||||
public static void main(String[] args) { | public static void main(String[] args) { | ||||
//generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor"); | //generate("PoffyZhang", "car.monitor",PATH_ZPF, "nd_vehicle_security_monitor"); | ||||
//generate("WendyYang", "irs",PATH_YYD, "nd_drivers_license"); | //generate("WendyYang", "irs",PATH_YYD, "nd_drivers_license"); | ||||
generate("CMM", "test",PATH_CMM, "nd_vehicle_equipment_no"); | |||||
generate("CMM", "test",PATH_CMM, "nd_road_behavior_analysis"); | |||||
} | } | ||||
} | } |
@@ -0,0 +1,48 @@ | |||||
package com.ningdatech.carapi.analysis.entity; | |||||
import com.baomidou.mybatisplus.annotation.IdType; | |||||
import com.baomidou.mybatisplus.annotation.TableId; | |||||
import com.baomidou.mybatisplus.annotation.TableName; | |||||
import java.io.Serializable; | |||||
import java.time.LocalDateTime; | |||||
import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | |||||
/** | |||||
* <p> | |||||
* | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-22 | |||||
*/ | |||||
@TableName("nd_road_behavior_analysis") | |||||
@ApiModel(value = "RoadBehaviorAnalysis对象", description = "") | |||||
@Data | |||||
public class RoadBehaviorAnalysis implements Serializable { | |||||
private static final long serialVersionUID = 1L; | |||||
@ApiModelProperty("主键") | |||||
@TableId(value = "id", type = IdType.AUTO) | |||||
private Long id; | |||||
@ApiModelProperty("异常行为类型 1 驾驶员异常 2 车辆速度异常 3 监控异常") | |||||
private Integer type; | |||||
@ApiModelProperty("异常行为描述") | |||||
private String behavior; | |||||
@ApiModelProperty("异常行为码") | |||||
private String behaviorCode; | |||||
@ApiModelProperty("异常行为时间") | |||||
private LocalDateTime behaviorTime; | |||||
@ApiModelProperty("创建时间") | |||||
private LocalDateTime createTime; | |||||
@ApiModelProperty("更新时间") | |||||
private LocalDateTime updateTime; | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package com.ningdatech.carapi.analysis.mapper; | |||||
import com.ningdatech.carapi.analysis.entity.RoadBehaviorAnalysis; | |||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||||
/** | |||||
* <p> | |||||
* Mapper 接口 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-22 | |||||
*/ | |||||
public interface RoadBehaviorAnalysisMapper extends BaseMapper<RoadBehaviorAnalysis> { | |||||
} |
@@ -0,0 +1,5 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
<mapper namespace="com.ningdatech.carapi.analysis.mapper.RoadBehaviorAnalysisMapper"> | |||||
</mapper> |
@@ -0,0 +1,16 @@ | |||||
package com.ningdatech.carapi.analysis.service; | |||||
import com.ningdatech.carapi.analysis.entity.RoadBehaviorAnalysis; | |||||
import com.baomidou.mybatisplus.extension.service.IService; | |||||
/** | |||||
* <p> | |||||
* 服务类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-22 | |||||
*/ | |||||
public interface IRoadBehaviorAnalysisService extends IService<RoadBehaviorAnalysis> { | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.ningdatech.carapi.analysis.service.impl; | |||||
import com.ningdatech.carapi.analysis.entity.RoadBehaviorAnalysis; | |||||
import com.ningdatech.carapi.analysis.mapper.RoadBehaviorAnalysisMapper; | |||||
import com.ningdatech.carapi.analysis.service.IRoadBehaviorAnalysisService; | |||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||||
import org.springframework.stereotype.Service; | |||||
/** | |||||
* <p> | |||||
* 服务实现类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-22 | |||||
*/ | |||||
@Service | |||||
public class RoadBehaviorAnalysisServiceImpl extends ServiceImpl<RoadBehaviorAnalysisMapper, RoadBehaviorAnalysis> implements IRoadBehaviorAnalysisService { | |||||
} |
@@ -306,7 +306,13 @@ public class PositionMonitorManage { | |||||
} | } | ||||
public List<ResRealTimeMonitorVehicleGisListVO> realTimeMonitorVehicleGisList(ReqRealTimeMonitorPO po) { | public List<ResRealTimeMonitorVehicleGisListVO> realTimeMonitorVehicleGisList(ReqRealTimeMonitorPO po) { | ||||
List<NdDataAccessGps> data = dataAccessGpsService.list(); | |||||
// 只查询当天最新的数据 | |||||
LocalDateTime startTime = LocalDateTime.now().withHour(0).withMinute(0).withSecond(0); | |||||
LocalDateTime endTime = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59); | |||||
List<NdDataAccessGps> data = dataAccessGpsService.list(Wrappers.lambdaQuery(NdDataAccessGps.class) | |||||
.ge(NdDataAccessGps::getUpdateTime, startTime).le(NdDataAccessGps::getUpdateTime, endTime) | |||||
.select(NdDataAccessGps::getCarLongitude, NdDataAccessGps::getCarLatitude, NdDataAccessGps::getCarPlate, | |||||
NdDataAccessGps::getCarVelocity, NdDataAccessGps::getCarDirection)); | |||||
if(CollUtil.isEmpty(data)){ | if(CollUtil.isEmpty(data)){ | ||||
return Collections.emptyList(); | return Collections.emptyList(); | ||||
} | } | ||||
@@ -35,7 +35,9 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | |||||
"com.ningdatech.carapi.qrcode.controller", | "com.ningdatech.carapi.qrcode.controller", | ||||
"com.ningdatech.carapi.open.controller", | "com.ningdatech.carapi.open.controller", | ||||
"com.ningdatech.carapi.industry.controller", | "com.ningdatech.carapi.industry.controller", | ||||
"com.ningdatech.carapi.homepage.controller" | |||||
"com.ningdatech.carapi.homepage.controller", | |||||
"com.ningdatech.carapi.radar.controller", | |||||
"com.ningdatech.carapi.gps.controller" | |||||
}) | }) | ||||
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> { | public class GlobalResponseHandler implements ResponseBodyAdvice<Object> { | ||||
@@ -0,0 +1,57 @@ | |||||
package com.ningdatech.carapi.gps.controller; | |||||
import com.ningdatech.carapi.gps.manage.GpsDataPullManage; | |||||
import com.ningdatech.carapi.gps.model.GpsDataVO; | |||||
import org.springframework.validation.annotation.Validated; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.PostMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import io.swagger.annotations.ApiOperation; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import java.util.List; | |||||
/** | |||||
* <p> | |||||
* 前端控制器 | |||||
* </p> | |||||
* | |||||
* @author PoffyZhang | |||||
* @since 2022-10-09 | |||||
*/ | |||||
@Slf4j | |||||
@Validated | |||||
@RestController | |||||
@RequestMapping("/gps") | |||||
@RequiredArgsConstructor | |||||
public class GpsDataPullController { | |||||
private final GpsDataPullManage gpsDataPullManage; | |||||
@ApiOperation(value = "GPS数据拉取测试", notes = "GPS数据拉取测试") | |||||
@GetMapping("/get-gps-data") | |||||
public String getGpsData() { | |||||
return gpsDataPullManage.getGpsData(); | |||||
} | |||||
@ApiOperation(value = "GPS数据增量拉取测试", notes = "GPS数据增量拉取测试") | |||||
@GetMapping("/get-add-gps-data") | |||||
public String getAddGpsData() { | |||||
return gpsDataPullManage.getAddGpsData(); | |||||
} | |||||
@ApiOperation(value = "GPS数据增量拉取缓存ID设置", notes = "GPS数据增量拉取缓存ID设置") | |||||
@GetMapping("/update-redis-id") | |||||
public String updateRedisId() { | |||||
return gpsDataPullManage.updateRedisId(); | |||||
} | |||||
@ApiOperation(value = "数据驾驶舱算法分析数据获取", notes = "数据驾驶舱算法分析数据获取") | |||||
@GetMapping("/get-redis-data") | |||||
public String getRedisData() { | |||||
return gpsDataPullManage.getRedisData(); | |||||
} | |||||
} |
@@ -0,0 +1,647 @@ | |||||
package com.ningdatech.carapi.gps.manage; | |||||
import java.io.BufferedReader; | |||||
import java.io.InputStreamReader; | |||||
import java.io.OutputStream; | |||||
import java.math.BigDecimal; | |||||
import java.net.HttpURLConnection; | |||||
import java.net.URL; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.security.MessageDigest; | |||||
import java.security.NoSuchAlgorithmException; | |||||
import java.security.cert.X509Certificate; | |||||
import java.sql.Connection; | |||||
import java.sql.DriverManager; | |||||
import java.sql.Statement; | |||||
import java.time.Instant; | |||||
import java.time.LocalDateTime; | |||||
import java.time.ZoneId; | |||||
import java.time.format.DateTimeFormatter; | |||||
import java.util.*; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.stream.Collectors; | |||||
import javax.net.ssl.HttpsURLConnection; | |||||
import javax.net.ssl.SSLContext; | |||||
import javax.net.ssl.TrustManager; | |||||
import javax.net.ssl.X509TrustManager; | |||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||||
import com.ningdatech.cache.model.cache.CacheKey; | |||||
import com.ningdatech.cache.repository.CachePlusOps; | |||||
import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps; | |||||
import com.ningdatech.carapi.scheduler.contants.TaskContant; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.stereotype.Component; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.ningdatech.carapi.gps.model.entity.DataAccessGpsRealTimeData; | |||||
import com.ningdatech.carapi.gps.service.IDataAccessGpsRealTimeDataService; | |||||
import com.ningdatech.carapi.homepage.service.IDataAccessGpsService; | |||||
import cn.hutool.core.collection.CollUtil; | |||||
import cn.hutool.core.collection.ListUtil; | |||||
import cn.hutool.core.date.StopWatch; | |||||
import cn.hutool.json.JSONObject; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* 车辆位置信息 | |||||
* | |||||
* @author CMM | |||||
* @since 2023/11/17 09:35 | |||||
*/ | |||||
@Slf4j | |||||
@Component | |||||
@RequiredArgsConstructor | |||||
public class GpsDataPullManage { | |||||
private final IDataAccessGpsService dataAccessGpsService; | |||||
private final IDataAccessGpsRealTimeDataService dataAccessGpsRealTimeDataService; | |||||
private final CachePlusOps cachePlusOps; | |||||
@Value("${spring.datasource.url}") | |||||
private String dataBaseUrl; | |||||
@Value("${spring.datasource.username}") | |||||
private String username; | |||||
@Value("${spring.datasource.password}") | |||||
private String password; | |||||
public String getGpsData() { | |||||
StopWatch stopWatch = new StopWatch(); | |||||
stopWatch.start(); | |||||
String url = "https://jtjdhcweb.z1l1.com:7443"; | |||||
// 获取当前表中的最大更新时间 | |||||
long maxUpdateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); | |||||
Optional<DataAccessGpsRealTimeData> max = dataAccessGpsRealTimeDataService.list(Wrappers.lambdaQuery(DataAccessGpsRealTimeData.class) | |||||
.select(DataAccessGpsRealTimeData::getUpdateTime)).stream().max(Comparator.comparing(DataAccessGpsRealTimeData::getUpdateTime)); | |||||
if (max.isPresent()){ | |||||
maxUpdateTime = max.get().getUpdateTime().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); | |||||
} | |||||
log.info("当前表中最大更新时间:{}", Instant.ofEpochMilli(maxUpdateTime).atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); | |||||
try { | |||||
trustAllCertificates(); | |||||
Map<String, String> requestParams = assemblyParams(maxUpdateTime); | |||||
String requestUrl = url + "/talent/gps/datalist"; | |||||
String response = sendPostRequest(requestUrl, requestParams); | |||||
if (response == null) { | |||||
return "下载路径请求失败!"; | |||||
} | |||||
com.alibaba.fastjson.JSONObject object = com.alibaba.fastjson.JSONObject.parseObject(response); | |||||
System.out.println("Response from server: " + response); | |||||
String downloadUrl = object.getString("data"); | |||||
if (downloadUrl == null){ | |||||
return "下载路径请求失败!"; | |||||
} | |||||
String downloadPath = url + downloadUrl; | |||||
String result = sendDownloadRequest(downloadPath); | |||||
if (result == null){ | |||||
return "文件下载失败!"; | |||||
} | |||||
List<DataAccessGpsRealTimeData> dataList = JSON.parseArray(result, com.alibaba.fastjson.JSONObject.class).stream().map(jsonObject -> { | |||||
String vehicleNo = jsonObject.getString("vehicle_no"); | |||||
Integer color = jsonObject.getInteger("color"); | |||||
// 纬度 | |||||
String longitude = jsonObject.getString("lon"); | |||||
// 经度 | |||||
String latitude = jsonObject.getString("lat"); | |||||
String direction = jsonObject.getString("direction"); | |||||
String gpsSpeed = jsonObject.getString("gps_speed"); | |||||
Long gpsTime = jsonObject.getLong("gps_time"); | |||||
DataAccessGpsRealTimeData dataAccessGpsRealTimeData = new DataAccessGpsRealTimeData(); | |||||
dataAccessGpsRealTimeData.setCarPlate(vehicleNo); | |||||
dataAccessGpsRealTimeData.setPlateColor(color.toString()); | |||||
dataAccessGpsRealTimeData.setCarLongitude(latitude); | |||||
dataAccessGpsRealTimeData.setCarLatitude(longitude); | |||||
dataAccessGpsRealTimeData.setCarDirection(direction); | |||||
dataAccessGpsRealTimeData.setCarVelocity(gpsSpeed); | |||||
// 将毫秒时间戳转换为Instant对象 | |||||
Instant instant = Instant.ofEpochMilli(gpsTime); | |||||
// 使用北京时间时区转换为LocalDateTime | |||||
LocalDateTime localDateTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime(); | |||||
dataAccessGpsRealTimeData.setUpdateTime(localDateTime); | |||||
dataAccessGpsRealTimeData.setCreateTime(LocalDateTime.now()); | |||||
return dataAccessGpsRealTimeData; | |||||
}).collect(Collectors.toList()); | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
return "数据为空!"; | |||||
} | |||||
// 获取此次拉取到的数据中上传时间大于上次拉取数据的最大上报时间的数据 | |||||
long finalMaxUpdateTime = maxUpdateTime; | |||||
dataList = dataList.stream().filter(e -> e.getUpdateTime().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli() > finalMaxUpdateTime).collect(Collectors.toList()); | |||||
// 如果没有 说明没有新上报的数据 不更新 | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
log.info("没有新上报的数据!"); | |||||
return "没有新上报的数据!"; | |||||
} | |||||
log.info("本次拉取到{}条数据", dataList.size()); | |||||
// 将数据库中的数据根据更新时间排序 按更新时间 从小到大 根据对应id删除此次拉取到的数据量 | |||||
List<DataAccessGpsRealTimeData> dataAccessGpsRealTimeDataList = dataAccessGpsRealTimeDataService.list(Wrappers.lambdaQuery(DataAccessGpsRealTimeData.class) | |||||
.select(DataAccessGpsRealTimeData::getUpdateTime, DataAccessGpsRealTimeData::getRecordId)) | |||||
.stream().sorted(Comparator.comparing(DataAccessGpsRealTimeData::getUpdateTime)).collect(Collectors.toList()); | |||||
// 截取dataList.size()条数据 | |||||
dataAccessGpsRealTimeDataList = dataAccessGpsRealTimeDataList.subList(0, dataList.size()); | |||||
// 从旧数据中删除这些数据 | |||||
List<Long> recordIdList = dataAccessGpsRealTimeDataList.stream().map(DataAccessGpsRealTimeData::getRecordId).collect(Collectors.toList()); | |||||
String tableName = "nd_data_access_gps_real_time_data"; | |||||
dataAccessGpsRealTimeDataService.removeByRecordIds(tableName,recordIdList); | |||||
// 先清空表中的数据 | |||||
//truncateTable(); | |||||
// 设置表的自增序号从1开始 | |||||
//resetAutoIncrement(); | |||||
// 批量保存数据 每批保存1万条 | |||||
// 分批 | |||||
List<List<DataAccessGpsRealTimeData>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(dataList), 10000)); | |||||
// 创建线程池 线程数是 分批的数量 | |||||
ExecutorService executorService = Executors.newFixedThreadPool(batchList.size()); | |||||
// 线程计数器,就是 分批的数 | |||||
CountDownLatch countDownLatch = new CountDownLatch(batchList.size()); | |||||
batchList.forEach(e -> { | |||||
// 每个分批用一个线程执行 | |||||
executorService.execute(() -> dataAccessGpsRealTimeDataService.saveBatch(e, 10000)); | |||||
countDownLatch.countDown(); | |||||
}); | |||||
// 等待所有线程执行完成 | |||||
countDownLatch.await(); | |||||
executorService.shutdown(); | |||||
executorService.awaitTermination(5, TimeUnit.MINUTES); | |||||
if (executorService.isTerminated()) { | |||||
log.info("子线程执行完毕"); | |||||
log.info("主线程开始"); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "拉取完成!"; | |||||
} catch (Exception e) { | |||||
log.error("Error sending POST request: {}", e.getMessage()); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "数据拉取失败!"; | |||||
} | |||||
private void truncateTable() { | |||||
// SQL 语句清空表 | |||||
String truncateTableSQL = "TRUNCATE TABLE nd_data_access_gps_real_time_data"; | |||||
Connection conn = null; | |||||
Statement stmt = null; | |||||
try { | |||||
// 加载数据库驱动 | |||||
Class.forName("com.mysql.cj.jdbc.Driver"); | |||||
// 建立连接 | |||||
conn = DriverManager.getConnection(dataBaseUrl, username, password); | |||||
// 创建 Statement 对象 | |||||
stmt = conn.createStatement(); | |||||
// 执行 SQL 语句 | |||||
stmt.executeUpdate(truncateTableSQL); | |||||
log.info("表已被清空,自增主键已重置。"); | |||||
System.out.println("表已被清空,自增主键已重置。"); | |||||
} catch (Exception e) { | |||||
log.info("Error truncating table: {}", e.getMessage()); | |||||
} finally { | |||||
// 关闭资源 | |||||
try { | |||||
if (stmt != null) { | |||||
stmt.close(); | |||||
} | |||||
if (conn != null) { | |||||
conn.close(); | |||||
} | |||||
} catch (Exception e) { | |||||
log.error("Error closing resources: {}", e.getMessage()); | |||||
} | |||||
} | |||||
} | |||||
private void resetAutoIncrement() { | |||||
// SQL 语句重置自增主键 | |||||
String resetAutoIncrementSQL = "ALTER TABLE nd_data_access_gps_real_time_data AUTO_INCREMENT = 1"; | |||||
Connection conn = null; | |||||
Statement stmt = null; | |||||
try { | |||||
// 加载数据库驱动 | |||||
Class.forName("com.mysql.cj.jdbc.Driver"); | |||||
// 建立连接 | |||||
conn = DriverManager.getConnection(dataBaseUrl, username, password); | |||||
// 创建 Statement 对象 | |||||
stmt = conn.createStatement(); | |||||
// 执行 SQL 语句 | |||||
stmt.executeUpdate(resetAutoIncrementSQL); | |||||
log.info("自增主键已重置为1。"); | |||||
} catch (Exception e) { | |||||
log.error("重置自增主键失败。{}", e.getMessage()); | |||||
} finally { | |||||
// 关闭资源 | |||||
try { | |||||
if (stmt != null) { | |||||
stmt.close(); | |||||
} | |||||
if (conn != null) { | |||||
conn.close(); | |||||
} | |||||
} catch (Exception e) { | |||||
log.error("关闭资源失败。{}", e.getMessage()); | |||||
} | |||||
} | |||||
} | |||||
public String sendDownloadRequest(String downloadPath) { | |||||
try { | |||||
// 创建URL对象 | |||||
URL url = new URL(downloadPath); | |||||
// 打开连接 | |||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | |||||
// 设置请求方法为GET | |||||
connection.setRequestMethod("GET"); | |||||
// 设置连接超时时间(单位:毫秒) | |||||
connection.setConnectTimeout(60000); | |||||
// 设置读取超时时间(单位:毫秒) | |||||
connection.setReadTimeout(60000); | |||||
// 获取响应码 | |||||
int responseCode = connection.getResponseCode(); | |||||
System.out.println("GET Response Code :: " + responseCode); | |||||
if (responseCode == HttpURLConnection.HTTP_OK) { | |||||
// 使用BufferedReader读取响应内容 | |||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); | |||||
String inputLine; | |||||
StringBuilder response = new StringBuilder(); | |||||
while ((inputLine = in.readLine()) != null) { | |||||
response.append(inputLine); | |||||
} | |||||
// 打印响应内容 | |||||
System.out.println("Response :: " + response); | |||||
in.close(); | |||||
connection.disconnect(); | |||||
return response.toString(); | |||||
} else { | |||||
System.out.println("GET请求未成功"); | |||||
} | |||||
} catch (Exception e) { | |||||
log.error("Error sending GET request: {}", e.getMessage()); | |||||
} | |||||
return null; | |||||
} | |||||
public Map<String, String> assemblyParams(){ | |||||
String key = "Gps@!@163."; | |||||
long timeNow = System.currentTimeMillis() / 1000; | |||||
String token = generateMD5Token(timeNow, key); | |||||
Map<String, String> params = new HashMap<>(); | |||||
params.put("timeNow", String.valueOf(timeNow)); | |||||
params.put("token", token); | |||||
//params.put("Slat", "120.0167"); | |||||
//params.put("Elat", "120.2337"); | |||||
// | |||||
//params.put("Slon", "29.1078"); | |||||
//params.put("Elon", "29.4697"); | |||||
return params; | |||||
} | |||||
public Map<String, String> assemblyParams(Long maxUpdateTime){ | |||||
String key = "Gps@!@163."; | |||||
long timeNow = System.currentTimeMillis() / 1000; | |||||
String token = generateMD5Token(timeNow, key); | |||||
Map<String, String> params = new HashMap<>(); | |||||
params.put("timeNow", String.valueOf(timeNow)); | |||||
params.put("token", token); | |||||
params.put("timeLine", String.valueOf(maxUpdateTime)); | |||||
//params.put("Slat", "120.0167"); | |||||
//params.put("Elat", "120.2337"); | |||||
// | |||||
//params.put("Slon", "29.1078"); | |||||
//params.put("Elon", "29.4697"); | |||||
return params; | |||||
} | |||||
public Map<String, String> assemblyParams(String id){ | |||||
String key = "Gps@!@163."; | |||||
long timeNow = System.currentTimeMillis() / 1000; | |||||
String token = generateMD5Token(timeNow, key); | |||||
Map<String, String> params = new HashMap<>(); | |||||
params.put("timeNow", String.valueOf(timeNow)); | |||||
params.put("token", token); | |||||
params.put("id", id); | |||||
//params.put("Slat", "120.0167"); | |||||
//params.put("Elat", "120.2337"); | |||||
// | |||||
//params.put("Slon", "29.1078"); | |||||
//params.put("Elon", "29.4697"); | |||||
return params; | |||||
} | |||||
public String generateMD5Token(long timeNow, String key) { | |||||
try { | |||||
String data = key + timeNow; | |||||
MessageDigest md = MessageDigest.getInstance("MD5"); | |||||
byte[] hash = md.digest(data.getBytes(StandardCharsets.UTF_8)); | |||||
StringBuilder hexString = new StringBuilder(); | |||||
for (byte b : hash) { | |||||
String hex = Integer.toHexString(0xff & b); | |||||
if (hex.length() == 1) { | |||||
hexString.append('0'); | |||||
} | |||||
hexString.append(hex); | |||||
} | |||||
return hexString.toString(); | |||||
} catch (NoSuchAlgorithmException e) { | |||||
throw new RuntimeException("MD5 algorithm not found", e); | |||||
} | |||||
} | |||||
public String sendPostRequest(String requestUrl, Map<String, String> params) throws Exception { | |||||
URL url = new URL(requestUrl); | |||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection(); | |||||
// 设置请求方法为POST | |||||
connection.setRequestMethod("POST"); | |||||
// 设置请求头信息 | |||||
connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); | |||||
connection.setDoOutput(true); | |||||
// 构建请求参数 | |||||
StringBuilder postData = new StringBuilder(); | |||||
for (Map.Entry<String, String> param : params.entrySet()) { | |||||
if (postData.length() != 0) postData.append('&'); | |||||
postData.append(param.getKey()); | |||||
postData.append('='); | |||||
postData.append(param.getValue()); | |||||
} | |||||
System.out.println("Post Data: " + postData); | |||||
// 发送请求参数 | |||||
try (OutputStream outputStream = connection.getOutputStream()) { | |||||
byte[] postDataBytes = postData.toString().getBytes(StandardCharsets.UTF_8); | |||||
outputStream.write(postDataBytes); | |||||
} | |||||
// 获取响应 | |||||
int responseCode = connection.getResponseCode(); | |||||
if (responseCode == HttpURLConnection.HTTP_OK) { | |||||
// 读取响应 | |||||
BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream())); | |||||
StringBuilder response = new StringBuilder(); | |||||
String inputLine; | |||||
while ((inputLine = in.readLine()) != null) { | |||||
response.append(inputLine); | |||||
} | |||||
in.close(); | |||||
connection.disconnect(); | |||||
// 尝试将响应转换成JSONObject | |||||
try { | |||||
return new JSONObject(response.toString()).toString(); | |||||
} catch (Exception e) { | |||||
System.err.println("Error parsing JSON: " + e.getMessage()); | |||||
return null; | |||||
} | |||||
} else { | |||||
// 读取错误信息 | |||||
try (java.io.InputStream inputStream = connection.getErrorStream()) { | |||||
java.util.Scanner scanner = new java.util.Scanner(inputStream, StandardCharsets.UTF_8.name()); | |||||
scanner.useDelimiter("\\A"); | |||||
return scanner.hasNext() ? scanner.next() : ""; | |||||
} | |||||
} | |||||
} | |||||
public void trustAllCertificates() throws Exception { | |||||
// 创建一个信任所有证书的信任管理器 | |||||
TrustManager[] trustAllCerts = new TrustManager[]{ | |||||
new X509TrustManager() { | |||||
public X509Certificate[] getAcceptedIssuers() { | |||||
return null; | |||||
} | |||||
public void checkClientTrusted(X509Certificate[] certs, String authType) { | |||||
} | |||||
public void checkServerTrusted(X509Certificate[] certs, String authType) { | |||||
} | |||||
} | |||||
}; | |||||
// 安装信任所有证书的信任管理器 | |||||
SSLContext sc = SSLContext.getInstance("SSL"); | |||||
sc.init(null, trustAllCerts, new java.security.SecureRandom()); | |||||
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory()); | |||||
// 创建一个信任所有主机名的验证器 | |||||
HttpsURLConnection.setDefaultHostnameVerifier((hostname, session) -> true); | |||||
} | |||||
public String getAddGpsData() { | |||||
log.info("=========== GPS增量数据拉取 ======== 任务开始"); | |||||
StopWatch stopWatch = new StopWatch(); | |||||
stopWatch.start(); | |||||
String url = "https://jtjdhcweb.z1l1.com:7443"; | |||||
try { | |||||
trustAllCertificates(); | |||||
String requestUrl = url + "/talent/gps/datalist"; | |||||
// 从缓存中获取业务数据最大id | |||||
CacheKey key = new CacheKey(TaskContant.RedisKey.GPS_DATA_PULL_MAX_BIZ_ID); | |||||
String id = cachePlusOps.get(key); | |||||
log.info("缓存中已存在id:{}", id); | |||||
// 如果不存在 从表中数据中获取 | |||||
if (StringUtils.isBlank(id)){ | |||||
Long maxBizId = dataAccessGpsService.getMaxBizId(); | |||||
if (maxBizId != null) { | |||||
id = maxBizId.toString(); | |||||
}else { | |||||
return "未获取到id!"; | |||||
} | |||||
} | |||||
Map<String, String> requestAddParams = assemblyParams(id); | |||||
String addResponse = sendPostRequest(requestUrl, requestAddParams); | |||||
if (addResponse == null) { | |||||
return "下载路径请求失败!"; | |||||
} | |||||
com.alibaba.fastjson.JSONObject addObject = com.alibaba.fastjson.JSONObject.parseObject(addResponse); | |||||
System.out.println("Response from server: " + addResponse); | |||||
String addDownloadUrl = addObject.getString("data"); | |||||
if (addDownloadUrl == null){ | |||||
return "下载路径请求失败!"; | |||||
} | |||||
String addDownloadPath = url + addDownloadUrl; | |||||
String addResult = sendDownloadRequest(addDownloadPath); | |||||
if (addResult == null){ | |||||
return "文件下载失败!"; | |||||
} | |||||
List<NdDataAccessGps> addDataList = getNdDataAccessGps(addResult); | |||||
if (CollUtil.isEmpty(addDataList)){ | |||||
return "数据为空!"; | |||||
} | |||||
log.info("此次拉取到的数据条数:{}",addDataList.size()); | |||||
// 获取业务数据最大id | |||||
String maxBizId = addDataList.stream().map(d -> { | |||||
String bizId = d.getBizId(); | |||||
return Long.parseLong(bizId); | |||||
}).collect(Collectors.toList()).stream().max(Long::compare).get().toString(); | |||||
// 保存到缓存中 | |||||
cachePlusOps.set(key,maxBizId,false); | |||||
String value = cachePlusOps.get(key); | |||||
System.out.println("已存入缓存中的id: " + value); | |||||
// 批量保存数据 每批保存1万条 | |||||
// 分批 | |||||
List<List<NdDataAccessGps>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(addDataList), 10000)); | |||||
// 创建线程池 线程数是 分批的数量 | |||||
ExecutorService executorService = Executors.newFixedThreadPool(batchList.size()); | |||||
// 线程计数器,就是 分批的数 | |||||
CountDownLatch countDownLatch = new CountDownLatch(batchList.size()); | |||||
batchList.forEach(e -> { | |||||
// 每个分批用一个线程执行 | |||||
executorService.execute(() -> dataAccessGpsService.saveBatch(e, 10000)); | |||||
countDownLatch.countDown(); | |||||
}); | |||||
// 等待所有线程执行完成 | |||||
countDownLatch.await(); | |||||
executorService.shutdown(); | |||||
executorService.awaitTermination(5, TimeUnit.MINUTES); | |||||
if (executorService.isTerminated()) { | |||||
log.info("子线程执行完毕"); | |||||
log.info("主线程开始"); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "拉取完成!"; | |||||
} catch (Exception e) { | |||||
log.error("Error sending POST request: {}", e.getMessage()); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "数据拉取失败!"; | |||||
} | |||||
public List<NdDataAccessGps> getNdDataAccessGps(String result) { | |||||
List<NdDataAccessGps> dataList = JSON.parseArray(result, com.alibaba.fastjson.JSONObject.class).stream().map(jsonObject -> { | |||||
String vehicleNo = jsonObject.getString("vehicle_no"); | |||||
Integer color = jsonObject.getInteger("color"); | |||||
// 纬度 | |||||
String longitude = jsonObject.getString("lon"); | |||||
// 经度 | |||||
String latitude = jsonObject.getString("lat"); | |||||
String direction = jsonObject.getString("direction"); | |||||
String gpsSpeed = jsonObject.getString("gps_speed"); | |||||
Long gpsTime = jsonObject.getLong("gps_time"); | |||||
String bizId = jsonObject.getString("id"); | |||||
NdDataAccessGps dataAccessGps = new NdDataAccessGps(); | |||||
dataAccessGps.setCarPlate(vehicleNo); | |||||
dataAccessGps.setPlateColor(color.toString()); | |||||
dataAccessGps.setCarLongitude(BigDecimal.valueOf(Double.parseDouble(latitude))); | |||||
dataAccessGps.setCarLatitude(BigDecimal.valueOf(Double.parseDouble(longitude))); | |||||
dataAccessGps.setCarDirection(BigDecimal.valueOf(Double.parseDouble(direction))); | |||||
dataAccessGps.setCarVelocity(BigDecimal.valueOf(Double.parseDouble(gpsSpeed))); | |||||
// 将毫秒时间戳转换为Instant对象 | |||||
Instant instant = Instant.ofEpochMilli(gpsTime); | |||||
// 使用北京时间时区转换为LocalDateTime | |||||
LocalDateTime localDateTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime(); | |||||
dataAccessGps.setUpdateTime(localDateTime); | |||||
dataAccessGps.setCreateTime(LocalDateTime.now()); | |||||
dataAccessGps.setBizId(bizId); | |||||
return dataAccessGps; | |||||
}).collect(Collectors.toList()); | |||||
return dataList; | |||||
} | |||||
public String updateRedisId() { | |||||
log.info("=========== 更新Redis缓存数据Id ======== 任务开始"); | |||||
StopWatch stopWatch = new StopWatch(); | |||||
stopWatch.start(); | |||||
// 从缓存中获取业务数据最大id | |||||
CacheKey key = new CacheKey(TaskContant.RedisKey.GPS_DATA_PULL_MAX_BIZ_ID); | |||||
Long id = cachePlusOps.get(key); | |||||
log.info("缓存中已存在id: {}", id); | |||||
// 缓存中已存在 | |||||
if (Objects.nonNull(id)){ | |||||
return "缓存中已存在id: " + id; | |||||
} | |||||
String url = "https://jtjdhcweb.z1l1.com:7443"; | |||||
try { | |||||
// 先查询最新的50万数据 | |||||
trustAllCertificates(); | |||||
Map<String, String> requestParams = assemblyParams(); | |||||
String requestUrl = url + "/talent/gps/datalist"; | |||||
String response = sendPostRequest(requestUrl, requestParams); | |||||
if (response == null) { | |||||
return "下载路径请求失败!"; | |||||
} | |||||
com.alibaba.fastjson.JSONObject object = com.alibaba.fastjson.JSONObject.parseObject(response); | |||||
System.out.println("Response from server: " + response); | |||||
String downloadUrl = object.getString("data"); | |||||
if (downloadUrl == null){ | |||||
return "下载路径请求失败!"; | |||||
} | |||||
String downloadPath = url + downloadUrl; | |||||
String result = sendDownloadRequest(downloadPath); | |||||
if (result == null){ | |||||
return "文件下载失败!"; | |||||
} | |||||
List<NdDataAccessGps> dataList = getNdDataAccessGps(result); | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
return "数据为空!"; | |||||
} | |||||
// 获取最新的50万条数据中最大的id | |||||
// 如果不存在 从新查询的数据中获取 | |||||
id = dataList.stream().map(d -> { | |||||
String bizId = d.getBizId(); | |||||
return Long.parseLong(bizId); | |||||
}).collect(Collectors.toList()).stream().max(Long::compare).get(); | |||||
// 保存到缓存中 | |||||
cachePlusOps.set(key,id,false); | |||||
Long value = cachePlusOps.get(key); | |||||
log.info("已存入缓存中的id: {}", value); | |||||
stopWatch.stop(); | |||||
log.info("=========== 更新Redis缓存数据Id ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "数据Id更新完成!"; | |||||
} catch (Exception e) { | |||||
log.error("Error sending POST request: {}", e.getMessage()); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== 更新Redis缓存数据Id ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
return "数据Id更新失败!"; | |||||
} | |||||
public String getRedisData() { | |||||
// 驾驶员行为分析数据 | |||||
String driverData = cachePlusOps.get(new CacheKey(TaskContant.RedisKey.ALGORITHM_REDIS_DRIVER_DATA_KEY)); | |||||
// 车辆超速数据 | |||||
String speedData = cachePlusOps.get(new CacheKey(TaskContant.RedisKey.ALGORITHM_REDIS_VEHICLE_DATA_KEY)); | |||||
// 视频推理数据 | |||||
String videoData = cachePlusOps.get(new CacheKey(TaskContant.RedisKey.ALGORITHM_REDIS_VIDEO_DATA_KEY)); | |||||
// 拼接数据 | |||||
String allData = cachePlusOps.get(new CacheKey(TaskContant.RedisKey.ALGORITHM_REDIS_ALL_DATA_KEY)); | |||||
log.info("driver:{},speed:{},video:{}", driverData, speedData, videoData); | |||||
log.info("allData:{}", allData); | |||||
return "driverData:" + driverData + ",speedData:" + speedData + ",videoData:" + videoData + ",allData:" + allData; | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package com.ningdatech.carapi.gps.mapper; | |||||
import com.ningdatech.carapi.gps.model.entity.DataAccessGpsRealTimeData; | |||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||||
/** | |||||
* <p> | |||||
* Mapper 接口 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-11 | |||||
*/ | |||||
public interface DataAccessGpsRealTimeDataMapper extends BaseMapper<DataAccessGpsRealTimeData> { | |||||
} |
@@ -0,0 +1,5 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
<mapper namespace="com.ningdatech.carapi.gps.mapper.DataAccessGpsRealTimeDataMapper"> | |||||
</mapper> |
@@ -0,0 +1,29 @@ | |||||
package com.ningdatech.carapi.gps.model; | |||||
import com.baomidou.mybatisplus.annotation.TableName; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
import java.io.Serializable; | |||||
/** | |||||
* @author CMM | |||||
* @since 2024/10/10 22:43 | |||||
*/ | |||||
@Data | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
public class GpsDataVO implements Serializable { | |||||
private static final long serialVersionUID = 1L; | |||||
private String vehicleNo; | |||||
private Integer color; | |||||
private String longitude; | |||||
private String latitude; | |||||
private String direction; | |||||
private String gpsSpeed; | |||||
private Long gpsTime; | |||||
} |
@@ -0,0 +1,56 @@ | |||||
package com.ningdatech.carapi.gps.model.entity; | |||||
import com.baomidou.mybatisplus.annotation.IdType; | |||||
import com.baomidou.mybatisplus.annotation.TableId; | |||||
import com.baomidou.mybatisplus.annotation.TableName; | |||||
import java.io.Serializable; | |||||
import java.time.LocalDateTime; | |||||
import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | |||||
/** | |||||
* <p> | |||||
* | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-11 | |||||
*/ | |||||
@TableName("nd_data_access_gps_real_time_data") | |||||
@ApiModel(value = "DataAccessGpsRealTimeData对象", description = "") | |||||
@Data | |||||
public class DataAccessGpsRealTimeData implements Serializable { | |||||
private static final long serialVersionUID = 1L; | |||||
@TableId(value = "record_id", type = IdType.AUTO) | |||||
private Long recordId; | |||||
@ApiModelProperty("车牌号") | |||||
private String carPlate; | |||||
@ApiModelProperty("车辆颜色") | |||||
private String plateColor; | |||||
@ApiModelProperty("车辆经度") | |||||
private String carLongitude; | |||||
@ApiModelProperty("车辆纬度") | |||||
private String carLatitude; | |||||
@ApiModelProperty("车辆高度") | |||||
private String carAltitude; | |||||
@ApiModelProperty("车辆速度") | |||||
private String carVelocity; | |||||
@ApiModelProperty("车辆方向") | |||||
private String carDirection; | |||||
@ApiModelProperty("更新时间") | |||||
private LocalDateTime updateTime; | |||||
@ApiModelProperty("创建时间") | |||||
private LocalDateTime createTime; | |||||
} |
@@ -0,0 +1,19 @@ | |||||
package com.ningdatech.carapi.gps.service; | |||||
import com.ningdatech.carapi.gps.model.entity.DataAccessGpsRealTimeData; | |||||
import com.baomidou.mybatisplus.extension.service.IService; | |||||
import java.util.List; | |||||
/** | |||||
* <p> | |||||
* 服务类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-11 | |||||
*/ | |||||
public interface IDataAccessGpsRealTimeDataService extends IService<DataAccessGpsRealTimeData> { | |||||
Boolean removeByRecordIds(String tableName,List<Long> recordIdList); | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.ningdatech.carapi.gps.service.impl; | |||||
import cn.hutool.core.collection.ListUtil; | |||||
import com.ningdatech.carapi.gps.model.entity.DataAccessGpsRealTimeData; | |||||
import com.ningdatech.carapi.gps.mapper.DataAccessGpsRealTimeDataMapper; | |||||
import com.ningdatech.carapi.gps.service.IDataAccessGpsRealTimeDataService; | |||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||||
import com.ningdatech.carapi.scheduler.mapper.VehicleOnlineStatusDayMapper; | |||||
import lombok.AllArgsConstructor; | |||||
import org.springframework.stereotype.Service; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
/** | |||||
* <p> | |||||
* 服务实现类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-10-11 | |||||
*/ | |||||
@Service | |||||
@AllArgsConstructor | |||||
public class DataAccessGpsRealTimeDataServiceImpl extends ServiceImpl<DataAccessGpsRealTimeDataMapper, DataAccessGpsRealTimeData> implements IDataAccessGpsRealTimeDataService { | |||||
private final VehicleOnlineStatusDayMapper mapper; | |||||
@Override | |||||
public Boolean removeByRecordIds(String tableName,List<Long> recordIdList) { | |||||
// 根据recordIdList分批删除 每批5000 | |||||
// 分批 | |||||
List<List<Long>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(recordIdList), 5000)); | |||||
for (List<Long> idList : batchList) { | |||||
mapper.batchDelete(tableName,idList); | |||||
} | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,178 @@ | |||||
package com.ningdatech.carapi.gps.task; | |||||
import java.math.BigDecimal; | |||||
import java.time.Instant; | |||||
import java.time.LocalDateTime; | |||||
import java.time.ZoneId; | |||||
import java.util.*; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.stream.Collectors; | |||||
import com.ningdatech.cache.model.cache.CacheKey; | |||||
import com.ningdatech.cache.repository.CachePlusOps; | |||||
import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps; | |||||
import com.ningdatech.carapi.homepage.service.IDataAccessGpsService; | |||||
import com.ningdatech.carapi.scheduler.contants.TaskContant; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.scheduling.annotation.Scheduled; | |||||
import org.springframework.stereotype.Component; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.ningdatech.carapi.gps.manage.GpsDataPullManage; | |||||
import cn.hutool.core.collection.CollUtil; | |||||
import cn.hutool.core.collection.ListUtil; | |||||
import cn.hutool.core.date.StopWatch; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* @author CMM | |||||
* GPS增量数据拉取定时任务 | |||||
* @since 2024/10/12 10:39 | |||||
*/ | |||||
@Component | |||||
@Slf4j | |||||
@RequiredArgsConstructor | |||||
public class GpsFullDataPullTask { | |||||
@Value("${task.switch.is-open}") | |||||
private boolean flag; | |||||
@Value("${task.gps-data-pull.domain}") | |||||
private String domain; | |||||
@Value("${task.gps-data-pull.real-time-data-url}") | |||||
private String realTimeDataUrl; | |||||
@Value("${task.gps-data-pull.key}") | |||||
private String gpsDataPullKey; | |||||
private final GpsDataPullManage gpsDataPullManage; | |||||
private final IDataAccessGpsService dataAccessGpsService; | |||||
private final CachePlusOps cachePlusOps; | |||||
// 定时增量更新车辆GPS数据 每1分钟一次 | |||||
@Scheduled(cron = "0 */1 * * * ?") | |||||
public void doTask() throws Exception { | |||||
if (!flag){ | |||||
log.info("GPS增量数据拉取定时任务未开启!"); | |||||
return; | |||||
} | |||||
log.info("=========== GPS增量数据拉取 ======== 任务开始"); | |||||
StopWatch stopWatch = new StopWatch(); | |||||
stopWatch.start(); | |||||
gpsDataPullManage.trustAllCertificates(); | |||||
// 从缓存中获取业务数据最大id | |||||
CacheKey key = new CacheKey(TaskContant.RedisKey.GPS_DATA_PULL_MAX_BIZ_ID); | |||||
Long id = cachePlusOps.get(key); | |||||
log.info("从缓存中获取业务数据最大id:{}",id); | |||||
// 如果不存在 从表中获取 | |||||
if (Objects.isNull(id)){ | |||||
id = dataAccessGpsService.getMaxBizId(); | |||||
} | |||||
if (Objects.isNull(id)){ | |||||
log.error("业务数据最大id为空!"); | |||||
return; | |||||
} | |||||
Map<String, String> requestParams = assemblyParams(gpsDataPullKey, id); | |||||
String requestUrl = domain + realTimeDataUrl; | |||||
String response = gpsDataPullManage.sendPostRequest(requestUrl, requestParams); | |||||
if (response == null) { | |||||
log.error("下载路径请求失败!"); | |||||
return; | |||||
} | |||||
JSONObject object = JSONObject.parseObject(response); | |||||
log.info("Response from server: {}", object); | |||||
String downloadUrl = object.getString("data"); | |||||
if (StringUtils.isBlank(downloadUrl)){ | |||||
log.error("下载路径请求失败!"); | |||||
return; | |||||
} | |||||
String downloadPath = domain + downloadUrl; | |||||
String result = gpsDataPullManage.sendDownloadRequest(downloadPath); | |||||
if (result == null){ | |||||
log.error("文件下载失败!"); | |||||
return; | |||||
} | |||||
List<NdDataAccessGps> dataList = JSON.parseArray(result, JSONObject.class).stream().map(jsonObject -> { | |||||
String vehicleNo = jsonObject.getString("vehicle_no"); | |||||
Integer color = jsonObject.getInteger("color"); | |||||
// 纬度 | |||||
String longitude = jsonObject.getString("lon"); | |||||
// 经度 | |||||
String latitude = jsonObject.getString("lat"); | |||||
String direction = jsonObject.getString("direction"); | |||||
String gpsSpeed = jsonObject.getString("gps_speed"); | |||||
Long gpsTime = jsonObject.getLong("gps_time"); | |||||
String bizId = jsonObject.getString("id"); | |||||
NdDataAccessGps dataAccessGps = new NdDataAccessGps(); | |||||
dataAccessGps.setCarPlate(vehicleNo); | |||||
dataAccessGps.setPlateColor(color.toString()); | |||||
dataAccessGps.setCarLongitude(BigDecimal.valueOf(Double.parseDouble(latitude))); | |||||
dataAccessGps.setCarLatitude(BigDecimal.valueOf(Double.parseDouble(longitude))); | |||||
dataAccessGps.setCarDirection(BigDecimal.valueOf(Double.parseDouble(direction))); | |||||
dataAccessGps.setCarVelocity(BigDecimal.valueOf(Double.parseDouble(gpsSpeed))); | |||||
// 将毫秒时间戳转换为Instant对象 | |||||
Instant instant = Instant.ofEpochMilli(gpsTime); | |||||
// 使用北京时间时区转换为LocalDateTime | |||||
LocalDateTime localDateTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime(); | |||||
dataAccessGps.setUpdateTime(localDateTime); | |||||
dataAccessGps.setCreateTime(LocalDateTime.now()); | |||||
dataAccessGps.setBizId(bizId); | |||||
return dataAccessGps; | |||||
}).collect(Collectors.toList()); | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
log.info("数据为空!"); | |||||
return; | |||||
} | |||||
log.info("此次拉取到的数据条数:{}",dataList.size()); | |||||
// 获取业务数据最大id | |||||
Long maxBizId = dataList.stream().map(d -> { | |||||
String bizId = d.getBizId(); | |||||
return Long.parseLong(bizId); | |||||
}).collect(Collectors.toList()).stream().max(Long::compare).get(); | |||||
// 保存到缓存中 | |||||
cachePlusOps.set(key,maxBizId,false); | |||||
// 批量保存数据 每批保存1万条 | |||||
// 分批 | |||||
List<List<NdDataAccessGps>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(dataList), 10000)); | |||||
// 创建线程池 线程数是 分批的数量 | |||||
ExecutorService executorService = Executors.newFixedThreadPool(batchList.size()); | |||||
// 线程计数器,就是 分批的数 | |||||
CountDownLatch countDownLatch = new CountDownLatch(batchList.size()); | |||||
batchList.forEach(e -> { | |||||
// 每个分批用一个线程执行 | |||||
executorService.execute(() -> dataAccessGpsService.saveBatch(e, 10000)); | |||||
countDownLatch.countDown(); | |||||
}); | |||||
// 等待所有线程执行完成 | |||||
countDownLatch.await(); | |||||
executorService.shutdown(); | |||||
executorService.awaitTermination(5, TimeUnit.MINUTES); | |||||
if (executorService.isTerminated()) { | |||||
log.info("子线程执行完毕"); | |||||
log.info("主线程开始"); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS实时数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
} | |||||
private Map<String, String> assemblyParams(String key, Long id) { | |||||
long timeNow = System.currentTimeMillis() / 1000; | |||||
String token = gpsDataPullManage.generateMD5Token(timeNow, key); | |||||
Map<String, String> params = new HashMap<>(); | |||||
params.put("timeNow", String.valueOf(timeNow)); | |||||
params.put("token", token); | |||||
params.put("id", String.valueOf(id)); | |||||
return params; | |||||
} | |||||
} |
@@ -0,0 +1,251 @@ | |||||
package com.ningdatech.carapi.gps.task; | |||||
import cn.hutool.core.collection.CollUtil; | |||||
import cn.hutool.core.collection.ListUtil; | |||||
import cn.hutool.core.date.StopWatch; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||||
import com.ningdatech.carapi.gps.manage.GpsDataPullManage; | |||||
import com.ningdatech.carapi.gps.model.entity.DataAccessGpsRealTimeData; | |||||
import com.ningdatech.carapi.gps.service.IDataAccessGpsRealTimeDataService; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.scheduling.annotation.Scheduled; | |||||
import org.springframework.stereotype.Component; | |||||
import java.sql.Connection; | |||||
import java.sql.DriverManager; | |||||
import java.sql.Statement; | |||||
import java.time.Instant; | |||||
import java.time.LocalDateTime; | |||||
import java.time.ZoneId; | |||||
import java.time.format.DateTimeFormatter; | |||||
import java.util.*; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.stream.Collectors; | |||||
/** | |||||
* @author CMM | |||||
* GPS实时数据拉取定时任务 | |||||
* @since 2024/10/12 10:39 | |||||
*/ | |||||
@Component | |||||
@Slf4j | |||||
@RequiredArgsConstructor | |||||
public class GpsRealTimeDataPullTask { | |||||
@Value("${task.switch.is-open}") | |||||
private boolean flag; | |||||
@Value("${task.gps-data-pull.domain}") | |||||
private String domain; | |||||
@Value("${task.gps-data-pull.real-time-data-url}") | |||||
private String realTimeDataUrl; | |||||
@Value("${task.gps-data-pull.key}") | |||||
private String key; | |||||
private final GpsDataPullManage gpsDataPullManage; | |||||
private final IDataAccessGpsRealTimeDataService dataAccessGpsRealTimeDataService; | |||||
@Value("${spring.datasource.url}") | |||||
private String dataBaseUrl; | |||||
@Value("${spring.datasource.username}") | |||||
private String username; | |||||
@Value("${spring.datasource.password}") | |||||
private String password; | |||||
// 定时更新车辆实时GPS数据 每5分钟一次 每次覆盖更新50万条数据 | |||||
@Scheduled(cron = "0 */5 * * * ?") | |||||
public void doTask() throws Exception { | |||||
if (!flag){ | |||||
log.info("GPS实时数据拉取定时任务未开启!"); | |||||
return; | |||||
} | |||||
log.info("=========== GPS实时数据拉取 ======== 任务开始"); | |||||
StopWatch stopWatch = new StopWatch(); | |||||
stopWatch.start(); | |||||
gpsDataPullManage.trustAllCertificates(); | |||||
// 经纬度范围 | |||||
// 最小纬度 | |||||
String sLon = null; | |||||
// 最大纬度 | |||||
String eLon = null; | |||||
// 最小经度 | |||||
String sLat = null; | |||||
// 最大经度 | |||||
String eLat = null; | |||||
// 获取当前表中的最大更新时间 | |||||
long maxUpdateTime = LocalDateTime.now().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); | |||||
Optional<DataAccessGpsRealTimeData> max = dataAccessGpsRealTimeDataService.list(Wrappers.lambdaQuery(DataAccessGpsRealTimeData.class) | |||||
.select(DataAccessGpsRealTimeData::getUpdateTime)).stream().max(Comparator.comparing(DataAccessGpsRealTimeData::getUpdateTime)); | |||||
if (max.isPresent()){ | |||||
maxUpdateTime = max.get().getUpdateTime().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli(); | |||||
} | |||||
log.info("当前表中最大更新时间:{}", Instant.ofEpochMilli(maxUpdateTime).atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); | |||||
Map<String, String> requestParams = assemblyParams(key,String.valueOf(maxUpdateTime), sLon, eLon, sLat, eLat); | |||||
String requestUrl = domain + realTimeDataUrl; | |||||
String response = gpsDataPullManage.sendPostRequest(requestUrl, requestParams); | |||||
if (response == null) { | |||||
log.error("下载路径请求失败!"); | |||||
return; | |||||
} | |||||
JSONObject object = JSONObject.parseObject(response); | |||||
System.out.println("Response from server: " + response); | |||||
String downloadUrl = object.getString("data"); | |||||
if (downloadUrl == null){ | |||||
log.error("下载路径请求失败!"); | |||||
return; | |||||
} | |||||
String downloadPath = domain + downloadUrl; | |||||
String result = gpsDataPullManage.sendDownloadRequest(downloadPath); | |||||
if (result == null){ | |||||
log.error("文件下载失败!"); | |||||
return; | |||||
} | |||||
List<DataAccessGpsRealTimeData> dataList = JSON.parseArray(result, com.alibaba.fastjson.JSONObject.class).stream().map(jsonObject -> { | |||||
String vehicleNo = jsonObject.getString("vehicle_no"); | |||||
Integer color = jsonObject.getInteger("color"); | |||||
// 纬度 | |||||
String longitude = jsonObject.getString("lon"); | |||||
// 经度 | |||||
String latitude = jsonObject.getString("lat"); | |||||
String direction = jsonObject.getString("direction"); | |||||
String gpsSpeed = jsonObject.getString("gps_speed"); | |||||
Long gpsTime = jsonObject.getLong("gps_time"); | |||||
DataAccessGpsRealTimeData dataAccessGpsRealTimeData = new DataAccessGpsRealTimeData(); | |||||
dataAccessGpsRealTimeData.setCarPlate(vehicleNo); | |||||
dataAccessGpsRealTimeData.setPlateColor(color.toString()); | |||||
dataAccessGpsRealTimeData.setCarLongitude(latitude); | |||||
dataAccessGpsRealTimeData.setCarLatitude(longitude); | |||||
dataAccessGpsRealTimeData.setCarDirection(direction); | |||||
dataAccessGpsRealTimeData.setCarVelocity(gpsSpeed); | |||||
// 将毫秒时间戳转换为Instant对象 | |||||
Instant instant = Instant.ofEpochMilli(gpsTime); | |||||
// 使用北京时间时区转换为LocalDateTime | |||||
LocalDateTime localDateTime = instant.atZone(ZoneId.of("Asia/Shanghai")).toLocalDateTime(); | |||||
dataAccessGpsRealTimeData.setUpdateTime(localDateTime); | |||||
dataAccessGpsRealTimeData.setCreateTime(LocalDateTime.now()); | |||||
return dataAccessGpsRealTimeData; | |||||
}).collect(Collectors.toList()); | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
log.info("数据为空!"); | |||||
return; | |||||
} | |||||
// 获取此次拉取到的数据中上传时间大于上次拉取数据的最大上报时间的数据 | |||||
long finalMaxUpdateTime = maxUpdateTime; | |||||
dataList = dataList.stream().filter(e -> e.getUpdateTime().atZone(ZoneId.of("Asia/Shanghai")).toInstant().toEpochMilli() > finalMaxUpdateTime).collect(Collectors.toList()); | |||||
// 如果没有 说明没有新上报的数据 不更新 | |||||
if (CollUtil.isEmpty(dataList)){ | |||||
log.info("没有新上报的数据!"); | |||||
return; | |||||
} | |||||
log.info("本次拉取到{}条数据", dataList.size()); | |||||
// 将数据库中的数据根据更新时间排序 按更新时间 从小到大 根据对应id删除此次拉取到的数据量 | |||||
List<DataAccessGpsRealTimeData> dataAccessGpsRealTimeDataList = dataAccessGpsRealTimeDataService.list(Wrappers.lambdaQuery(DataAccessGpsRealTimeData.class) | |||||
.select(DataAccessGpsRealTimeData::getUpdateTime, DataAccessGpsRealTimeData::getRecordId)) | |||||
.stream().sorted(Comparator.comparing(DataAccessGpsRealTimeData::getUpdateTime)).collect(Collectors.toList()); | |||||
// 截取dataList.size()条数据 | |||||
dataAccessGpsRealTimeDataList = dataAccessGpsRealTimeDataList.subList(0, dataList.size()); | |||||
// 从旧数据中删除这些数据 | |||||
List<Long> recordIdList = dataAccessGpsRealTimeDataList.stream().map(DataAccessGpsRealTimeData::getRecordId).collect(Collectors.toList()); | |||||
String tableName = "nd_data_access_gps_real_time_data"; | |||||
dataAccessGpsRealTimeDataService.removeByRecordIds(tableName,recordIdList); | |||||
// 先清空表中的数据 | |||||
//truncateTable(); | |||||
// 批量保存数据 每批保存1万条 | |||||
// 分批 | |||||
List<List<DataAccessGpsRealTimeData>> batchList = new ArrayList<>(ListUtil.partition(new ArrayList<>(dataList), 10000)); | |||||
// 创建线程池 线程数是 分批的数量 | |||||
ExecutorService executorService = Executors.newFixedThreadPool(batchList.size()); | |||||
// 线程计数器,就是 分批的数 | |||||
CountDownLatch countDownLatch = new CountDownLatch(batchList.size()); | |||||
batchList.forEach(e -> { | |||||
// 每个分批用一个线程执行 | |||||
executorService.execute(() -> dataAccessGpsRealTimeDataService.saveBatch(e, 10000)); | |||||
countDownLatch.countDown(); | |||||
}); | |||||
// 等待所有线程执行完成 | |||||
countDownLatch.await(); | |||||
executorService.shutdown(); | |||||
executorService.awaitTermination(5, TimeUnit.MINUTES); | |||||
if (executorService.isTerminated()) { | |||||
log.info("子线程执行完毕"); | |||||
log.info("主线程开始"); | |||||
} | |||||
stopWatch.stop(); | |||||
log.info("=========== GPS实时数据拉取 ======== 任务结束 {}s",stopWatch.getTotalTimeSeconds()); | |||||
} | |||||
private void truncateTable() { | |||||
// SQL 语句清空表 | |||||
String truncateTableSQL = "TRUNCATE TABLE nd_data_access_gps_real_time_data"; | |||||
Connection conn = null; | |||||
Statement stmt = null; | |||||
try { | |||||
// 加载数据库驱动 | |||||
Class.forName("com.mysql.cj.jdbc.Driver"); | |||||
// 建立连接 | |||||
conn = DriverManager.getConnection(dataBaseUrl, username, password); | |||||
// 创建 Statement 对象 | |||||
stmt = conn.createStatement(); | |||||
// 执行 SQL 语句 | |||||
stmt.executeUpdate(truncateTableSQL); | |||||
log.info("表已被清空,自增主键已重置。"); | |||||
} catch (Exception e) { | |||||
log.error("Error truncating table: {}", e.getMessage()); | |||||
} finally { | |||||
// 关闭资源 | |||||
try { | |||||
if (stmt != null) { | |||||
stmt.close(); | |||||
} | |||||
if (conn != null) { | |||||
conn.close(); | |||||
} | |||||
} catch (Exception e) { | |||||
log.error("Error closing resources: {}", e.getMessage()); | |||||
} | |||||
} | |||||
} | |||||
private Map<String, String> assemblyParams(String key, String queryTime, String sLon, String eLon, String sLat, String eLat) { | |||||
long timeNow = System.currentTimeMillis() / 1000; | |||||
String token = gpsDataPullManage.generateMD5Token(timeNow, key); | |||||
Map<String, String> params = new HashMap<>(); | |||||
params.put("timeNow", String.valueOf(timeNow)); | |||||
params.put("token", token); | |||||
if (StringUtils.isNotBlank(queryTime)) { | |||||
params.put("timeLine", queryTime); | |||||
} | |||||
if (StringUtils.isNotBlank(sLon)) { | |||||
params.put("Slon", sLon); | |||||
} | |||||
if (StringUtils.isNotBlank(eLon)) { | |||||
params.put("Elon", eLon); | |||||
} | |||||
if (StringUtils.isNotBlank(sLat)) { | |||||
params.put("Slat", sLat); | |||||
} | |||||
if (StringUtils.isNotBlank(eLat)) { | |||||
params.put("Elat", eLat); | |||||
} | |||||
return params; | |||||
} | |||||
} |
@@ -1,24 +1,21 @@ | |||||
package com.ningdatech.carapi.homepage.entity.model; | package com.ningdatech.carapi.homepage.entity.model; | ||||
import java.io.Serializable; | |||||
import java.math.BigDecimal; | |||||
import java.time.LocalDateTime; | |||||
import org.springframework.stereotype.Component; | |||||
import com.baomidou.mybatisplus.annotation.IdType; | import com.baomidou.mybatisplus.annotation.IdType; | ||||
import com.baomidou.mybatisplus.annotation.TableField; | import com.baomidou.mybatisplus.annotation.TableField; | ||||
import com.baomidou.mybatisplus.annotation.TableId; | import com.baomidou.mybatisplus.annotation.TableId; | ||||
import com.baomidou.mybatisplus.annotation.TableName; | import com.baomidou.mybatisplus.annotation.TableName; | ||||
import com.ningdatech.cache.model.cache.CacheHashKey; | |||||
import com.ningdatech.cache.model.cache.CacheKeyBuilder; | |||||
import io.swagger.annotations.ApiModel; | import io.swagger.annotations.ApiModel; | ||||
import io.swagger.annotations.ApiModelProperty; | import io.swagger.annotations.ApiModelProperty; | ||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | import lombok.Data; | ||||
import lombok.NoArgsConstructor; | |||||
import lombok.RequiredArgsConstructor; | import lombok.RequiredArgsConstructor; | ||||
import lombok.extern.slf4j.Slf4j; | import lombok.extern.slf4j.Slf4j; | ||||
import org.springframework.stereotype.Component; | |||||
import javax.validation.constraints.Size; | |||||
import java.io.Serializable; | |||||
import java.math.BigDecimal; | |||||
import java.time.LocalDateTime; | |||||
/** | /** | ||||
* @author CMM | * @author CMM | ||||
@@ -28,7 +25,7 @@ import java.time.LocalDateTime; | |||||
@Slf4j | @Slf4j | ||||
@Data | @Data | ||||
@RequiredArgsConstructor | @RequiredArgsConstructor | ||||
@TableName("nd_data_access_gps_2024_04_03") | |||||
@TableName("nd_data_access_gps") | |||||
@ApiModel(value = "NdDataAccessGps", description = "车辆GPS") | @ApiModel(value = "NdDataAccessGps", description = "车辆GPS") | ||||
public class NdDataAccessGps implements Serializable { | public class NdDataAccessGps implements Serializable { | ||||
@@ -99,4 +96,8 @@ public class NdDataAccessGps implements Serializable { | |||||
@ApiModelProperty("时间") | @ApiModelProperty("时间") | ||||
@TableField(value = "create_time") | @TableField(value = "create_time") | ||||
private LocalDateTime createTime; | private LocalDateTime createTime; | ||||
@ApiModelProperty("业务数据ID") | |||||
@TableField(value = "biz_id") | |||||
private String bizId; | |||||
} | } |
@@ -9,4 +9,5 @@ import com.ningdatech.carapi.homepage.entity.model.NdDataAccessGps; | |||||
*/ | */ | ||||
public interface IDataAccessGpsService extends IService<NdDataAccessGps> { | public interface IDataAccessGpsService extends IService<NdDataAccessGps> { | ||||
Long getMaxBizId(); | |||||
} | } |
@@ -1,6 +1,8 @@ | |||||
package com.ningdatech.carapi.homepage.service.impl; | package com.ningdatech.carapi.homepage.service.impl; | ||||
import cn.hutool.core.collection.CollUtil; | |||||
import cn.hutool.core.collection.ListUtil; | import cn.hutool.core.collection.ListUtil; | ||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers; | |||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | ||||
import com.google.common.collect.Lists; | import com.google.common.collect.Lists; | ||||
import com.ningdatech.basic.exception.BizException; | import com.ningdatech.basic.exception.BizException; | ||||
@@ -51,4 +53,16 @@ import java.util.stream.Collectors; | |||||
@RequiredArgsConstructor | @RequiredArgsConstructor | ||||
public class DataAccessGpsServiceImpl extends ServiceImpl<DataAccessGpsMapper, NdDataAccessGps> implements IDataAccessGpsService { | public class DataAccessGpsServiceImpl extends ServiceImpl<DataAccessGpsMapper, NdDataAccessGps> implements IDataAccessGpsService { | ||||
@Override | |||||
public Long getMaxBizId() { | |||||
List<Long> list = baseMapper.selectList(Wrappers.lambdaQuery(NdDataAccessGps.class).select(NdDataAccessGps::getBizId)) | |||||
.stream().map(d -> { | |||||
String bizId = d.getBizId(); | |||||
return Long.parseLong(bizId); | |||||
}).collect(Collectors.toList()); | |||||
if (CollUtil.isNotEmpty(list)){ | |||||
return list.stream().max(Long::compare).get(); | |||||
} | |||||
return null; | |||||
} | |||||
} | } |
@@ -0,0 +1,71 @@ | |||||
package com.ningdatech.carapi.radar.client; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.DataInputStream; | |||||
import java.io.IOException; | |||||
import java.net.Socket; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.time.LocalDateTime; | |||||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* @author CMM | |||||
* 雷达客户端数据处理器 | |||||
* @since 2024/09/28 10:32 | |||||
*/ | |||||
@Slf4j | |||||
public class RadarHandler implements Runnable{ | |||||
private final Socket clientSocket; | |||||
private final IRadarDataService radarDataService; | |||||
private final String radarIp; | |||||
public RadarHandler(Socket socket, IRadarDataService radarDataService, String radarIp) { | |||||
this.clientSocket = socket; | |||||
this.radarDataService = radarDataService; | |||||
this.radarIp = radarIp; | |||||
} | |||||
@Override | |||||
public void run() { | |||||
// 获取输入流以读取雷达发送的数据 | |||||
try { | |||||
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); | |||||
// 开启保活选项 | |||||
clientSocket.setKeepAlive(true); | |||||
byte[] allData = readAllBytesFromDataInputStream(dataInputStream); | |||||
// 处理从雷达接收到的数据 | |||||
if (allData.length > 0) { | |||||
String dataString = new String(allData, StandardCharsets.UTF_8); | |||||
log.info("Received: {}", dataString); | |||||
RadarData radarData = new RadarData(); | |||||
radarData.setRadarIp(radarIp); | |||||
radarData.setData(dataString); | |||||
radarData.setCreateOn(LocalDateTime.now()); | |||||
radarData.setUpdateOn(LocalDateTime.now()); | |||||
radarDataService.save(radarData); | |||||
} | |||||
// 关闭当前周期的连接 | |||||
clientSocket.close(); | |||||
} catch (IOException e) { | |||||
throw new RuntimeException(e); | |||||
} | |||||
} | |||||
private byte[] readAllBytesFromDataInputStream(DataInputStream dis) throws IOException { | |||||
// 使用 ByteArrayOutputStream 来收集所有读取的字节 | |||||
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { | |||||
// 使用一个合适的缓冲区大小 | |||||
byte[] data = new byte[4096]; | |||||
int nRead; | |||||
while ((nRead = dis.read(data, 0, data.length)) != -1) { | |||||
buffer.write(data, 0, nRead); | |||||
} | |||||
buffer.flush(); | |||||
return buffer.toByteArray(); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,37 @@ | |||||
package com.ningdatech.carapi.radar.controller; | |||||
import com.ningdatech.carapi.radar.manage.RadarManage; | |||||
import org.springframework.validation.annotation.Validated; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import com.ningdatech.carapi.common.util.OssUtils; | |||||
import io.swagger.annotations.ApiOperation; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* <p> | |||||
* 前端控制器 | |||||
* </p> | |||||
* | |||||
* @author PoffyZhang | |||||
* @since 2022-10-09 | |||||
*/ | |||||
@Slf4j | |||||
@Validated | |||||
@RestController | |||||
@RequestMapping("/radar") | |||||
@RequiredArgsConstructor | |||||
public class RadarController { | |||||
private final RadarManage radarManage; | |||||
@ApiOperation(value = "雷达监听连接测试", notes = "雷达监听连接测试") | |||||
@GetMapping("/get-radar-data") | |||||
public String getRadarData() { | |||||
return radarManage.getRadarData(); | |||||
} | |||||
} |
@@ -0,0 +1,70 @@ | |||||
package com.ningdatech.carapi.radar.manage; | |||||
import java.io.ByteArrayOutputStream; | |||||
import java.io.DataInputStream; | |||||
import java.io.IOException; | |||||
import java.net.InetAddress; | |||||
import java.net.ServerSocket; | |||||
import java.net.Socket; | |||||
import java.nio.charset.StandardCharsets; | |||||
import org.apache.log4j.net.SocketServer; | |||||
import org.springframework.stereotype.Component; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* 车辆位置信息 | |||||
* | |||||
* @author CMM | |||||
* @since 2023/11/17 09:35 | |||||
*/ | |||||
@Slf4j | |||||
@Component | |||||
@RequiredArgsConstructor | |||||
public class RadarManage { | |||||
public String getRadarData() { | |||||
try { | |||||
ServerSocket serverSocket = new ServerSocket(13000, 600, InetAddress.getByName("192.168.6.42")); | |||||
// 设置为0表示无限等待,可以根据需要设置超时时间 | |||||
serverSocket.setSoTimeout(6000); | |||||
System.out.println("ServerSocket started on port: " + 13000); | |||||
log.info("ServerSocket started on port: {}", 13000); | |||||
Socket clientSocket = serverSocket.accept(); | |||||
System.out.println("Connect Successfully!"); | |||||
log.info("Connect Successfully!"); | |||||
DataInputStream dataInputStream = new DataInputStream(clientSocket.getInputStream()); | |||||
// 开启保活选项 | |||||
clientSocket.setKeepAlive(true); | |||||
byte[] allData; | |||||
while (!clientSocket.isClosed()) { | |||||
// 使用 ByteArrayOutputStream 来收集所有读取的字节 | |||||
try (ByteArrayOutputStream buffer = new ByteArrayOutputStream()) { | |||||
// 使用一个合适的缓冲区大小 | |||||
byte[] data = new byte[4096]; | |||||
int nRead; | |||||
while ((nRead = dataInputStream.read(data, 0, data.length)) != -1 && buffer.size() < 4096) { | |||||
buffer.write(data, 0, nRead); | |||||
} | |||||
buffer.flush(); | |||||
allData = buffer.toByteArray(); | |||||
} | |||||
// 处理从雷达接收到的数据 | |||||
if (allData.length > 0) { | |||||
String dataString = new String(allData, StandardCharsets.UTF_8); | |||||
System.out.println("Received: " + dataString); | |||||
log.info("Received: {}", dataString); | |||||
break; | |||||
} | |||||
} | |||||
// 关闭当前周期的连接 | |||||
clientSocket.close(); | |||||
} catch (IOException e) { | |||||
return e.getMessage(); | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package com.ningdatech.carapi.radar.mapper; | |||||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; | |||||
/** | |||||
* <p> | |||||
* Mapper 接口 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-09-28 | |||||
*/ | |||||
public interface RadarDataMapper extends BaseMapper<RadarData> { | |||||
} |
@@ -0,0 +1,5 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | |||||
<mapper namespace="com.ningdatech.carapi.radar.mapper.RadarDataMapper"> | |||||
</mapper> |
@@ -0,0 +1,22 @@ | |||||
package com.ningdatech.carapi.radar.model.dto; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @author CMM | |||||
* @since 2024/09/28 11:24 | |||||
*/ | |||||
@Data | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
public class RadarInfoDTO { | |||||
@ApiModelProperty("雷达ip") | |||||
private String radarIp; | |||||
@ApiModelProperty("雷达监听端口号") | |||||
private Integer radarPort; | |||||
} |
@@ -0,0 +1,42 @@ | |||||
package com.ningdatech.carapi.radar.model.entity; | |||||
import com.baomidou.mybatisplus.annotation.IdType; | |||||
import com.baomidou.mybatisplus.annotation.TableId; | |||||
import com.baomidou.mybatisplus.annotation.TableName; | |||||
import java.io.Serializable; | |||||
import java.time.LocalDateTime; | |||||
import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | |||||
/** | |||||
* <p> | |||||
* | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-09-28 | |||||
*/ | |||||
@TableName("nd_radar_data") | |||||
@ApiModel(value = "RadarData对象", description = "") | |||||
@Data | |||||
public class RadarData implements Serializable { | |||||
private static final long serialVersionUID = 1L; | |||||
@ApiModelProperty("主键") | |||||
@TableId(value = "id", type = IdType.AUTO) | |||||
private Long id; | |||||
@ApiModelProperty("雷达推送的数据包") | |||||
private String data; | |||||
@ApiModelProperty("推送数据的雷达ip") | |||||
private String radarIp; | |||||
@ApiModelProperty("创建时间") | |||||
private LocalDateTime createOn; | |||||
@ApiModelProperty("更新时间") | |||||
private LocalDateTime updateOn; | |||||
} |
@@ -0,0 +1,51 @@ | |||||
package com.ningdatech.carapi.radar.properties; | |||||
import lombok.Data; | |||||
import org.springframework.boot.context.properties.ConfigurationProperties; | |||||
import org.springframework.stereotype.Component; | |||||
/** | |||||
* @author CMM | |||||
* @author CMM | |||||
* @date 2024/9/27 14:40 | |||||
* / | |||||
* /** | |||||
* @since 2024/09/27 14:40 | |||||
*/ | |||||
@Data | |||||
@Component | |||||
@ConfigurationProperties(prefix = "radar-data-task") | |||||
public class RadarDataTaskProperties { | |||||
/** | |||||
* 是否开启雷达数据接收 | |||||
*/ | |||||
private Boolean enable; | |||||
// 雷达设备的IP地址 | |||||
// 黄山隧道义乌来向 | |||||
private String hsYwComeRadarIp; | |||||
// 黄山隧道义乌去向 | |||||
private String hsYwGoRadarIp; | |||||
// 黄山隧道东阳来向 | |||||
private String hsDyComeRadarIp; | |||||
// 黄山隧道东阳去向 | |||||
private String hsDyGoRadarIp; | |||||
// 何里隧道义乌方向 | |||||
private String hlYwRadarIp; | |||||
// 何里隧道兰溪方向 | |||||
private String hlLxRadarIp; | |||||
// 雷达设备监听的端口号 | |||||
// 黄山隧道义乌来向 | |||||
private Integer hsYwComeRadarPort; | |||||
// 黄山隧道义乌去向 | |||||
private Integer hsYwGoRadarPort; | |||||
// 黄山隧道东阳来向 | |||||
private Integer hsDyComeRadarPort; | |||||
// 黄山隧道东阳去向 | |||||
private Integer hsDyGoRadarPort; | |||||
// 何里隧道义乌方向 | |||||
private Integer hlYwRadarPort; | |||||
// 何里隧道兰溪方向 | |||||
private Integer hlLxRadarPort; | |||||
} |
@@ -0,0 +1,61 @@ | |||||
package com.ningdatech.carapi.radar.server; | |||||
import com.ningdatech.carapi.radar.client.RadarHandler; | |||||
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO; | |||||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import java.io.IOException; | |||||
import java.net.*; | |||||
import java.util.List; | |||||
import java.util.Objects; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
/** | |||||
* @author CMM | |||||
* @author CMM | |||||
* @date 2024/9/28 10:27 | |||||
* / | |||||
* /** | |||||
* @since 2024/09/28 10:27 | |||||
*/ | |||||
@Slf4j | |||||
public class MultiRadarServer { | |||||
private final IRadarDataService radarDataService; | |||||
private final ExecutorService executorService; | |||||
public MultiRadarServer(IRadarDataService radarDataService,List<RadarInfoDTO> radarInfoList) { | |||||
this.radarDataService = radarDataService; | |||||
this.executorService = Executors.newFixedThreadPool(radarInfoList.size()); | |||||
} | |||||
public void startServers(List<RadarInfoDTO> radarInfoList) { | |||||
log.info("Radar server started. Waiting for connections..."); | |||||
for (RadarInfoDTO radarInfo : radarInfoList) { | |||||
if (Objects.nonNull(radarInfo.getRadarIp()) && Objects.nonNull(radarInfo.getRadarPort())) { | |||||
// 每个设备创建一个线程 建立一个ServerSocket | |||||
executorService.execute(() -> { | |||||
try (ServerSocket serverSocket = new ServerSocket(radarInfo.getRadarPort(), 600, InetAddress.getByName(radarInfo.getRadarIp()))){ | |||||
// 设置为0表示无限等待,可以根据需要设置超时时间 | |||||
serverSocket.setSoTimeout(0); | |||||
log.info("ServerSocket started on port: {}", radarInfo.getRadarPort()); | |||||
while (!Thread.currentThread().isInterrupted()) { | |||||
Socket clientSocket = serverSocket.accept(); | |||||
//log.info("Connected to a radar on port: {}", radarInfo.getRadarPort()); | |||||
RadarHandler task = new RadarHandler(clientSocket, radarDataService, radarInfo.getRadarIp()); | |||||
Thread thread = new Thread(task); | |||||
thread.start(); | |||||
} | |||||
} catch (IOException e) { | |||||
log.error("Error occurred while accepting client connection: ", e); | |||||
} | |||||
}); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,16 @@ | |||||
package com.ningdatech.carapi.radar.service; | |||||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||||
import com.baomidou.mybatisplus.extension.service.IService; | |||||
/** | |||||
* <p> | |||||
* 服务类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-09-28 | |||||
*/ | |||||
public interface IRadarDataService extends IService<RadarData> { | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.ningdatech.carapi.radar.service.impl; | |||||
import com.ningdatech.carapi.radar.model.entity.RadarData; | |||||
import com.ningdatech.carapi.radar.mapper.RadarDataMapper; | |||||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; | |||||
import org.springframework.stereotype.Service; | |||||
/** | |||||
* <p> | |||||
* 服务实现类 | |||||
* </p> | |||||
* | |||||
* @author CMM | |||||
* @since 2024-09-28 | |||||
*/ | |||||
@Service | |||||
public class RadarDataServiceImpl extends ServiceImpl<RadarDataMapper, RadarData> implements IRadarDataService { | |||||
} |
@@ -0,0 +1,55 @@ | |||||
package com.ningdatech.carapi.radar.task; | |||||
import java.util.List; | |||||
import javax.annotation.PostConstruct; | |||||
import org.apache.commons.compress.utils.Lists; | |||||
import org.springframework.stereotype.Component; | |||||
import com.ningdatech.carapi.radar.model.dto.RadarInfoDTO; | |||||
import com.ningdatech.carapi.radar.properties.RadarDataTaskProperties; | |||||
import com.ningdatech.carapi.radar.server.MultiRadarServer; | |||||
import com.ningdatech.carapi.radar.service.IRadarDataService; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* @author CMM | |||||
* 雷达数据接收定时任务 | |||||
* @since 2024/09/27 14:36 | |||||
*/ | |||||
@Slf4j | |||||
@Component | |||||
@RequiredArgsConstructor | |||||
public class RadarDataTask { | |||||
private final RadarDataTaskProperties properties; | |||||
private final IRadarDataService radarDataService; | |||||
@PostConstruct | |||||
public void initTask() { | |||||
if (!properties.getEnable()) { | |||||
log.warn("雷达数据同步已关闭……"); | |||||
return; | |||||
} | |||||
//initRadarDataTaskByStart(); | |||||
} | |||||
/** | |||||
* 项目重启之后重新初始化雷达接收定时任务 | |||||
*/ | |||||
private void initRadarDataTaskByStart() { | |||||
log.info("雷达数据接收任务已启动……"); | |||||
List<RadarInfoDTO> radarInfoList = Lists.newArrayList(); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHsYwComeRadarIp(),properties.getHsYwComeRadarPort())); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHsYwGoRadarIp(),properties.getHsYwGoRadarPort())); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHsDyComeRadarIp(),properties.getHsDyComeRadarPort())); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHsDyGoRadarIp(),properties.getHsDyGoRadarPort())); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHlYwRadarIp(),properties.getHlYwRadarPort())); | |||||
radarInfoList.add(new RadarInfoDTO(properties.getHlLxRadarIp(),properties.getHlLxRadarPort())); | |||||
MultiRadarServer server = new MultiRadarServer(radarDataService,radarInfoList); | |||||
server.startServers(radarInfoList); | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package com.ningdatech.carapi.road.constant; | |||||
import java.util.Objects; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @return | |||||
* @author CMM | |||||
* @since 2022/12/20 14:10 | |||||
*/ | |||||
@Getter | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
@ApiModel(value = "VehicleDangerLevelEnum", description = "车辆风险等级-枚举") | |||||
public enum DriverAbnormalTypeEnum { | |||||
/** | |||||
* 驾驶员异常行为类型 | |||||
*/ | |||||
FATIGUE(1, "疲劳驾驶"), | |||||
POOR_HABITS(2, "不良驾驶习惯"); | |||||
private Integer code; | |||||
private String desc; | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
public void setDesc(String desc) { | |||||
this.desc = desc; | |||||
} | |||||
public static String getDescByCode(Integer code) { | |||||
if(Objects.isNull(code)){ | |||||
return StringUtils.EMPTY; | |||||
} | |||||
for (DriverAbnormalTypeEnum t : DriverAbnormalTypeEnum.values()) { | |||||
if (code.equals(t.getCode())) { | |||||
return t.desc; | |||||
} | |||||
} | |||||
return StringUtils.EMPTY; | |||||
} | |||||
public static Integer getCodeByDesc(String desc) { | |||||
if(StringUtils.isBlank(desc)){ | |||||
return null; | |||||
} | |||||
for (DriverAbnormalTypeEnum t : DriverAbnormalTypeEnum.values()) { | |||||
if (desc.equals(t.getDesc())) { | |||||
return t.code; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public boolean eq(String val) { | |||||
return this.name().equals(val); | |||||
} | |||||
} |
@@ -0,0 +1,71 @@ | |||||
package com.ningdatech.carapi.road.constant; | |||||
import java.util.Objects; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @return | |||||
* @author CMM | |||||
* @since 2022/12/20 14:10 | |||||
*/ | |||||
@Getter | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
@ApiModel(value = "VehicleDangerLevelEnum", description = "车辆风险等级-枚举") | |||||
public enum RoadBehaviorTypeEnum { | |||||
/** | |||||
* 道路异常行为类型 | |||||
*/ | |||||
DRIVER(1, "驾驶员行为异常"), | |||||
SPEED(2, "车辆速度异常"), | |||||
VIDEO(3,"道路监控异常"); | |||||
private Integer code; | |||||
private String desc; | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
public void setDesc(String desc) { | |||||
this.desc = desc; | |||||
} | |||||
public static String getDescByCode(Integer code) { | |||||
if(Objects.isNull(code)){ | |||||
return StringUtils.EMPTY; | |||||
} | |||||
for (RoadBehaviorTypeEnum t : RoadBehaviorTypeEnum.values()) { | |||||
if (code.equals(t.getCode())) { | |||||
return t.desc; | |||||
} | |||||
} | |||||
return StringUtils.EMPTY; | |||||
} | |||||
public static Integer getCodeByDesc(String desc) { | |||||
if(StringUtils.isBlank(desc)){ | |||||
return null; | |||||
} | |||||
for (RoadBehaviorTypeEnum t : RoadBehaviorTypeEnum.values()) { | |||||
if (desc.equals(t.getDesc())) { | |||||
return t.code; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public boolean eq(String val) { | |||||
return this.name().equals(val); | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package com.ningdatech.carapi.road.constant; | |||||
import java.util.Objects; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @return | |||||
* @author CMM | |||||
* @since 2022/12/20 14:10 | |||||
*/ | |||||
@Getter | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
@ApiModel(value = "RoadStatusTypeEnum", description = "道路拥堵情况-枚举") | |||||
public enum RoadStatusTypeEnum { | |||||
/** | |||||
* 道路情况 | |||||
*/ | |||||
HEAVY(1, "严重拥堵"), | |||||
MEDIUM(2, "中度拥堵"), | |||||
LIGHT(3, "轻度拥堵"); | |||||
private Integer code; | |||||
private String desc; | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
public void setDesc(String desc) { | |||||
this.desc = desc; | |||||
} | |||||
public static String getDescByCode(Integer code) { | |||||
if(Objects.isNull(code)){ | |||||
return StringUtils.EMPTY; | |||||
} | |||||
for (RoadStatusTypeEnum t : RoadStatusTypeEnum.values()) { | |||||
if (code.equals(t.getCode())) { | |||||
return t.desc; | |||||
} | |||||
} | |||||
return StringUtils.EMPTY; | |||||
} | |||||
public static Integer getCodeByDesc(String desc) { | |||||
if(StringUtils.isBlank(desc)){ | |||||
return null; | |||||
} | |||||
for (RoadStatusTypeEnum t : RoadStatusTypeEnum.values()) { | |||||
if (desc.equals(t.getDesc())) { | |||||
return t.code; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public boolean eq(String val) { | |||||
return this.name().equals(val); | |||||
} | |||||
} |
@@ -0,0 +1,70 @@ | |||||
package com.ningdatech.carapi.road.constant; | |||||
import java.util.Objects; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @return | |||||
* @author CMM | |||||
* @since 2022/12/20 14:10 | |||||
*/ | |||||
@Getter | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
@ApiModel(value = "VehicleDangerLevelEnum", description = "车辆风险等级-枚举") | |||||
public enum VehicleSpeedAbnormalTypeEnum { | |||||
/** | |||||
* 车辆速度异常类型 | |||||
*/ | |||||
OVER(1, "超速"), | |||||
LOW(2, "低速"), | |||||
STOP(3, "停车"), | |||||
REVERSE(4, "逆向行驶"); | |||||
private Integer code; | |||||
private String desc; | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
public void setDesc(String desc) { | |||||
this.desc = desc; | |||||
} | |||||
public static String getDescByCode(Integer code) { | |||||
if(Objects.isNull(code)){ | |||||
return StringUtils.EMPTY; | |||||
} | |||||
for (VehicleSpeedAbnormalTypeEnum t : VehicleSpeedAbnormalTypeEnum.values()) { | |||||
if (code.equals(t.getCode())) { | |||||
return t.desc; | |||||
} | |||||
} | |||||
return StringUtils.EMPTY; | |||||
} | |||||
public static Integer getCodeByDesc(String desc) { | |||||
if(StringUtils.isBlank(desc)){ | |||||
return null; | |||||
} | |||||
for (VehicleSpeedAbnormalTypeEnum t : VehicleSpeedAbnormalTypeEnum.values()) { | |||||
if (desc.equals(t.getDesc())) { | |||||
return t.code; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public boolean eq(String val) { | |||||
return this.name().equals(val); | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package com.ningdatech.carapi.road.constant; | |||||
import java.util.Objects; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import io.swagger.annotations.ApiModel; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Getter; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @return | |||||
* @author CMM | |||||
* @since 2022/12/20 14:10 | |||||
*/ | |||||
@Getter | |||||
@AllArgsConstructor | |||||
@NoArgsConstructor | |||||
@ApiModel(value = "WeatherStatusTypeEnum", description = "天气情况-枚举") | |||||
public enum WeatherStatusTypeEnum { | |||||
/** | |||||
* 道路情况 | |||||
*/ | |||||
FOG(1, "雾天"), | |||||
RAIN(2, "雨天"), | |||||
SNOW(3, "雪天"), | |||||
WIND(4, "大风"), | |||||
SANDSTORM(5, "沙尘暴"); | |||||
private Integer code; | |||||
private String desc; | |||||
public String getDesc() { | |||||
return desc; | |||||
} | |||||
public void setDesc(String desc) { | |||||
this.desc = desc; | |||||
} | |||||
public static String getDescByCode(Integer code) { | |||||
if(Objects.isNull(code)){ | |||||
return StringUtils.EMPTY; | |||||
} | |||||
for (WeatherStatusTypeEnum t : WeatherStatusTypeEnum.values()) { | |||||
if (code.equals(t.getCode())) { | |||||
return t.desc; | |||||
} | |||||
} | |||||
return StringUtils.EMPTY; | |||||
} | |||||
public static Integer getCodeByDesc(String desc) { | |||||
if(StringUtils.isBlank(desc)){ | |||||
return null; | |||||
} | |||||
for (WeatherStatusTypeEnum t : WeatherStatusTypeEnum.values()) { | |||||
if (desc.equals(t.getDesc())) { | |||||
return t.code; | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public boolean eq(String val) { | |||||
return this.name().equals(val); | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.ningdatech.carapi.road.controller; | |||||
import com.ningdatech.carapi.road.manage.RoadMonitorManage; | |||||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | |||||
import org.springframework.validation.annotation.Validated; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import com.ningdatech.carapi.gps.manage.GpsDataPullManage; | |||||
import io.swagger.annotations.ApiOperation; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
/** | |||||
* <p> | |||||
* 前端控制器 | |||||
* </p> | |||||
* | |||||
* @author PoffyZhang | |||||
* @since 2022-10-09 | |||||
*/ | |||||
@Slf4j | |||||
@Validated | |||||
@RestController | |||||
@RequestMapping("/road-monitor") | |||||
@RequiredArgsConstructor | |||||
public class RoadMonitorController { | |||||
private final RoadMonitorManage roadMonitorManage; | |||||
@ApiOperation(value = "综合态势数据", notes = "综合态势数据") | |||||
@GetMapping("/get-comprehensive-situation-data") | |||||
public ComprehensiveSituationVO getComSitData() { | |||||
return roadMonitorManage.getComSitData(); | |||||
} | |||||
} |
@@ -0,0 +1,67 @@ | |||||
package com.ningdatech.carapi.road.manage; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.ningdatech.cache.model.cache.CacheKey; | |||||
import com.ningdatech.cache.repository.CachePlusOps; | |||||
import com.ningdatech.carapi.road.constant.RoadStatusTypeEnum; | |||||
import com.ningdatech.carapi.road.model.vo.ComprehensiveSituationVO; | |||||
import com.ningdatech.carapi.scheduler.contants.TaskContant; | |||||
import lombok.RequiredArgsConstructor; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.springframework.stereotype.Component; | |||||
import java.util.Objects; | |||||
/** | |||||
* @author CMM | |||||
* @since 2024/10/23 10:11 | |||||
*/ | |||||
@Slf4j | |||||
@Component | |||||
@RequiredArgsConstructor | |||||
public class RoadMonitorManage { | |||||
private final CachePlusOps cachePlusOps; | |||||
public ComprehensiveSituationVO getComSitData() { | |||||
ComprehensiveSituationVO vo = new ComprehensiveSituationVO(); | |||||
// 设置默认值 | |||||
vo.setRoadSafetyIndex(100); | |||||
vo.setRoadStatus("通畅"); | |||||
vo.setWeatherStatus("良好"); | |||||
vo.setDangerousActionCount(0); | |||||
// 实时从缓存中获取算法分析的结果 | |||||
String allData = cachePlusOps.get(new CacheKey(TaskContant.RedisKey.ALGORITHM_REDIS_ALL_DATA_KEY)); | |||||
JSONObject jsonObject = JSON.parseObject(allData); | |||||
if (Objects.nonNull(jsonObject)) { | |||||
// 道路情况 | |||||
String congestion = jsonObject.getString("Congestion"); | |||||
RoadStatusTypeEnum roadStatusTypeEnum = RoadStatusTypeEnum.valueOf(congestion); | |||||
if (StringUtils.isNotBlank(congestion)){ | |||||
switch (roadStatusTypeEnum){ | |||||
case HEAVY: | |||||
vo.setRoadStatus(RoadStatusTypeEnum.getDescByCode(RoadStatusTypeEnum.HEAVY.getCode())); | |||||
break; | |||||
case MEDIUM: | |||||
vo.setRoadStatus(RoadStatusTypeEnum.getDescByCode(RoadStatusTypeEnum.MEDIUM.getCode())); | |||||
break; | |||||
case LIGHT: | |||||
vo.setRoadStatus(RoadStatusTypeEnum.getDescByCode(RoadStatusTypeEnum.LIGHT.getCode())); | |||||
break; | |||||
default: | |||||
vo.setRoadStatus("良好"); | |||||
} | |||||
} | |||||
// 天气情况 | |||||
String weather = jsonObject.getString("Weather"); | |||||
vo.setWeatherStatus(jsonObject.getString("weatherStatus")); | |||||
vo.setRoadSafetyIndex(jsonObject.getInteger("roadSafetyIndex")); | |||||
vo.setDangerousActionCount(jsonObject.getInteger("dangerousActionCount")); | |||||
return vo; | |||||
} | |||||
return null; | |||||
} | |||||
} |
@@ -0,0 +1,30 @@ | |||||
package com.ningdatech.carapi.road.model.vo; | |||||
import io.swagger.annotations.ApiModel; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.AllArgsConstructor; | |||||
import lombok.Data; | |||||
import lombok.NoArgsConstructor; | |||||
/** | |||||
* @author CMM | |||||
* @since 2024/10/23 10:49 | |||||
*/ | |||||
@Data | |||||
@ApiModel(description = "道路安全监综合态势计数据-VO") | |||||
@NoArgsConstructor | |||||
@AllArgsConstructor | |||||
public class ComprehensiveSituationVO { | |||||
@ApiModelProperty("道路安全指数") | |||||
private Integer roadSafetyIndex; | |||||
@ApiModelProperty("当前道路情况") | |||||
private String roadStatus; | |||||
@ApiModelProperty("当前天气情况") | |||||
private String weatherStatus; | |||||
@ApiModelProperty("当前危险行为统计") | |||||
private Integer dangerousActionCount; | |||||
} |
@@ -109,6 +109,13 @@ public interface TaskContant { | |||||
public static final Integer IRS_SECRET_TIME_INTERVAL = 10; | public static final Integer IRS_SECRET_TIME_INTERVAL = 10; | ||||
public static final String GPS_DATA_PULL_MAX_BIZ_ID = "cm:task:gps:gps_data_pull_max_biz_id"; | |||||
public static final String ALGORITHM_REDIS_DRIVER_DATA_KEY = "yw:algorithm:redis:driver_data_key"; | |||||
public static final String ALGORITHM_REDIS_VEHICLE_DATA_KEY = "yw:algorithm:redis:vehicle_data_key"; | |||||
public static final String ALGORITHM_REDIS_VIDEO_DATA_KEY = "yw:algorithm:redis:video_data_key"; | |||||
public static final String ALGORITHM_REDIS_ALL_DATA_KEY = "yw:algorithm:redis:all_data_key"; | |||||
} | } | ||||
@@ -32,4 +32,6 @@ public interface VehicleOnlineStatusDayMapper extends BaseMapper<VehicleOnlineSt | |||||
List<VehicleOnlineStatusDay> listByTable(@Param("tableName") String tableName, @Param("statusDay") VehicleOnlineStatusDay statusDay); | List<VehicleOnlineStatusDay> listByTable(@Param("tableName") String tableName, @Param("statusDay") VehicleOnlineStatusDay statusDay); | ||||
Boolean batchSave(@Param("tableName") String tableName,@Param("statusDays") List<VehicleOnlineStatusDay> statusDays); | Boolean batchSave(@Param("tableName") String tableName,@Param("statusDays") List<VehicleOnlineStatusDay> statusDays); | ||||
Boolean batchDelete(@Param("tableName") String tableName, @Param("recordIdList") List<Long> recordIdList); | |||||
} | } |
@@ -1,6 +1,12 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | <?xml version="1.0" encoding="UTF-8"?> | ||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> | ||||
<mapper namespace="com.ningdatech.carapi.scheduler.mapper.VehicleOnlineStatusDayMapper"> | <mapper namespace="com.ningdatech.carapi.scheduler.mapper.VehicleOnlineStatusDayMapper"> | ||||
<delete id="batchDelete"> | |||||
DELETE FROM ${tableName} WHERE record_id IN | |||||
<foreach collection="recordIdList" item="recordId" open="(" close=")" separator=","> | |||||
#{recordId} | |||||
</foreach> | |||||
</delete> | |||||
<select id="countTable" resultType="java.lang.Integer"> | <select id="countTable" resultType="java.lang.Integer"> | ||||
SELECT count(0) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=#{tableName}; | SELECT count(0) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=#{tableName}; | ||||
</select> | </select> | ||||
@@ -0,0 +1,11 @@ | |||||
package com.ningdatech.carapi.scheduler.task; | |||||
/** | |||||
* @description: GPS数据拉取任务 | |||||
* @author CMM | |||||
* @since 2024/10/08 10:55 | |||||
*/ | |||||
public class GpsDataPullTask { | |||||
} |
@@ -236,6 +236,7 @@ public class RegionsCacheHelper implements InitializingBean { | |||||
result.put(region, listChildRegionId(region.getId())); | result.put(region, listChildRegionId(region.getId())); | ||||
} | } | ||||
} | } | ||||
result.put(getByRegionId(regionId), listChildRegionId(regionId)); | |||||
return result; | return result; | ||||
} | } | ||||
@@ -96,6 +96,11 @@ task: | |||||
host: iZbp13nwyvib53j4j1p2xoZ | host: iZbp13nwyvib53j4j1p2xoZ | ||||
switch: | switch: | ||||
is-open: false #开关 | is-open: false #开关 | ||||
gps-data-pull: | |||||
domain: https://jtjdhcweb.z1l1.com:7443 | |||||
real-time-data-url: /talent/gps/datalist | |||||
key: Gps@!@163. | |||||
corn: | corn: | ||||
task-corn: 0/30 * * * * ? #每隔30秒统计一次车辆在线数 | task-corn: 0/30 * * * * ? #每隔30秒统计一次车辆在线数 | ||||
driver-abnormal-behavior-task-corn: 0 0/1 * * * ? #每隔1分钟更新一次驾驶员异常行为数据 | driver-abnormal-behavior-task-corn: 0 0/1 * * * ? #每隔1分钟更新一次驾驶员异常行为数据 | ||||
@@ -144,4 +149,20 @@ gps: | |||||
vedio: | vedio: | ||||
host: 120.26.44.207 | host: 120.26.44.207 | ||||
send-urge-warn: | send-urge-warn: | ||||
url: http://120.26.44.207:8092/warn/send_urge_warn | |||||
url: http://120.26.44.207:8092/warn/send_urge_warn | |||||
radar-data-task: | |||||
enable: false | |||||
# 黄山隧道 | |||||
hs-yw-come-radar-ip: 192.168.6.40 | |||||
hs-yw-come-radar-port: 10000 | |||||
hs-yw-go-radar-ip: 192.168.6.43 | |||||
hs-yw-go-radar-port: 20000 | |||||
hs-dy-come-radar-ip: 192.168.6.47 | |||||
hs-dy-come-radar-port: 30000 | |||||
hs-dy-go-radar-ip: 192.168.6.45 | |||||
hs-dy-go-radar-port: 40000 | |||||
# 何里隧道 | |||||
hl-yw-radar-ip: 192.168.10.60 | |||||
hl-yw-radar-port: 50000 | |||||
hl-lx-radar-ip: 192.168.10.63 | |||||
hl-lx-radar-port: 60000 |
@@ -123,6 +123,10 @@ task: | |||||
host: iZi5c01gdwbs8j8r0dzf8lZ | host: iZi5c01gdwbs8j8r0dzf8lZ | ||||
switch: | switch: | ||||
is-open: true #开关 | is-open: true #开关 | ||||
gps-data-pull: | |||||
domain: https://jtjdhcweb.z1l1.com:7443 | |||||
real-time-data-url: /talent/gps/datalist | |||||
key: Gps@!@163. | |||||
corn: | corn: | ||||
task-corn: 0/30 * * * * ? #每隔30秒统计一次车辆在线数 | task-corn: 0/30 * * * * ? #每隔30秒统计一次车辆在线数 | ||||
driver-abnormal-behavior-task-corn: 0 0/1 * * * ? #每隔1分钟更新一次驾驶员异常行为数据 | driver-abnormal-behavior-task-corn: 0 0/1 * * * ? #每隔1分钟更新一次驾驶员异常行为数据 | ||||
@@ -172,4 +176,20 @@ vedio: | |||||
host: 10.145.213.162 | host: 10.145.213.162 | ||||
# 督办调用接口 | # 督办调用接口 | ||||
send-urge-warn: | send-urge-warn: | ||||
url: http://10.145.213.161:8092/warn/send_urge_warn | |||||
url: http://10.145.213.161:8092/warn/send_urge_warn | |||||
radar-data-task: | |||||
enable: false | |||||
# 黄山隧道 | |||||
hs-yw-come-radar-ip: 192.168.6.40 | |||||
hs-yw-come-radar-port: 10000 | |||||
hs-yw-go-radar-ip: 192.168.6.43 | |||||
hs-yw-go-radar-port: 20000 | |||||
hs-dy-come-radar-ip: 192.168.6.47 | |||||
hs-dy-come-radar-port: 30000 | |||||
hs-dy-go-radar-ip: 192.168.6.45 | |||||
hs-dy-go-radar-port: 40000 | |||||
# 何里隧道 | |||||
hl-yw-radar-ip: 192.168.10.60 | |||||
hl-yw-radar-port: 50000 | |||||
hl-lx-radar-ip: 192.168.10.63 | |||||
hl-lx-radar-port: 60000 |
@@ -20,6 +20,8 @@ security: | |||||
- /doc.html | - /doc.html | ||||
- /ok.html | - /ok.html | ||||
- /open/api/** | - /open/api/** | ||||
- /radar/** | |||||
- /gps/** | |||||
ignore-csrf-urls: | ignore-csrf-urls: | ||||
- /api/v1/user/auth/** | - /api/v1/user/auth/** | ||||
- /v2/api-docs | - /v2/api-docs | ||||
@@ -42,4 +44,6 @@ security: | |||||
- /api/qrcode/** | - /api/qrcode/** | ||||
- /api/industry/** | - /api/industry/** | ||||
- /api/car-rpt/** | - /api/car-rpt/** | ||||
- /open/api/** | |||||
- /open/api/** | |||||
- /radar/** | |||||
- /gps/** |
@@ -19,6 +19,8 @@ security: | |||||
- /doc.html | - /doc.html | ||||
- /ok.html | - /ok.html | ||||
- /open/api/** | - /open/api/** | ||||
- /radar/** | |||||
- /gps/** | |||||
ignore-csrf-urls: | ignore-csrf-urls: | ||||
- /api/v1/user/auth/** | - /api/v1/user/auth/** | ||||
- /v2/api-docs | - /v2/api-docs | ||||
@@ -41,4 +43,6 @@ security: | |||||
- /api/qrcode/** | - /api/qrcode/** | ||||
- /api/industry/** | - /api/industry/** | ||||
- /api/car-rpt/** | - /api/car-rpt/** | ||||
- /open/api/** | |||||
- /open/api/** | |||||
- /radar/** | |||||
- /gps/** |