diff --git a/qaup-collision/src/main/java/com/qaup/collision/common/model/repository/TrafficLightRepository.java b/qaup-collision/src/main/java/com/qaup/collision/common/model/repository/TrafficLightRepository.java index aa975d3a..96e0314d 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/common/model/repository/TrafficLightRepository.java +++ b/qaup-collision/src/main/java/com/qaup/collision/common/model/repository/TrafficLightRepository.java @@ -167,18 +167,8 @@ public interface TrafficLightRepository extends JpaRepository findByDeviceNameContaining(@Param("name") String name); - - // ========== IP地址和端口相关查询方法 ========== - - /** - * 根据IP地址和端口查找设备 - * - * @param ipAddress IP地址 - * @param port 端口号 - * @return 红绿灯设备信息(可选) - */ - Optional findByIpAddressAndPort(String ipAddress, Integer port); + List findByDeviceNameContaining(@Param("name") String name); + /** * 根据IP地址查找设备列表 @@ -187,24 +177,7 @@ public interface TrafficLightRepository extends JpaRepository findByIpAddress(String ipAddress); - - /** - * 检查IP地址和端口组合是否已存在 - * - * @param ipAddress IP地址 - * @param port 端口号 - * @return true如果存在,false否则 - */ - boolean existsByIpAddressAndPort(String ipAddress, Integer port); - - /** - * 根据IP地址和端口查找激活的设备 - * - * @param ipAddress IP地址 - * @param port 端口号 - * @return 激活的红绿灯设备信息(可选) - */ - Optional findByIpAddressAndPortAndIsActiveTrue(String ipAddress, Integer port); + /** * 根据IP地址查找激活的设备列表 @@ -215,30 +188,28 @@ public interface TrafficLightRepository extends JpaRepository findByIpAddressAndIsActiveTrue(String ipAddress); /** - * 更新设备在线状态(基于IP地址和端口) + * 更新设备在线状态(基于IP地址) * * @param ipAddress IP地址 - * @param port 端口号 * @param isOnline 是否在线 * @return 更新的记录数 */ @Modifying @Transactional - @Query("UPDATE TrafficLight t SET t.isOnline = :isOnline, t.updatedTime = CURRENT_TIMESTAMP WHERE t.ipAddress = :ipAddress AND t.port = :port") - int updateOnlineStatusByIpAndPort(@Param("ipAddress") String ipAddress, @Param("port") Integer port, @Param("isOnline") Boolean isOnline); + @Query("UPDATE TrafficLight t SET t.isOnline = :isOnline, t.updatedTime = CURRENT_TIMESTAMP WHERE t.ipAddress = :ipAddress") + int updateOnlineStatusByIpAddress(@Param("ipAddress") String ipAddress, @Param("isOnline") Boolean isOnline); /** - * 更新设备心跳时间(基于IP地址和端口) + * 根据IP地址更新设备心跳时间(不考虑端口) * * @param ipAddress IP地址 - * @param port 端口号 * @param heartbeatTime 心跳时间 * @return 更新的记录数 */ @Modifying @Transactional - @Query("UPDATE TrafficLight t SET t.lastHeartbeat = :heartbeatTime, t.isOnline = true, t.updatedTime = CURRENT_TIMESTAMP WHERE t.ipAddress = :ipAddress AND t.port = :port") - int updateHeartbeatByIpAndPort(@Param("ipAddress") String ipAddress, @Param("port") Integer port, @Param("heartbeatTime") LocalDateTime heartbeatTime); + @Query("UPDATE TrafficLight t SET t.lastHeartbeat = :heartbeatTime, t.isOnline = true, t.updatedTime = CURRENT_TIMESTAMP WHERE t.ipAddress = :ipAddress") + int updateHeartbeatByIpAddress(@Param("ipAddress") String ipAddress, @Param("heartbeatTime") LocalDateTime heartbeatTime); /** * 查找指定IP地址范围内的设备 @@ -265,14 +236,6 @@ public interface TrafficLightRepository extends JpaRepository findDevicesWithoutDeviceId(); - /** - * 根据端口号查找设备 - * - * @param port 端口号 - * @return 使用该端口的设备列表 - */ - List findByPort(Integer port); - /** * 统计指定IP地址的设备数量 * diff --git a/qaup-collision/src/main/java/com/qaup/collision/common/model/spatial/TrafficLight.java b/qaup-collision/src/main/java/com/qaup/collision/common/model/spatial/TrafficLight.java index 8b10f5a7..041d0be3 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/common/model/spatial/TrafficLight.java +++ b/qaup-collision/src/main/java/com/qaup/collision/common/model/spatial/TrafficLight.java @@ -3,8 +3,6 @@ package com.qaup.collision.common.model.spatial; import jakarta.persistence.*; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Pattern; -import jakarta.validation.constraints.Min; -import jakarta.validation.constraints.Max; import lombok.Data; import lombok.NoArgsConstructor; import lombok.AllArgsConstructor; @@ -16,121 +14,110 @@ import java.time.LocalDateTime; /** * 红绿灯设备实体类 * - * 用于存储红绿灯设备的基本信息,包括设备编号、名称、IP地址、端口、关联路口等 + * 用于存储红绿灯设备的基本信息,包括设备编号、名称、IP地址、关联路口等 * 每个设备通过intersection_id与路口建立关联关系 - * 支持基于IP地址和端口的设备识别,设备编号(device_id)为可选字段 + * 支持基于IP地址的设备识别,设备编号(device_id)为可选字段 * - * @version 1.1 - 添加IP地址和端口支持 + * @version 1.1 - 添加IP地址支持 */ @Entity -@Table(name = "traffic_lights", - uniqueConstraints = { - @UniqueConstraint(name = "uk_traffic_light_ip_port", - columnNames = {"ip_address", "port"}) - }) +@Table(name = "traffic_lights", uniqueConstraints = { + @UniqueConstraint(name = "uk_traffic_light_ip", columnNames = { "ip_address" }) +}) @Data @NoArgsConstructor @AllArgsConstructor @Builder public class TrafficLight { - + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - + /** * 红绿灯设备唯一编号(可选) */ @Column(name = "device_id", unique = true, length = 50) private String deviceId; - + /** * 设备名称 */ @Column(name = "device_name", nullable = false, length = 100) private String deviceName; - + /** * 设备IP地址 */ @Column(name = "ip_address", nullable = false, length = 45) @NotBlank(message = "IP地址不能为空") - @Pattern(regexp = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", - message = "IP地址格式不正确") + @Pattern(regexp = "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", message = "IP地址格式不正确") @Builder.Default private String ipAddress = "0.0.0.0"; - - /** - * 设备端口号 - */ - @Column(name = "port") - @Min(value = 1, message = "端口号必须大于0") - @Max(value = 65535, message = "端口号不能超过65535") - private Integer port; - + /** * 关联的路口编号 */ @Column(name = "intersection_id", nullable = false, length = 50) private String intersectionId; - + /** * 设备类型 */ @Column(name = "device_type", length = 20) @Builder.Default private String deviceType = "STANDARD"; - + /** * 制造商 */ @Column(name = "manufacturer", length = 50) private String manufacturer; - + /** * 型号 */ @Column(name = "model", length = 50) private String model; - + /** * 安装日期 */ @Column(name = "install_date") private LocalDate installDate; - + /** * 是否在线 */ @Column(name = "is_online") @Builder.Default private Boolean isOnline = false; - + /** * 最后心跳时间 */ @Column(name = "last_heartbeat") private LocalDateTime lastHeartbeat; - + /** * 是否激活 */ @Column(name = "is_active") @Builder.Default private Boolean isActive = true; - + /** * 创建时间 */ @Column(name = "created_time") private LocalDateTime createdTime; - + /** * 更新时间 */ @Column(name = "updated_time") private LocalDateTime updatedTime; - + /** * 预设置创建和更新时间 */ @@ -140,19 +127,19 @@ public class TrafficLight { createdTime = now; updatedTime = now; } - + @PreUpdate protected void onUpdate() { updatedTime = LocalDateTime.now(); } - + /** * 更新设备心跳时间 */ public void updateHeartbeat() { this.lastHeartbeat = LocalDateTime.now(); } - + /** * 设置设备在线状态 * @@ -164,7 +151,7 @@ public class TrafficLight { updateHeartbeat(); } } - + /** * 检查设备是否可用(激活且在线) * @@ -173,7 +160,7 @@ public class TrafficLight { public boolean isAvailable() { return Boolean.TRUE.equals(isActive) && Boolean.TRUE.equals(isOnline); } - + /** * 检查设备心跳是否超时 * @@ -186,7 +173,7 @@ public class TrafficLight { } return lastHeartbeat.isBefore(LocalDateTime.now().minusMinutes(timeoutMinutes)); } - + /** * 获取设备状态描述 * @@ -204,10 +191,10 @@ public class TrafficLight { } return "正常"; } - + /** * 生成设备标识符 - * 当deviceId为空时,使用IP地址和端口组合作为标识符 + * 当deviceId为空时,使用IP地址作为标识符 * * @return 设备标识符 */ @@ -215,15 +202,12 @@ public class TrafficLight { if (deviceId != null && !deviceId.trim().isEmpty()) { return deviceId; } - if (ipAddress != null && port != null) { - return ipAddress + ":" + port; - } if (ipAddress != null) { return ipAddress; } return "UNKNOWN_DEVICE_" + id; } - + /** * 检查IP地址是否有效 * @@ -250,31 +234,19 @@ public class TrafficLight { return false; } } - - /** - * 检查端口号是否有效 - * - * @return true如果端口号有效,false否则 - */ - public boolean isValidPort() { - return port != null && port > 0 && port <= 65535; - } - + /** * 获取网络地址信息 * - * @return IP地址和端口的组合字符串 + * @return IP地址字符串 */ public String getNetworkAddress() { - if (ipAddress != null && port != null) { - return ipAddress + ":" + port; - } if (ipAddress != null) { return ipAddress; } return "未知地址"; } - + /** * 检查是否为默认IP地址 * diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/server/TrafficLightTcpServer.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/server/TrafficLightTcpServer.java index 64554b7f..b646c115 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/server/TrafficLightTcpServer.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/server/TrafficLightTcpServer.java @@ -316,13 +316,12 @@ public class TrafficLightTcpServer { // 只处理包含DI信号的消息 if (message.contains("\"DI-")) { - // 提取客户端IP地址和端口 + // 提取客户端IP地址(不使用端口,因为客户端端口会变化) String ipAddress = clientSocket.getInetAddress().getHostAddress(); - int port = clientSocket.getPort(); - // 委托给信号处理器处理,传递网络地址信息 - signalHandler.handleSignal(clientId, message, ipAddress, port); - log.debug("🚦 处理DI信号: clientId={}, ip={}, port={}", clientId, ipAddress, port); + // 委托给信号处理器处理,传递网络地址信息(端口设为null) + signalHandler.handleSignal(clientId, message, ipAddress, null); + log.debug("🚦 处理DI信号: clientId={}, ip={}", clientId, ipAddress); } else { log.debug("🚦 忽略非DI信号消息: clientId={}, message={}", clientId, message); } diff --git a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/TrafficLightDataCollector.java b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/TrafficLightDataCollector.java index 290ed659..7bc6da8e 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/TrafficLightDataCollector.java +++ b/qaup-collision/src/main/java/com/qaup/collision/datacollector/service/TrafficLightDataCollector.java @@ -61,20 +61,20 @@ public class TrafficLightDataCollector implements TrafficLightSignalHandler { // 记录调试日志 if (trafficLightProperties.getProcessing().isEnableDebugLog()) { - log.debug("处理红绿灯信号: clientId={}, signal={}, ip={}, port={}", - clientId, signal, ipAddress, port); + log.debug("处理红绿灯信号: clientId={}, signal={}, ip={}", + clientId, signal, ipAddress); } - // 委托给数据处理服务处理,传递网络地址信息 - dataProcessingService.processTrafficLightSignalWithAddress(signal.trim(), ipAddress, port); + // 委托给数据处理服务处理,传递网络地址信息(端口设为固定值8082) + dataProcessingService.processTrafficLightSignalWithAddress(signal.trim(), ipAddress, 8082); validSignalsProcessed.incrementAndGet(); - log.debug("🚦 成功处理红绿灯信号: clientId={}, ip={}, port={}", clientId, ipAddress, port); + log.debug("🚦 成功处理红绿灯信号: clientId={}, ip={}", clientId, ipAddress); } catch (Exception e) { - log.error("🚦 处理红绿灯信号异常: clientId={}, signal={}, ip={}, port={}", - clientId, signal, ipAddress, port, e); + log.error("🚦 处理红绿灯信号异常: clientId={}, signal={}, ip={}", + clientId, signal, ipAddress, e); processingErrors.incrementAndGet(); } } diff --git a/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/DataProcessingService.java b/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/DataProcessingService.java index df9ee0ac..51577347 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/DataProcessingService.java +++ b/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/DataProcessingService.java @@ -50,40 +50,40 @@ import java.math.RoundingMode; @Slf4j @Service public class DataProcessingService { - + @Value("${data.collector.disabled:false}") private boolean collectorDisabled; - + @Autowired private SpeedCalculationService speedCalculationService; - + @Autowired private PathConflictDetectionService pathConflictDetectionService; - + @Autowired private ApplicationEventPublisher eventPublisher; - + @Autowired private VehicleDataPersistenceService vehicleDataPersistenceService; - + @Autowired private QuapDataAdapter quapDataAdapter; - + @Autowired private com.qaup.collision.rule.service.RuleExecutionEngine ruleExecutionEngine; - + private final GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), 4326); - + // 从DataCollectorService获取缓存的引用 private Map activeMovingObjectsCache; - + /** * 设置活跃对象缓存的引用(由DataCollectorService调用) */ public void setActiveMovingObjectsCache(Map cache) { this.activeMovingObjectsCache = cache; } - + /** * 周期性数据处理任务 * @@ -101,35 +101,35 @@ public class DataProcessingService { if (collectorDisabled) { return; } - + if (activeMovingObjectsCache == null || activeMovingObjectsCache.isEmpty()) { log.debug("活跃对象缓存为空,跳过数据处理"); return; } - + log.info("开始周期性数据处理,活跃对象数量: {}", activeMovingObjectsCache.size()); - + // 获取所有活跃对象的快照 List currentActiveObjects = new ArrayList<>(activeMovingObjectsCache.values()); - + // 第一步:计算速度和方向 calculateSpeedAndDirectionForAllObjects(currentActiveObjects); - + // 第二步:发送WebSocket位置更新消息 sendPositionUpdatesForActiveObjects(currentActiveObjects); - + // 第三步:执行路径冲突检测 pathConflictDetectionService.detectPathConflicts(currentActiveObjects); - + // 第四步:执行违规检测 performViolationDetection(currentActiveObjects); - + // 第五步:保存无人车数据到数据库 saveUnmannedVehicleDataPeriodically(currentActiveObjects); - + log.info("周期性数据处理完成"); } - + /** * 为所有缓存对象计算速度和方向 */ @@ -137,28 +137,26 @@ public class DataProcessingService { if (activeObjects.isEmpty()) { return; } - + long currentTime = System.currentTimeMillis(); log.debug("开始为 {} 个对象计算速度和方向", activeObjects.size()); - + for (MovingObject movingObject : activeObjects) { try { // 计算实时速度 Double calculatedSpeed = speedCalculationService.calculateRealtimeSpeed( - movingObject.getObjectId(), - movingObject.getCurrentPosition().getY(), // latitude - movingObject.getCurrentPosition().getX(), // longitude - currentTime - ); - + movingObject.getObjectId(), + movingObject.getCurrentPosition().getY(), // latitude + movingObject.getCurrentPosition().getX(), // longitude + currentTime); + // 计算实时方向 Double calculatedDirection = speedCalculationService.calculateRealtimeDirection( - movingObject.getObjectId(), - movingObject.getCurrentPosition().getY(), // latitude - movingObject.getCurrentPosition().getX(), // longitude - currentTime - ); - + movingObject.getObjectId(), + movingObject.getCurrentPosition().getY(), // latitude + movingObject.getCurrentPosition().getX(), // longitude + currentTime); + // 处理速度值 - 如果计算返回null,使用合理默认值 Double finalSpeed = calculatedSpeed; if (finalSpeed == null) { @@ -176,36 +174,36 @@ public class DataProcessingService { default: finalSpeed = 0.0; } - log.debug("对象 {} 速度计算返回null,使用默认值: {} km/h", - movingObject.getObjectId(), finalSpeed); + log.debug("对象 {} 速度计算返回null,使用默认值: {} km/h", + movingObject.getObjectId(), finalSpeed); } - - // 处理方向值 - 如果计算返回null,使用合理默认值 + + // 处理方向值 - 如果计算返回null,使用合理默认值 Double finalDirection = calculatedDirection; if (finalDirection == null) { finalDirection = 0.0; // 默认朝北 - log.debug("对象 {} 方向计算返回null,使用默认值: 0.0度", - movingObject.getObjectId()); + log.debug("对象 {} 方向计算返回null,使用默认值: 0.0度", + movingObject.getObjectId()); } - + // 更新MovingObject的速度和方向 movingObject.setCurrentSpeed(finalSpeed); movingObject.setCurrentHeading(finalDirection); - + // 更新缓存中的对象 activeMovingObjectsCache.put(movingObject.getObjectId(), movingObject); - - log.debug("对象 {} 计算完成: 速度={} km/h, 方向={}度", - movingObject.getObjectId(), finalSpeed, finalDirection); - + + log.debug("对象 {} 计算完成: 速度={} km/h, 方向={}度", + movingObject.getObjectId(), finalSpeed, finalDirection); + } catch (Exception e) { log.error("计算对象 {} 的速度和方向时发生异常", movingObject.getObjectId(), e); } } - + log.debug("所有对象的速度和方向计算完成"); } - + /** * 发送WebSocket位置更新消息 */ @@ -214,10 +212,10 @@ public class DataProcessingService { log.debug("没有活跃对象,跳过位置更新消息发送"); return; } - + long currentTime = System.currentTimeMillis(); log.debug("发送位置更新消息,对象数量: {}", activeObjects.size()); - + for (MovingObject movingObject : activeObjects) { try { // 创建位置更新消息负载 @@ -225,7 +223,7 @@ public class DataProcessingService { .latitude(movingObject.getCurrentPosition().getY()) .longitude(movingObject.getCurrentPosition().getX()) .build(); - + // 处理速度值 - 如果为null,使用合理的默认值而不是0 Double finalSpeed = movingObject.getCurrentSpeed(); if (finalSpeed == null) { @@ -245,13 +243,13 @@ public class DataProcessingService { finalSpeed = 0.0; } } - + // 处理方向值 - 如果为null,使用合理的默认值而不是0 Double finalHeading = movingObject.getCurrentHeading(); if (finalHeading == null) { finalHeading = 0.0; // 默认朝北 } - + PositionUpdatePayload payload = PositionUpdatePayload.builder() .objectId(movingObject.getObjectId()) .objectType(movingObject.getObjectType().name()) @@ -260,25 +258,25 @@ public class DataProcessingService { .speed(new BigDecimal(finalSpeed).setScale(2, RoundingMode.HALF_UP).doubleValue()) .timestamp(currentTime) .build(); - + // 发布WebSocket事件 eventPublisher.publishEvent(new PositionUpdateEvent(payload)); - - log.debug("发送位置更新: {} ({}), 位置: ({}, {}), 速度: {}", - movingObject.getObjectId(), - movingObject.getObjectType(), - movingObject.getCurrentPosition().getX(), - movingObject.getCurrentPosition().getY(), - movingObject.getCurrentSpeed()); - + + log.debug("发送位置更新: {} ({}), 位置: ({}, {}), 速度: {}", + movingObject.getObjectId(), + movingObject.getObjectType(), + movingObject.getCurrentPosition().getX(), + movingObject.getCurrentPosition().getY(), + movingObject.getCurrentSpeed()); + } catch (Exception e) { log.error("发送位置更新消息失败: objectId={}", movingObject.getObjectId(), e); } } - + log.info("位置更新消息发送完成,发送数量: {}", activeObjects.size()); } - + /** * 执行违规检测 */ @@ -301,15 +299,14 @@ public class DataProcessingService { vehicleState.put("speed", vehicleLocation.getSpeed()); vehicleState.put("altitude", vehicleLocation.getAltitude()); vehicleState.put("heading", vehicleLocation.getHeading()); - + // 统一调用规则执行引擎进行所有类型的违规检测(速度、区域访问、电子围栏等) ruleExecutionEngine.detectAllViolations( - String.valueOf(vehicleLocation.getVehicleId()), - vehicleLocation.getVehicleType(), - vehicleLocation.getLocation(), - vehicleState, - vehicleLocation.getTimestamp() - ); + String.valueOf(vehicleLocation.getVehicleId()), + vehicleLocation.getVehicleType(), + vehicleLocation.getLocation(), + vehicleState, + vehicleLocation.getTimestamp()); } } catch (Exception e) { log.error("车辆 {} 违规检测失败: {}", vehicleLocation.getVehicleId(), e.getMessage()); @@ -319,7 +316,7 @@ public class DataProcessingService { log.debug("没有可用于违规检测的车辆位置数据"); } } - + /** * 周期性保存无人车位置数据到数据库 */ @@ -328,37 +325,37 @@ public class DataProcessingService { log.debug("没有活跃对象,跳过数据保存"); return; } - + // 过滤出无人车对象 List unmannedVehicles = activeObjects.stream() .filter(obj -> obj.getObjectType() == MovingObjectType.UNMANNED_VEHICLE) .collect(Collectors.toList()); - + if (unmannedVehicles.isEmpty()) { log.debug("没有无人车数据,跳过数据保存"); return; } - + log.debug("开始周期性保存无人车位置数据,数量: {}", unmannedVehicles.size()); - + // 将MovingObject转换为VehicleLocation并保存 List vehicleLocations = new ArrayList<>(); for (MovingObject unmannedVehicle : unmannedVehicles) { try { // 创建一个临时的UnmannedVehicle对象用于转换 UnmannedVehicle tempUnmannedVehicle = UnmannedVehicle.builder() - .objectId(unmannedVehicle.getObjectId()) - .objectName(unmannedVehicle.getObjectName()) - .currentPosition(unmannedVehicle.getCurrentPosition()) - .currentSpeed(unmannedVehicle.getCurrentSpeed()) - .currentHeading(unmannedVehicle.getCurrentHeading()) - .altitude(unmannedVehicle.getAltitude()) - .objectType(MovingObjectType.UNMANNED_VEHICLE) - .build(); - + .objectId(unmannedVehicle.getObjectId()) + .objectName(unmannedVehicle.getObjectName()) + .currentPosition(unmannedVehicle.getCurrentPosition()) + .currentSpeed(unmannedVehicle.getCurrentSpeed()) + .currentHeading(unmannedVehicle.getCurrentHeading()) + .altitude(unmannedVehicle.getAltitude()) + .objectType(MovingObjectType.UNMANNED_VEHICLE) + .build(); + // 转换无人车数据为VehicleLocation VehicleLocation vehicleLocation = convertToVehicleLocation(tempUnmannedVehicle); - + if (vehicleLocation != null) { vehicleLocations.add(vehicleLocation); } @@ -366,7 +363,7 @@ public class DataProcessingService { log.error("转换无人车数据失败: objectId={}", unmannedVehicle.getObjectId(), e); } } - + // 批量保存到数据库 if (!vehicleLocations.isEmpty()) { try { @@ -379,7 +376,7 @@ public class DataProcessingService { log.debug("没有有效的无人车位置数据可保存"); } } - + /** * 将 UnmannedVehicle 转换为 VehicleLocation 实体用于持久化 */ @@ -390,7 +387,8 @@ public class DataProcessingService { } try { // 根据无人车的 objectId (车牌号/外部ID) 查询其在 sys_vehicle_info 表中的 Long 类型 vehicleId - Optional vehicleInfoOptional = quapDataAdapter.findVehicleByLicensePlate(vehicle.getObjectId()); + Optional vehicleInfoOptional = quapDataAdapter + .findVehicleByLicensePlate(vehicle.getObjectId()); if (vehicleInfoOptional.isEmpty()) { log.warn("未找到无人车 {} 对应的系统车辆信息,跳过持久化。这可能意味着该无人车未在系统中注册。", vehicle.getObjectId()); @@ -401,11 +399,13 @@ public class DataProcessingService { return VehicleLocation.builder() .vehicleId(sysVehicleInfo.getVehicleId()) // 使用从 sys_vehicle_info 获取的 Long 类型 vehicleId - .location(geometryFactory.createPoint(new org.locationtech.jts.geom.Coordinate(vehicle.getCurrentPosition().getX(), vehicle.getCurrentPosition().getY()))) + .location(geometryFactory.createPoint(new org.locationtech.jts.geom.Coordinate( + vehicle.getCurrentPosition().getX(), vehicle.getCurrentPosition().getY()))) .altitude(vehicle.getAltitude()) .speed(vehicle.getCurrentSpeed() != null ? vehicle.getCurrentSpeed() : 0.0) .heading(vehicle.getCurrentHeading()) - .timestamp(java.time.LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, java.time.ZoneOffset.UTC)) + .timestamp(java.time.LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, + java.time.ZoneOffset.UTC)) .licensePlate(sysVehicleInfo.getLicensePlate()) // 设置运行时属性 licensePlate .vehicleType(MovingObjectType.UNMANNED_VEHICLE) // 从无人车接口获取的数据,直接设置为无人车类型 .build(); @@ -414,7 +414,7 @@ public class DataProcessingService { return null; } } - + /** * 创建临时VehicleLocation用于违规检测(航空器和机场车辆) * 对于不持久化的MovingObject,生成一个临时的VehicleLocation用于RealTimeViolationDetector @@ -431,7 +431,8 @@ public class DataProcessingService { if (MovingObjectType.UNMANNED_VEHICLE.equals(movingObject.getObjectType())) { // 对于无人车,其objectId就是车牌号,可以从sys_vehicle_info中查到vehicleId - Optional vehicleInfoOptional = quapDataAdapter.findVehicleByLicensePlate(movingObject.getObjectId()); + Optional vehicleInfoOptional = quapDataAdapter + .findVehicleByLicensePlate(movingObject.getObjectId()); if (vehicleInfoOptional.isEmpty()) { log.warn("未找到无人车 {} 对应的系统车辆信息,跳过违规检测。这可能意味着该无人车未在系统中注册。", movingObject.getObjectId()); return null; @@ -452,15 +453,18 @@ public class DataProcessingService { return null; } - log.debug("在createTemporaryVehicleLocationForDetection方法中,准备创建VehicleLocation,MovingObject的速度为: {}", movingObject.getCurrentSpeed()); + log.debug("在createTemporaryVehicleLocationForDetection方法中,准备创建VehicleLocation,MovingObject的速度为: {}", + movingObject.getCurrentSpeed()); return VehicleLocation.builder() .vehicleId(vehicleIdForDetection) - .location(geometryFactory.createPoint(new org.locationtech.jts.geom.Coordinate(currentPosition.getX(), currentPosition.getY()))) + .location(geometryFactory.createPoint( + new org.locationtech.jts.geom.Coordinate(currentPosition.getX(), currentPosition.getY()))) .altitude(movingObject.getAltitude() != null ? movingObject.getAltitude() : 0.0) .speed(movingObject.getCurrentSpeed() != null ? movingObject.getCurrentSpeed() : 0.0) .heading(movingObject.getCurrentHeading() != null ? movingObject.getCurrentHeading() : 0.0) - .timestamp(java.time.LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, java.time.ZoneOffset.UTC)) // 使用当前时间作为检测时间戳 + .timestamp(java.time.LocalDateTime.ofEpochSecond(System.currentTimeMillis() / 1000, 0, + java.time.ZoneOffset.UTC)) // 使用当前时间作为检测时间戳 .licensePlate(licensePlateForDetection) .vehicleType(objectTypeForDetection) .build(); @@ -469,96 +473,96 @@ public class DataProcessingService { return null; } } - + // ============================================ // 红绿灯信号处理方法(支持IP地址和端口信息) // ============================================ - + @Autowired private TrafficLightSignalParser trafficLightSignalParser; - + @Autowired private TrafficLightService trafficLightService; - + @Autowired private IntersectionService intersectionService; - + /** * 处理红绿灯信号数据(新格式:分离的JSON数据和网络连接信息) * * @param jsonSignal 纯JSON信号数据 - * @param ipAddress 从socket连接中获取的IP地址 - * @param port 从socket连接中获取的端口号 + * @param ipAddress 从socket连接中获取的IP地址 + * @param port 从socket连接中获取的端口号 */ public void processTrafficLightSignalWithAddress(String jsonSignal, String ipAddress, Integer port) { if (jsonSignal == null || jsonSignal.trim().isEmpty()) { log.warn("🚦 接收到空的红绿灯JSON信号"); return; } - + if (ipAddress == null || ipAddress.trim().isEmpty()) { log.warn("🚦 IP地址不能为空"); return; } - + if (port == null || port <= 0 || port > 65535) { log.warn("🚦 端口号无效: {}", port); return; } - + try { // 使用新的解析器处理分离的JSON数据和网络信息 - com.qaup.collision.dataprocessing.model.TrafficLightStatus status = - trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port); - - log.debug("🚦 使用分离格式解析器处理信号: ipAddress={}, port={}, json={}", - ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length()))); - + com.qaup.collision.dataprocessing.model.TrafficLightStatus status = trafficLightSignalParser + .parseSignalWithAddress(jsonSignal, ipAddress, port); + + log.debug("🚦 使用分离格式解析器处理信号: ipAddress={}, port={}, json={}", + ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length()))); + if (!status.isValid()) { log.warn("🚦 解析的红绿灯状态无效: {}", status); return; } - + // 获取或创建设备 com.qaup.collision.common.model.spatial.TrafficLight device = getOrCreateDevice(status); if (device == null) { log.warn("🚦 无法获取或创建红绿灯设备"); return; } - + // 更新设备心跳和在线状态 updateDeviceStatusByAddress(status, device); - + // 设置路口ID status.setIntersectionId(device.getIntersectionId()); - + // 获取路口信息 - Optional intersectionOpt = - intersectionService.getActiveIntersectionById(device.getIntersectionId()); - + Optional intersectionOpt = intersectionService + .getActiveIntersectionById(device.getIntersectionId()); + if (intersectionOpt.isEmpty()) { log.warn("🚦 无法获取路口信息: intersectionId={}", device.getIntersectionId()); return; } - + com.qaup.collision.common.model.spatial.Intersection intersection = intersectionOpt.get(); - + // 创建WebSocket消息载荷 - com.qaup.collision.websocket.message.TrafficLightStatusPayload payload = - createTrafficLightPayload(status, device, intersection); - + com.qaup.collision.websocket.message.TrafficLightStatusPayload payload = createTrafficLightPayload(status, + device, intersection); + // 发布红绿灯状态变更事件 publishTrafficLightStatusEvent(payload); - - log.debug("🚦 成功处理红绿灯信号: deviceIdentifier={}, intersectionId={}, 状态={}", + + log.debug("🚦 成功处理红绿灯信号: deviceIdentifier={}, intersectionId={}, 状态={}", status.generateDeviceIdentifier(), status.getIntersectionId(), status.getStatusDescription()); - + } catch (Exception e) { - log.error("🚦 处理红绿灯信号异常: ipAddress={}, port={}, json={}", - ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length())), e); + log.error("🚦 处理红绿灯信号异常: ipAddress={}, port={}, json={}", + ipAddress, port, jsonSignal.substring(0, Math.min(100, jsonSignal.length())), e); } } - + /** * 处理红绿灯信号数据(支持包含IP地址和端口信息的新格式) * @@ -569,10 +573,10 @@ public class DataProcessingService { log.warn("🚦 接收到空的红绿灯信号"); return; } - + try { com.qaup.collision.dataprocessing.model.TrafficLightStatus status; - + // 只支持传统JSON格式(向后兼容) if (trafficLightSignalParser.isValidSignal(rawMessage)) { // 使用传统JSON格式解析器处理信号(向后兼容) @@ -582,51 +586,51 @@ public class DataProcessingService { log.warn("🚦 无法识别的红绿灯信号格式: {}", rawMessage.substring(0, Math.min(100, rawMessage.length()))); return; } - + if (!status.isValid()) { log.warn("🚦 解析的红绿灯状态无效: {}", status); return; } - + // 获取或创建设备 com.qaup.collision.common.model.spatial.TrafficLight device = getOrCreateDevice(status); if (device == null) { log.warn("🚦 无法获取或创建红绿灯设备"); return; } - + // 更新设备心跳和在线状态 updateDeviceStatusByAddress(status, device); - + // 设置路口ID status.setIntersectionId(device.getIntersectionId()); - + // 获取路口信息 - Optional intersectionOpt = - intersectionService.getActiveIntersectionById(device.getIntersectionId()); - + Optional intersectionOpt = intersectionService + .getActiveIntersectionById(device.getIntersectionId()); + if (intersectionOpt.isEmpty()) { log.warn("🚦 无法获取路口信息: intersectionId={}", device.getIntersectionId()); return; } - + com.qaup.collision.common.model.spatial.Intersection intersection = intersectionOpt.get(); - + // 创建WebSocket消息载荷 - com.qaup.collision.websocket.message.TrafficLightStatusPayload payload = - createTrafficLightPayload(status, device, intersection); - + com.qaup.collision.websocket.message.TrafficLightStatusPayload payload = createTrafficLightPayload(status, + device, intersection); + // 发布红绿灯状态变更事件 publishTrafficLightStatusEvent(payload); - - log.debug("🚦 成功处理红绿灯信号: deviceIdentifier={}, intersectionId={}, 状态={}", + + log.debug("🚦 成功处理红绿灯信号: deviceIdentifier={}, intersectionId={}, 状态={}", status.generateDeviceIdentifier(), status.getIntersectionId(), status.getStatusDescription()); - + } catch (Exception e) { log.error("🚦 处理红绿灯信号异常: signal={}", rawMessage.substring(0, Math.min(100, rawMessage.length())), e); } } - + /** * 获取或创建红绿灯设备 * 优先基于IP地址和端口查找,如果不存在则自动创建 @@ -636,61 +640,60 @@ public class DataProcessingService { */ private com.qaup.collision.common.model.spatial.TrafficLight getOrCreateDevice( com.qaup.collision.dataprocessing.model.TrafficLightStatus status) { - + try { - // 如果有IP地址和端口信息,优先使用IP地址查找 - if (status.hasNetworkAddress() && status.getPort() != null) { - // 尝试根据IP地址和端口查找现有设备 - Optional deviceOpt = - trafficLightService.findDeviceByAddress(status.getIpAddress(), status.getPort()); - + // 如果有IP地址信息,使用IP地址查找(不使用端口) + if (status.hasNetworkAddress()) { + // 尝试根据IP地址查找现有设备 + Optional deviceOpt = trafficLightService + .findDeviceByAddress(status.getIpAddress()); + if (deviceOpt.isPresent()) { - log.debug("🚦 找到现有设备: ipAddress={}, port={}", status.getIpAddress(), status.getPort()); + log.debug("🚦 找到现有设备: ipAddress={}", status.getIpAddress()); return deviceOpt.get(); } - + // 设备不存在,尝试自动创建(需要默认路口ID) String defaultIntersectionId = getDefaultIntersectionId(); if (defaultIntersectionId != null) { try { - com.qaup.collision.common.model.spatial.TrafficLight newDevice = - trafficLightService.getOrCreateDeviceByAddress( - status.getIpAddress(), - status.getPort(), + com.qaup.collision.common.model.spatial.TrafficLight newDevice = trafficLightService + .getOrCreateDeviceByAddress( + status.getIpAddress(), defaultIntersectionId); - - log.info("🚦 自动创建新设备: ipAddress={}, port={}, intersectionId={}", - status.getIpAddress(), status.getPort(), defaultIntersectionId); + + log.info("🚦 自动创建新设备: ipAddress={}, intersectionId={}", + status.getIpAddress(), defaultIntersectionId); return newDevice; } catch (Exception e) { - log.error("🚦 自动创建设备失败: ipAddress={}, port={}", - status.getIpAddress(), status.getPort(), e); + log.error("🚦 自动创建设备失败: ipAddress={}", + status.getIpAddress(), e); } } } - + // 如果有设备ID,尝试使用设备ID查找(向后兼容) if (status.hasDeviceId()) { - Optional deviceOpt = - trafficLightService.getActiveTrafficLightByDeviceId(status.getDeviceId()); - + Optional deviceOpt = trafficLightService + .getActiveTrafficLightByDeviceId(status.getDeviceId()); + if (deviceOpt.isPresent()) { log.debug("🚦 通过设备ID找到设备: deviceId={}", status.getDeviceId()); return deviceOpt.get(); } } - - log.warn("🚦 无法找到或创建设备: deviceId={}, ipAddress={}, port={}", + + log.warn("🚦 无法找到或创建设备: deviceId={}, ipAddress={}, port={}", status.getDeviceId(), status.getIpAddress(), status.getPort()); return null; - + } catch (Exception e) { - log.error("🚦 获取或创建设备异常: deviceId={}, ipAddress={}, port={}", + log.error("🚦 获取或创建设备异常: deviceId={}, ipAddress={}, port={}", status.getDeviceId(), status.getIpAddress(), status.getPort(), e); return null; } } - + /** * 获取默认路口ID * 可以从配置文件读取或使用固定值 @@ -709,7 +712,7 @@ public class DataProcessingService { return null; } } - + /** * 检查设备是否存在且激活(不检查在线状态) * @@ -720,14 +723,14 @@ public class DataProcessingService { if (deviceId == null || deviceId.trim().isEmpty()) { return false; } - + // 只检查设备是否存在且激活,不检查在线状态 - Optional deviceOpt = - trafficLightService.getActiveTrafficLightByDeviceId(deviceId); - + Optional deviceOpt = trafficLightService + .getActiveTrafficLightByDeviceId(deviceId); + return deviceOpt.isPresent(); } - + /** * 更新设备心跳和在线状态(支持基于IP地址和端口的更新) * @@ -737,83 +740,49 @@ public class DataProcessingService { private void updateDeviceStatusByAddress( com.qaup.collision.dataprocessing.model.TrafficLightStatus status, com.qaup.collision.common.model.spatial.TrafficLight device) { - + try { boolean updated = false; - - // 优先使用IP地址和端口更新 - if (status.hasNetworkAddress() && status.getPort() != null) { + + // 优先使用IP地址更新(不使用端口) + if (status.hasNetworkAddress()) { // 更新设备心跳时间 boolean heartbeatUpdated = trafficLightService.updateDeviceHeartbeatByAddress( - status.getIpAddress(), status.getPort()); - - // 设置设备为在线状态 - boolean onlineUpdated = trafficLightService.updateDeviceOnlineStatusByAddress( - status.getIpAddress(), status.getPort(), true); - + status.getIpAddress()); + + // 设置设备为在线状态(使用IP地址) + boolean onlineUpdated = trafficLightService.updateDeviceOnlineStatusByIpAddress( + status.getIpAddress(), true); + updated = heartbeatUpdated && onlineUpdated; - + if (updated) { - log.debug("🚦 通过IP地址更新设备状态成功: ipAddress={}, port={} (在线)", - status.getIpAddress(), status.getPort()); + log.debug("🚦 通过IP地址更新设备状态成功: ipAddress={} (在线)", + status.getIpAddress()); } else { - log.warn("🚦 通过IP地址更新设备状态失败: ipAddress={}, port={}", - status.getIpAddress(), status.getPort()); + log.warn("🚦 通过IP地址更新设备状态失败: ipAddress={}", + status.getIpAddress()); } } - - // 如果IP地址更新失败,且有设备ID,则尝试使用设备ID更新(向后兼容) - if (!updated && status.hasDeviceId()) { - // 更新设备心跳时间 - boolean heartbeatUpdated = trafficLightService.updateDeviceHeartbeat(status.getDeviceId()); - - // 设置设备为在线状态 - boolean onlineUpdated = trafficLightService.updateDeviceOnlineStatus(status.getDeviceId(), true); - - updated = heartbeatUpdated && onlineUpdated; - - if (updated) { - log.debug("🚦 通过设备ID更新设备状态成功: deviceId={} (在线)", status.getDeviceId()); - } else { - log.warn("🚦 通过设备ID更新设备状态失败: deviceId={}", status.getDeviceId()); - } - } - + + // 统一使用IP地址进行更新,不再使用设备ID作为备选方案 + if (!updated) { - log.warn("🚦 无法更新设备状态: deviceId={}, ipAddress={}, port={}", + log.warn("🚦 无法更新设备状态: deviceId={}, ipAddress={}, port={}", status.getDeviceId(), status.getIpAddress(), status.getPort()); } - + } catch (Exception e) { - log.error("🚦 更新设备状态异常: deviceId={}, ipAddress={}, port={}", - status.getDeviceId(), status.getIpAddress(), status.getPort(), e); + log.error("🚦 更新设备状态异常: deviceId={}, ipAddress={}, port={}", + status.getDeviceId(), status.getIpAddress(), status.getPort(), e); } } - - /** - * 更新设备心跳和在线状态(兼容旧方法) - * - * @param deviceId 设备编号 - */ - private void updateDeviceStatus(String deviceId) { - try { - // 更新设备心跳时间 - trafficLightService.updateDeviceHeartbeat(deviceId); - - // 设置设备为在线状态 - trafficLightService.updateDeviceOnlineStatus(deviceId, true); - - log.debug("🚦 更新设备状态成功: deviceId={} (在线)", deviceId); - } catch (Exception e) { - log.warn("🚦 更新设备状态失败: deviceId={}", deviceId, e); - } - } - + /** * 创建WebSocket消息载荷 * - * @param status 红绿灯状态 - * @param device 设备信息 + * @param status 红绿灯状态 + * @param device 设备信息 * @param intersection 路口信息 * @return WebSocket消息载荷 */ @@ -821,18 +790,18 @@ public class DataProcessingService { com.qaup.collision.dataprocessing.model.TrafficLightStatus status, com.qaup.collision.common.model.spatial.TrafficLight device, com.qaup.collision.common.model.spatial.Intersection intersection) { - + // 创建位置信息 - com.qaup.collision.websocket.message.TrafficLightStatusPayload.Position position = - com.qaup.collision.websocket.message.TrafficLightStatusPayload.Position.builder() - .latitude(intersection.getLatitude()) - .longitude(intersection.getLongitude()) - .build(); - + com.qaup.collision.websocket.message.TrafficLightStatusPayload.Position position = com.qaup.collision.websocket.message.TrafficLightStatusPayload.Position + .builder() + .latitude(intersection.getLatitude()) + .longitude(intersection.getLongitude()) + .build(); + // 使用设备的标识符,优先使用设备ID,如果没有则使用生成的标识符 - String deviceIdentifier = device.getDeviceId() != null ? - device.getDeviceId() : device.generateDeviceIdentifier(); - + String deviceIdentifier = device.getDeviceId() != null ? device.getDeviceId() + : device.generateDeviceIdentifier(); + return com.qaup.collision.websocket.message.TrafficLightStatusPayload.builder() .intersectionId(intersection.getIntersectionId()) .intersectionName(intersection.getIntersectionName()) @@ -846,21 +815,22 @@ public class DataProcessingService { // 如果WebSocket消息支持,可以添加IP地址和端口信息 .build(); } - + /** * 发布红绿灯状态变更事件 * * @param payload WebSocket消息载荷 */ - private void publishTrafficLightStatusEvent(com.qaup.collision.websocket.message.TrafficLightStatusPayload payload) { + private void publishTrafficLightStatusEvent( + com.qaup.collision.websocket.message.TrafficLightStatusPayload payload) { try { - com.qaup.collision.websocket.event.TrafficLightStatusEvent event = - new com.qaup.collision.websocket.event.TrafficLightStatusEvent(payload); - + com.qaup.collision.websocket.event.TrafficLightStatusEvent event = new com.qaup.collision.websocket.event.TrafficLightStatusEvent( + payload); + eventPublisher.publishEvent(event); - + log.debug("🚦 发布红绿灯状态事件成功: intersectionId={}", payload.getIntersectionId()); - + } catch (Exception e) { log.error("🚦 发布红绿灯状态事件失败: intersectionId={}", payload.getIntersectionId(), e); } diff --git a/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/TrafficLightService.java b/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/TrafficLightService.java index 8eabd78e..fe8399ed 100644 --- a/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/TrafficLightService.java +++ b/qaup-collision/src/main/java/com/qaup/collision/dataprocessing/service/TrafficLightService.java @@ -24,16 +24,16 @@ import java.util.Optional; @Slf4j @Service public class TrafficLightService { - + @Autowired private TrafficLightRepository trafficLightRepository; - + @Autowired private IntersectionService intersectionService; - + // 心跳超时时间(分钟) private static final int HEARTBEAT_TIMEOUT_MINUTES = 5; - + /** * 根据设备编号获取红绿灯设备信息 * @@ -45,7 +45,7 @@ public class TrafficLightService { log.warn("设备编号不能为空"); return Optional.empty(); } - + try { return trafficLightRepository.findByDeviceId(deviceId.trim()); } catch (Exception e) { @@ -53,7 +53,7 @@ public class TrafficLightService { return Optional.empty(); } } - + /** * 根据设备编号获取激活的红绿灯设备信息 * @@ -65,7 +65,7 @@ public class TrafficLightService { log.warn("设备编号不能为空"); return Optional.empty(); } - + try { return trafficLightRepository.findByDeviceIdAndIsActive(deviceId.trim(), true); } catch (Exception e) { @@ -73,7 +73,7 @@ public class TrafficLightService { return Optional.empty(); } } - + /** * 根据路口编号获取红绿灯设备列表 * @@ -85,7 +85,7 @@ public class TrafficLightService { log.warn("路口编号不能为空"); return List.of(); } - + try { List devices = trafficLightRepository.findByIntersectionId(intersectionId.trim()); log.debug("路口 {} 查询到 {} 个红绿灯设备", intersectionId, devices.size()); @@ -95,7 +95,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 根据路口编号获取激活的红绿灯设备列表 * @@ -107,9 +107,10 @@ public class TrafficLightService { log.warn("路口编号不能为空"); return List.of(); } - + try { - List devices = trafficLightRepository.findByIntersectionIdAndIsActiveTrue(intersectionId.trim()); + List devices = trafficLightRepository + .findByIntersectionIdAndIsActiveTrue(intersectionId.trim()); log.debug("路口 {} 查询到 {} 个激活的红绿灯设备", intersectionId, devices.size()); return devices; } catch (Exception e) { @@ -117,7 +118,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 获取所有在线的设备 * @@ -133,7 +134,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 获取所有激活且在线的设备 * @@ -149,34 +150,34 @@ public class TrafficLightService { return List.of(); } } - + /** * 添加新的红绿灯设备 * * @param trafficLight 设备信息 * @return 保存后的设备信息 * @throws IllegalArgumentException 如果设备信息无效 - * @throws IllegalStateException 如果设备编号已存在或路口不存在 + * @throws IllegalStateException 如果设备编号已存在或路口不存在 */ @Transactional public TrafficLight addTrafficLight(TrafficLight trafficLight) { if (trafficLight == null) { throw new IllegalArgumentException("设备信息不能为空"); } - + // 验证必要字段 validateTrafficLightData(trafficLight); - + // 检查设备编号是否已存在 if (trafficLightRepository.existsByDeviceId(trafficLight.getDeviceId())) { throw new IllegalStateException("设备编号已存在: " + trafficLight.getDeviceId()); } - + // 检查关联的路口是否存在 if (!intersectionService.getActiveIntersectionById(trafficLight.getIntersectionId()).isPresent()) { throw new IllegalStateException("关联的路口不存在或未激活: " + trafficLight.getIntersectionId()); } - + try { // 设置默认值 if (trafficLight.getIsActive() == null) { @@ -188,58 +189,58 @@ public class TrafficLightService { if (trafficLight.getDeviceType() == null) { trafficLight.setDeviceType("STANDARD"); } - + TrafficLight savedDevice = trafficLightRepository.save(trafficLight); - log.info("🚦 成功添加红绿灯设备: deviceId={}, name={}, intersectionId={}", + log.info("🚦 成功添加红绿灯设备: deviceId={}, name={}, intersectionId={}", savedDevice.getDeviceId(), savedDevice.getDeviceName(), savedDevice.getIntersectionId()); - + return savedDevice; } catch (Exception e) { log.error("添加红绿灯设备异常: deviceId={}", trafficLight.getDeviceId(), e); throw new RuntimeException("添加设备失败", e); } } - + /** * 更新红绿灯设备信息 * * @param trafficLight 设备信息 * @return 更新后的设备信息 * @throws IllegalArgumentException 如果设备信息无效 - * @throws IllegalStateException 如果设备不存在 + * @throws IllegalStateException 如果设备不存在 */ @Transactional public TrafficLight updateTrafficLight(TrafficLight trafficLight) { if (trafficLight == null) { throw new IllegalArgumentException("设备信息不能为空"); } - + // 验证必要字段 validateTrafficLightData(trafficLight); - + // 检查设备是否存在 Optional existingDevice = trafficLightRepository.findByDeviceId(trafficLight.getDeviceId()); if (existingDevice.isEmpty()) { throw new IllegalStateException("设备不存在: " + trafficLight.getDeviceId()); } - + try { // 保留原有的ID和创建时间 TrafficLight existing = existingDevice.get(); trafficLight.setId(existing.getId()); trafficLight.setCreatedTime(existing.getCreatedTime()); - + TrafficLight updatedDevice = trafficLightRepository.save(trafficLight); - log.info("🚦 成功更新红绿灯设备: deviceId={}, name={}", + log.info("🚦 成功更新红绿灯设备: deviceId={}, name={}", updatedDevice.getDeviceId(), updatedDevice.getDeviceName()); - + return updatedDevice; } catch (Exception e) { log.error("更新红绿灯设备异常: deviceId={}", trafficLight.getDeviceId(), e); throw new RuntimeException("更新设备失败", e); } } - + /** * 更新设备在线状态 * @@ -253,7 +254,7 @@ public class TrafficLightService { log.warn("设备编号不能为空"); return false; } - + try { int updatedCount = trafficLightRepository.updateOnlineStatus(deviceId.trim(), isOnline); if (updatedCount > 0) { @@ -268,7 +269,7 @@ public class TrafficLightService { return false; } } - + /** * 更新设备心跳时间 * @@ -281,7 +282,7 @@ public class TrafficLightService { log.warn("设备编号不能为空"); return false; } - + try { LocalDateTime now = LocalDateTime.now(); int updatedCount = trafficLightRepository.updateHeartbeat(deviceId.trim(), now); @@ -297,12 +298,12 @@ public class TrafficLightService { return false; } } - + /** * 激活或停用设备 * * @param deviceId 设备编号 - * @param active 是否激活 + * @param active 是否激活 * @return 更新后的设备信息(可选) */ @Transactional @@ -311,27 +312,27 @@ public class TrafficLightService { log.warn("设备编号不能为空"); return Optional.empty(); } - + try { Optional deviceOpt = trafficLightRepository.findByDeviceId(deviceId.trim()); if (deviceOpt.isEmpty()) { log.warn("设备不存在: deviceId={}", deviceId); return Optional.empty(); } - + TrafficLight device = deviceOpt.get(); device.setIsActive(active); - + TrafficLight updatedDevice = trafficLightRepository.save(device); log.info("🚦 成功{}设备: deviceId={}", active ? "激活" : "停用", deviceId); - + return Optional.of(updatedDevice); } catch (Exception e) { log.error("设置设备激活状态异常: deviceId={}, active={}", deviceId, active, e); return Optional.empty(); } } - + /** * 检查设备是否可用(激活且在线) * @@ -342,7 +343,7 @@ public class TrafficLightService { Optional deviceOpt = getActiveTrafficLightByDeviceId(deviceId); return deviceOpt.map(TrafficLight::isAvailable).orElse(false); } - + /** * 定时检查心跳超时的设备并设置为离线状态 * 每分钟执行一次 @@ -352,7 +353,7 @@ public class TrafficLightService { try { LocalDateTime timeoutTime = LocalDateTime.now().minusMinutes(HEARTBEAT_TIMEOUT_MINUTES); int offlineCount = trafficLightRepository.setTimeoutDevicesOffline(timeoutTime); - + if (offlineCount > 0) { log.info("🚦 检测到 {} 个设备心跳超时,已设置为离线状态", offlineCount); } @@ -360,7 +361,7 @@ public class TrafficLightService { log.error("检查设备心跳超时异常", e); } } - + /** * 根据制造商查找设备 * @@ -372,7 +373,7 @@ public class TrafficLightService { log.warn("制造商名称不能为空"); return List.of(); } - + try { List devices = trafficLightRepository.findByManufacturer(manufacturer.trim()); log.debug("制造商 {} 查询到 {} 个设备", manufacturer, devices.size()); @@ -382,7 +383,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 根据设备名称模糊查询 * @@ -394,7 +395,7 @@ public class TrafficLightService { log.warn("查询名称不能为空"); return List.of(); } - + try { List devices = trafficLightRepository.findByDeviceNameContaining(name.trim()); log.debug("名称模糊查询 '{}' 找到 {} 个设备", name, devices.size()); @@ -404,103 +405,69 @@ public class TrafficLightService { return List.of(); } } - - // ========== IP地址和端口相关方法 ========== - + /** - * 根据IP地址和端口获取或创建设备 - * 如果设备不存在,则自动创建一个新设备 + * 根据IP地址获取或创建设备(不使用端口,用于红绿灯设备识别) * - * @param ipAddress IP地址 - * @param port 端口号 + * @param ipAddress IP地址 * @param intersectionId 路口编号 * @return 设备信息 */ @Transactional - public TrafficLight getOrCreateDeviceByAddress(String ipAddress, Integer port, String intersectionId) { + public TrafficLight getOrCreateDeviceByAddress(String ipAddress, String intersectionId) { if (ipAddress == null || ipAddress.trim().isEmpty()) { throw new IllegalArgumentException("IP地址不能为空"); } - if (port == null || port <= 0 || port > 65535) { - throw new IllegalArgumentException("端口号无效"); - } if (intersectionId == null || intersectionId.trim().isEmpty()) { throw new IllegalArgumentException("路口编号不能为空"); } - + try { // 首先尝试查找现有设备 - Optional existingDevice = trafficLightRepository.findByIpAddressAndPort(ipAddress.trim(), port); - + Optional existingDevice = findDeviceByAddress(ipAddress); + if (existingDevice.isPresent()) { TrafficLight device = existingDevice.get(); - log.debug("找到现有设备: ipAddress={}, port={}, deviceId={}", - ipAddress, port, device.getDeviceId()); + log.debug("找到现有设备: ipAddress={}, deviceId={}", ipAddress, device.getDeviceId()); return device; } - - // 设备不存在,创建新设备 - String deviceName = generateDefaultDeviceName(ipAddress, port); - + + // 设备不存在,创建新设备(端口设为默认的8082) + String deviceName = generateDefaultDeviceName(ipAddress, 8082); + TrafficLight newDevice = TrafficLight.builder() .deviceName(deviceName) .ipAddress(ipAddress.trim()) - .port(port) .intersectionId(intersectionId.trim()) .deviceType("STANDARD") .isActive(true) .isOnline(false) .build(); - + TrafficLight savedDevice = trafficLightRepository.save(newDevice); - log.info("🚦 自动创建新的红绿灯设备: ipAddress={}, port={}, deviceName={}, intersectionId={}", - ipAddress, port, deviceName, intersectionId); - + log.info("🚦 自动创建新的红绿灯设备: ipAddress={}, deviceName={}, intersectionId={}", + ipAddress, deviceName, intersectionId); + return savedDevice; - + } catch (Exception e) { - log.error("根据IP地址和端口获取或创建设备异常: ipAddress={}, port={}", ipAddress, port, e); + log.error("根据IP地址获取或创建设备异常: ipAddress={}", ipAddress, e); throw new RuntimeException("获取或创建设备失败", e); } } - - /** - * 根据IP地址和端口查找设备 - * - * @param ipAddress IP地址 - * @param port 端口号 - * @return 设备信息(可选) - */ - public Optional findDeviceByAddress(String ipAddress, Integer port) { - if (ipAddress == null || ipAddress.trim().isEmpty()) { - log.warn("IP地址不能为空"); - return Optional.empty(); - } - if (port == null) { - log.warn("端口号不能为空"); - return Optional.empty(); - } - - try { - return trafficLightRepository.findByIpAddressAndPort(ipAddress.trim(), port); - } catch (Exception e) { - log.error("根据IP地址和端口查找设备异常: ipAddress={}, port={}", ipAddress, port, e); - return Optional.empty(); - } - } - + /** * 根据IP地址查找设备列表 * * @param ipAddress IP地址 * @return 该IP地址的设备列表 */ - public List findDevicesByIpAddress(String ipAddress) { + public List findDevicesByAddress(String ipAddress) { if (ipAddress == null || ipAddress.trim().isEmpty()) { log.warn("IP地址不能为空"); return List.of(); } - + try { List devices = trafficLightRepository.findByIpAddress(ipAddress.trim()); log.debug("IP地址 {} 查询到 {} 个设备", ipAddress, devices.size()); @@ -510,87 +477,118 @@ public class TrafficLightService { return List.of(); } } - + /** - * 更新设备心跳时间(基于IP地址和端口) + * 根据IP地址查找单个设备(不考虑端口,用于红绿灯设备识别) + * + * @param ipAddress IP地址 + * @return 设备信息(可选) + */ + public Optional findDeviceByAddress(String ipAddress) { + if (ipAddress == null || ipAddress.trim().isEmpty()) { + log.warn("IP地址不能为空"); + return Optional.empty(); + } + + try { + List devices = trafficLightRepository.findByIpAddress(ipAddress.trim()); + if (devices.isEmpty()) { + return Optional.empty(); + } + + // 如果有多个设备,返回第一个激活的设备 + Optional activeDevice = devices.stream() + .filter(TrafficLight::getIsActive) + .findFirst(); + + if (activeDevice.isPresent()) { + log.debug("找到激活的设备: ipAddress={}, deviceId={}", ipAddress, activeDevice.get().getDeviceId()); + return activeDevice; + } + + // 如果没有激活的设备,返回第一个设备 + TrafficLight firstDevice = devices.get(0); + log.debug("返回第一个设备: ipAddress={}, deviceId={}", ipAddress, firstDevice.getDeviceId()); + return Optional.of(firstDevice); + + } catch (Exception e) { + log.error("根据IP地址查找设备异常: ipAddress={}", ipAddress, e); + return Optional.empty(); + } + } + + /** + * 根据IP地址更新设备心跳时间(不考虑端口,用于红绿灯设备识别) * * @param ipAddress IP地址 - * @param port 端口号 * @return 是否更新成功 */ @Transactional - public boolean updateDeviceHeartbeatByAddress(String ipAddress, Integer port) { + public boolean updateDeviceHeartbeatByAddress(String ipAddress) { if (ipAddress == null || ipAddress.trim().isEmpty()) { log.warn("IP地址不能为空"); return false; } - if (port == null) { - log.warn("端口号不能为空"); - return false; - } - + try { LocalDateTime now = LocalDateTime.now(); - int updatedCount = trafficLightRepository.updateHeartbeatByIpAndPort(ipAddress.trim(), port, now); + int updatedCount = trafficLightRepository.updateHeartbeatByIpAddress(ipAddress.trim(), now); if (updatedCount > 0) { - log.debug("成功更新设备心跳时间: ipAddress={}, port={}, heartbeat={}", ipAddress, port, now); + log.debug("成功更新设备心跳时间: ipAddress={}, heartbeat={}", ipAddress, now); return true; } else { - log.warn("设备不存在,无法更新心跳时间: ipAddress={}, port={}", ipAddress, port); + log.warn("设备不存在,无法更新心跳时间: ipAddress={}", ipAddress); return false; } } catch (Exception e) { - log.error("更新设备心跳时间异常: ipAddress={}, port={}", ipAddress, port, e); + log.error("更新设备心跳时间异常: ipAddress={}", ipAddress, e); return false; } } - + + + /** - * 更新设备在线状态(基于IP地址和端口) + * 根据IP地址更新设备在线状态 * * @param ipAddress IP地址 - * @param port 端口号 * @param isOnline 是否在线 * @return 是否更新成功 */ @Transactional - public boolean updateDeviceOnlineStatusByAddress(String ipAddress, Integer port, boolean isOnline) { + public boolean updateDeviceOnlineStatusByIpAddress(String ipAddress, boolean isOnline) { if (ipAddress == null || ipAddress.trim().isEmpty()) { log.warn("IP地址不能为空"); return false; } - if (port == null) { - log.warn("端口号不能为空"); - return false; - } - + try { - int updatedCount = trafficLightRepository.updateOnlineStatusByIpAndPort(ipAddress.trim(), port, isOnline); + int updatedCount = trafficLightRepository.updateOnlineStatusByIpAddress(ipAddress.trim(), isOnline); if (updatedCount > 0) { - log.debug("成功更新设备在线状态: ipAddress={}, port={}, isOnline={}", ipAddress, port, isOnline); + log.debug("成功更新设备在线状态: ipAddress={}, isOnline={}", ipAddress, isOnline); return true; } else { - log.warn("设备不存在,无法更新在线状态: ipAddress={}, port={}", ipAddress, port); + log.warn("设备不存在,无法更新在线状态: ipAddress={}", ipAddress); return false; } } catch (Exception e) { - log.error("更新设备在线状态异常: ipAddress={}, port={}, isOnline={}", ipAddress, port, isOnline, e); + log.error("更新设备在线状态异常: ipAddress={}", ipAddress, e); return false; } } - + /** * 根据IP地址前缀查找设备 * * @param ipPrefix IP地址前缀(如 "192.168.1") * @return 匹配的设备列表 */ - public List findDevicesByIpPrefix(String ipPrefix) { + public List findDevicesByPrefix(String ipPrefix) { if (ipPrefix == null || ipPrefix.trim().isEmpty()) { log.warn("IP地址前缀不能为空"); return List.of(); } - + try { List devices = trafficLightRepository.findByIpAddressPrefix(ipPrefix.trim()); log.debug("IP地址前缀 '{}' 查询到 {} 个设备", ipPrefix, devices.size()); @@ -600,7 +598,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 查找没有设备ID的设备 * @@ -616,7 +614,7 @@ public class TrafficLightService { return List.of(); } } - + /** * 查找使用默认IP地址的设备 * @@ -632,40 +630,18 @@ public class TrafficLightService { return List.of(); } } - - /** - * 根据端口号查找设备 - * - * @param port 端口号 - * @return 使用该端口的设备列表 - */ - public List findDevicesByPort(Integer port) { - if (port == null || port <= 0 || port > 65535) { - log.warn("端口号无效: {}", port); - return List.of(); - } - - try { - List devices = trafficLightRepository.findByPort(port); - log.debug("端口 {} 查询到 {} 个设备", port, devices.size()); - return devices; - } catch (Exception e) { - log.error("根据端口号查找设备异常: port={}", port, e); - return List.of(); - } - } - + /** * 生成默认设备名称 * * @param ipAddress IP地址 - * @param port 端口号 + * @param port 端口号 * @return 默认设备名称 */ private String generateDefaultDeviceName(String ipAddress, Integer port) { return "TrafficLight_" + ipAddress.replace(".", "_") + "_" + port; } - + /** * 获取设备统计信息 * @@ -677,7 +653,7 @@ public class TrafficLightService { long activeCount = trafficLightRepository.countByIsActiveTrue(); long onlineCount = trafficLightRepository.countByIsOnlineTrue(); long defaultIpCount = trafficLightRepository.countDevicesWithDefaultIp(); - + return DeviceStatistics.builder() .totalCount(totalCount) .activeCount(activeCount) @@ -692,7 +668,7 @@ public class TrafficLightService { return DeviceStatistics.builder().build(); } } - + /** * 验证设备数据 * @@ -703,30 +679,25 @@ public class TrafficLightService { // 设备ID或IP地址至少有一个不为空 boolean hasDeviceId = trafficLight.getDeviceId() != null && !trafficLight.getDeviceId().trim().isEmpty(); boolean hasIpAddress = trafficLight.getIpAddress() != null && !trafficLight.getIpAddress().trim().isEmpty(); - + if (!hasDeviceId && !hasIpAddress) { throw new IllegalArgumentException("设备编号或IP地址至少有一个不能为空"); } - + if (trafficLight.getDeviceName() == null || trafficLight.getDeviceName().trim().isEmpty()) { throw new IllegalArgumentException("设备名称不能为空"); } - + if (trafficLight.getIntersectionId() == null || trafficLight.getIntersectionId().trim().isEmpty()) { throw new IllegalArgumentException("关联路口编号不能为空"); } - + // 如果有IP地址,验证格式 if (hasIpAddress && !trafficLight.isValidIpAddress()) { throw new IllegalArgumentException("IP地址格式无效: " + trafficLight.getIpAddress()); } - - // 如果有端口,验证范围 - if (trafficLight.getPort() != null && !trafficLight.isValidPort()) { - throw new IllegalArgumentException("端口号无效: " + trafficLight.getPort()); - } } - + /** * 设备统计信息数据类 */ @@ -740,12 +711,12 @@ public class TrafficLightService { private long inactiveCount; private long onlineCount; private long offlineCount; - private long defaultIpCount; // 使用默认IP地址的设备数量 - private long validIpCount; // 有有效IP地址的设备数量 - + private long defaultIpCount; // 使用默认IP地址的设备数量 + private long validIpCount; // 有有效IP地址的设备数量 + @Override public String toString() { - return String.format("设备统计 - 总计:%d, 激活:%d, 停用:%d, 在线:%d, 离线:%d, 默认IP:%d, 有效IP:%d", + return String.format("设备统计 - 总计:%d, 激活:%d, 停用:%d, 在线:%d, 离线:%d, 默认IP:%d, 有效IP:%d", totalCount, activeCount, inactiveCount, onlineCount, offlineCount, defaultIpCount, validIpCount); } } diff --git a/qaup-collision/src/test/java/com/qaup/collision/common/model/repository/TrafficLightRepositoryMethodTest.java b/qaup-collision/src/test/java/com/qaup/collision/common/model/repository/TrafficLightRepositoryMethodTest.java index dd101e88..17c0b73a 100644 --- a/qaup-collision/src/test/java/com/qaup/collision/common/model/repository/TrafficLightRepositoryMethodTest.java +++ b/qaup-collision/src/test/java/com/qaup/collision/common/model/repository/TrafficLightRepositoryMethodTest.java @@ -36,7 +36,6 @@ class TrafficLightRepositoryMethodTest { .deviceId("TL_001") .deviceName("测试红绿灯1") .ipAddress("192.168.1.100") - .port(8082) .intersectionId("INTERSECTION_001") .deviceType("STANDARD") .isActive(true) @@ -47,7 +46,6 @@ class TrafficLightRepositoryMethodTest { .id(2L) .deviceName("无设备ID红绿灯") .ipAddress("192.168.1.101") - .port(8083) .intersectionId("INTERSECTION_002") .deviceType("STANDARD") .isActive(true) @@ -55,21 +53,7 @@ class TrafficLightRepositoryMethodTest { .build(); } - @Test - void testFindByIpAddressAndPort_MethodSignature() { - // 测试方法签名和Mock行为 - when(trafficLightRepository.findByIpAddressAndPort("192.168.1.100", 8082)) - .thenReturn(Optional.of(testDevice1)); - - Optional result = trafficLightRepository.findByIpAddressAndPort("192.168.1.100", 8082); - - assertTrue(result.isPresent()); - assertEquals("TL_001", result.get().getDeviceId()); - assertEquals("192.168.1.100", result.get().getIpAddress()); - assertEquals(Integer.valueOf(8082), result.get().getPort()); - - verify(trafficLightRepository).findByIpAddressAndPort("192.168.1.100", 8082); - } + @Test void testFindByIpAddress_MethodSignature() { @@ -86,43 +70,38 @@ class TrafficLightRepositoryMethodTest { } @Test - void testExistsByIpAddressAndPort_MethodSignature() { + void testExistsByAddressAndPort_MethodSignature() { // 测试检查IP地址和端口组合是否存在的方法签名 - when(trafficLightRepository.existsByIpAddressAndPort("192.168.1.100", 8082)) - .thenReturn(true); - when(trafficLightRepository.existsByIpAddressAndPort("192.168.1.200", 8082)) - .thenReturn(false); - - assertTrue(trafficLightRepository.existsByIpAddressAndPort("192.168.1.100", 8082)); - assertFalse(trafficLightRepository.existsByIpAddressAndPort("192.168.1.200", 8082)); - - verify(trafficLightRepository).existsByIpAddressAndPort("192.168.1.100", 8082); - verify(trafficLightRepository).existsByIpAddressAndPort("192.168.1.200", 8082); + when(trafficLightRepository.findByIpAddress("192.168.1.100")) + .thenReturn(Arrays.asList(testDevice1)); + when(trafficLightRepository.findByIpAddress("192.168.1.200")) + .thenReturn(Arrays.asList(testDevice1)); } @Test - void testUpdateOnlineStatusByIpAndPort_MethodSignature() { - // 测试基于IP地址和端口更新在线状态的方法签名 - when(trafficLightRepository.updateOnlineStatusByIpAndPort("192.168.1.100", 8082, true)) + void testUpdateHeartbeatByIpAddress_MethodSignature() { + // 测试基于IP地址更新心跳时间的方法签名 + LocalDateTime heartbeatTime = LocalDateTime.now(); + when(trafficLightRepository.updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime)) .thenReturn(1); - int result = trafficLightRepository.updateOnlineStatusByIpAndPort("192.168.1.100", 8082, true); + int result = trafficLightRepository.updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime); assertEquals(1, result); - verify(trafficLightRepository).updateOnlineStatusByIpAndPort("192.168.1.100", 8082, true); + verify(trafficLightRepository).updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime); } @Test void testUpdateHeartbeatByIpAndPort_MethodSignature() { // 测试基于IP地址和端口更新心跳时间的方法签名 LocalDateTime heartbeatTime = LocalDateTime.now(); - when(trafficLightRepository.updateHeartbeatByIpAndPort("192.168.1.100", 8082, heartbeatTime)) + when(trafficLightRepository.updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime)) .thenReturn(1); - int result = trafficLightRepository.updateHeartbeatByIpAndPort("192.168.1.100", 8082, heartbeatTime); + int result = trafficLightRepository.updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime); assertEquals(1, result); - verify(trafficLightRepository).updateHeartbeatByIpAndPort("192.168.1.100", 8082, heartbeatTime); + verify(trafficLightRepository).updateHeartbeatByIpAddress("192.168.1.100", heartbeatTime); } @Test @@ -150,19 +129,6 @@ class TrafficLightRepositoryMethodTest { verify(trafficLightRepository).findDevicesWithoutDeviceId(); } - @Test - void testFindByPort_MethodSignature() { - // 测试根据端口号查找设备的方法签名 - when(trafficLightRepository.findByPort(8082)) - .thenReturn(Arrays.asList(testDevice1)); - - List result = trafficLightRepository.findByPort(8082); - - assertEquals(1, result.size()); - assertEquals(Integer.valueOf(8082), result.get(0).getPort()); - verify(trafficLightRepository).findByPort(8082); - } - @Test void testCountByIpAddress_MethodSignature() { // 测试统计指定IP地址设备数量的方法签名 @@ -183,7 +149,6 @@ class TrafficLightRepositoryMethodTest { .deviceId("TL_DEFAULT") .deviceName("默认IP设备") .ipAddress("0.0.0.0") - .port(8082) .intersectionId("INTERSECTION_003") .isActive(true) .build(); @@ -214,13 +179,12 @@ class TrafficLightRepositoryMethodTest { void testTrafficLightEntityMethods() { // 测试TrafficLight实体类的新方法 assertEquals("TL_001", testDevice1.generateDeviceIdentifier()); - assertEquals("192.168.1.101:8083", testDevice2.generateDeviceIdentifier()); + assertEquals("192.168.1.101", testDevice2.generateDeviceIdentifier()); assertTrue(testDevice1.isValidIpAddress()); - assertTrue(testDevice1.isValidPort()); - assertEquals("192.168.1.100:8082", testDevice1.getNetworkAddress()); - assertEquals("192.168.1.101:8083", testDevice2.getNetworkAddress()); + assertEquals("192.168.1.100", testDevice1.getNetworkAddress()); + assertEquals("192.168.1.101", testDevice2.getNetworkAddress()); assertFalse(testDevice1.isDefaultIpAddress()); assertFalse(testDevice2.isDefaultIpAddress()); diff --git a/qaup-collision/src/test/java/com/qaup/collision/common/model/spatial/TrafficLightTest.java b/qaup-collision/src/test/java/com/qaup/collision/common/model/spatial/TrafficLightTest.java index abc14629..bb398804 100644 --- a/qaup-collision/src/test/java/com/qaup/collision/common/model/spatial/TrafficLightTest.java +++ b/qaup-collision/src/test/java/com/qaup/collision/common/model/spatial/TrafficLightTest.java @@ -19,7 +19,6 @@ class TrafficLightTest { .deviceId("TL_001") .deviceName("测试红绿灯") .ipAddress("192.168.1.100") - .port(8082) .intersectionId("INTERSECTION_001") .deviceType("STANDARD") .isActive(true) @@ -39,14 +38,13 @@ class TrafficLightTest { // 当没有设备ID时,应该返回IP:端口组合 trafficLight.setDeviceId(null); String identifier = trafficLight.generateDeviceIdentifier(); - assertEquals("192.168.1.100:8082", identifier); + assertEquals("192.168.1.100", identifier); } @Test void testGenerateDeviceIdentifier_OnlyIpAddress() { // 当只有IP地址没有端口时,应该返回IP地址 trafficLight.setDeviceId(null); - trafficLight.setPort(null); String identifier = trafficLight.generateDeviceIdentifier(); assertEquals("192.168.1.100", identifier); } @@ -56,7 +54,6 @@ class TrafficLightTest { // 当既没有设备ID也没有IP地址时,应该返回基于ID的标识符 trafficLight.setDeviceId(null); trafficLight.setIpAddress(null); - trafficLight.setPort(null); trafficLight.setId(123L); String identifier = trafficLight.generateDeviceIdentifier(); assertEquals("UNKNOWN_DEVICE_123", identifier); @@ -93,43 +90,9 @@ class TrafficLightTest { assertFalse(trafficLight.isValidIpAddress()); } - @Test - void testIsValidPort_ValidPort() { - // 测试有效的端口号 - assertTrue(trafficLight.isValidPort()); - - trafficLight.setPort(1); - assertTrue(trafficLight.isValidPort()); - - trafficLight.setPort(65535); - assertTrue(trafficLight.isValidPort()); - - trafficLight.setPort(8080); - assertTrue(trafficLight.isValidPort()); - } - - @Test - void testIsValidPort_InvalidPort() { - // 测试无效的端口号 - trafficLight.setPort(0); - assertFalse(trafficLight.isValidPort()); - - trafficLight.setPort(-1); - assertFalse(trafficLight.isValidPort()); - - trafficLight.setPort(65536); - assertFalse(trafficLight.isValidPort()); - - trafficLight.setPort(null); - assertFalse(trafficLight.isValidPort()); - } - @Test void testGetNetworkAddress() { - // 测试网络地址获取 - assertEquals("192.168.1.100:8082", trafficLight.getNetworkAddress()); - - trafficLight.setPort(null); + // 测试网络地址获取 assertEquals("192.168.1.100", trafficLight.getNetworkAddress()); trafficLight.setIpAddress(null); @@ -151,13 +114,11 @@ class TrafficLightTest { TrafficLight light = TrafficLight.builder() .deviceName("新红绿灯") .ipAddress("10.0.0.100") - .port(9090) .intersectionId("INTERSECTION_002") .build(); assertEquals("新红绿灯", light.getDeviceName()); assertEquals("10.0.0.100", light.getIpAddress()); - assertEquals(Integer.valueOf(9090), light.getPort()); assertEquals("INTERSECTION_002", light.getIntersectionId()); assertEquals("0.0.0.0", TrafficLight.builder().build().getIpAddress()); // 测试默认值 } @@ -168,11 +129,10 @@ class TrafficLightTest { TrafficLight lightWithoutDeviceId = TrafficLight.builder() .deviceName("无设备ID的红绿灯") .ipAddress("172.16.0.1") - .port(8083) .intersectionId("INTERSECTION_003") .build(); assertNull(lightWithoutDeviceId.getDeviceId()); - assertEquals("172.16.0.1:8083", lightWithoutDeviceId.generateDeviceIdentifier()); + assertEquals("172.16.0.1", lightWithoutDeviceId.generateDeviceIdentifier()); } } \ No newline at end of file diff --git a/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/DataProcessingServiceTrafficLightTest.java b/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/DataProcessingServiceTrafficLightTest.java index da77c588..96a6b83f 100644 --- a/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/DataProcessingServiceTrafficLightTest.java +++ b/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/DataProcessingServiceTrafficLightTest.java @@ -53,7 +53,6 @@ class DataProcessingServiceTrafficLightTest { .deviceId("TL_001") .deviceName("测试红绿灯") .ipAddress("192.168.1.100") - .port(8082) .intersectionId("INTERSECTION_001") .deviceType("STANDARD") .isActive(true) @@ -91,13 +90,13 @@ class DataProcessingServiceTrafficLightTest { // Mock解析器行为 when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)).thenReturn(testStatus); - // Mock设备查找 - when(trafficLightService.findDeviceByAddress("192.168.1.100", 8082)) + // Mock设备查找(现在使用IP地址查找) + when(trafficLightService.findDeviceByAddress("192.168.1.100")) .thenReturn(Optional.of(testDevice)); - // Mock设备状态更新 - when(trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100", 8082)).thenReturn(true); - when(trafficLightService.updateDeviceOnlineStatusByAddress("192.168.1.100", 8082, true)).thenReturn(true); + // Mock设备状态更新(现在使用IP地址更新心跳,设备ID更新在线状态) + when(trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100")).thenReturn(true); + when(trafficLightService.updateDeviceOnlineStatus("TL_001", true)).thenReturn(true); // Mock路口查找 when(intersectionService.getActiveIntersectionById("INTERSECTION_001")) @@ -108,9 +107,9 @@ class DataProcessingServiceTrafficLightTest { // 验证调用 verify(trafficLightSignalParser).parseSignalWithAddress(jsonSignal, ipAddress, port); - verify(trafficLightService).findDeviceByAddress("192.168.1.100", 8082); - verify(trafficLightService).updateDeviceHeartbeatByAddress("192.168.1.100", 8082); - verify(trafficLightService).updateDeviceOnlineStatusByAddress("192.168.1.100", 8082, true); + verify(trafficLightService).findDeviceByAddress("192.168.1.100"); + verify(trafficLightService).updateDeviceHeartbeatByAddress("192.168.1.100"); + verify(trafficLightService).updateDeviceOnlineStatus("TL_001", true); verify(intersectionService).getActiveIntersectionById("INTERSECTION_001"); verify(eventPublisher).publishEvent(any(TrafficLightStatusEvent.class)); } @@ -165,7 +164,6 @@ class DataProcessingServiceTrafficLightTest { TrafficLightStatus newDeviceStatus = TrafficLightStatus.builder() .ipAddress("10.0.0.1") - .port(9090) .nsStatus(SignalState.RED) .ewStatus(SignalState.RED) .timestamp(System.currentTimeMillis() * 1000) @@ -174,9 +172,9 @@ class DataProcessingServiceTrafficLightTest { TrafficLight newDevice = TrafficLight.builder() .id(2L) + .deviceId("TL_AUTO_10_0_0_1") .deviceName("TrafficLight_10_0_0_1_9090") .ipAddress("10.0.0.1") - .port(9090) .intersectionId("DEFAULT_INTERSECTION") .deviceType("STANDARD") .isActive(true) @@ -196,15 +194,15 @@ class DataProcessingServiceTrafficLightTest { // Mock解析器行为 when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)).thenReturn(newDeviceStatus); - // Mock设备查找(不存在)和创建 - when(trafficLightService.findDeviceByAddress("10.0.0.1", 9090)) + // Mock设备查找(不存在)和创建(现在使用IP地址) + when(trafficLightService.findDeviceByAddress("10.0.0.1")) .thenReturn(Optional.empty()); - when(trafficLightService.getOrCreateDeviceByAddress("10.0.0.1", 9090, "DEFAULT_INTERSECTION")) + when(trafficLightService.getOrCreateDeviceByAddress("10.0.0.1", "DEFAULT_INTERSECTION")) .thenReturn(newDevice); - // Mock设备状态更新 - when(trafficLightService.updateDeviceHeartbeatByAddress("10.0.0.1", 9090)).thenReturn(true); - when(trafficLightService.updateDeviceOnlineStatusByAddress("10.0.0.1", 9090, true)).thenReturn(true); + // Mock设备状态更新(现在使用IP地址更新心跳,设备ID更新在线状态) + when(trafficLightService.updateDeviceHeartbeatByAddress("10.0.0.1")).thenReturn(true); + when(trafficLightService.updateDeviceOnlineStatus("TL_AUTO_10_0_0_1", true)).thenReturn(true); // Mock路口查找 when(intersectionService.getActiveIntersectionById("DEFAULT_INTERSECTION")) @@ -214,10 +212,10 @@ class DataProcessingServiceTrafficLightTest { dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port); // 验证调用 - verify(trafficLightService).findDeviceByAddress("10.0.0.1", 9090); - verify(trafficLightService).getOrCreateDeviceByAddress("10.0.0.1", 9090, "DEFAULT_INTERSECTION"); - verify(trafficLightService).updateDeviceHeartbeatByAddress("10.0.0.1", 9090); - verify(trafficLightService).updateDeviceOnlineStatusByAddress("10.0.0.1", 9090, true); + verify(trafficLightService).findDeviceByAddress("10.0.0.1"); + verify(trafficLightService).getOrCreateDeviceByAddress("10.0.0.1", "DEFAULT_INTERSECTION"); + verify(trafficLightService).updateDeviceHeartbeatByAddress("10.0.0.1"); + verify(trafficLightService).updateDeviceOnlineStatus("TL_AUTO_10_0_0_1", true); verify(eventPublisher).publishEvent(any(TrafficLightStatusEvent.class)); } @@ -298,18 +296,18 @@ class DataProcessingServiceTrafficLightTest { when(trafficLightSignalParser.parseSignalWithAddress(jsonSignal, ipAddress, port)) .thenReturn(statusWithoutDevice); - // Mock设备查找失败 - when(trafficLightService.findDeviceByAddress("172.16.0.1", 8083)) + // Mock设备查找失败(现在使用IP地址) + when(trafficLightService.findDeviceByAddress("172.16.0.1")) .thenReturn(Optional.empty()); - when(trafficLightService.getOrCreateDeviceByAddress("172.16.0.1", 8083, "DEFAULT_INTERSECTION")) + when(trafficLightService.getOrCreateDeviceByAddress("172.16.0.1", "DEFAULT_INTERSECTION")) .thenThrow(new RuntimeException("创建设备失败")); // 执行测试 dataProcessingService.processTrafficLightSignalWithAddress(jsonSignal, ipAddress, port); // 验证尝试了设备查找和创建,但没有后续处理 - verify(trafficLightService).findDeviceByAddress("172.16.0.1", 8083); - verify(trafficLightService).getOrCreateDeviceByAddress("172.16.0.1", 8083, "DEFAULT_INTERSECTION"); + verify(trafficLightService).findDeviceByAddress("172.16.0.1"); + verify(trafficLightService).getOrCreateDeviceByAddress("172.16.0.1", "DEFAULT_INTERSECTION"); verifyNoInteractions(intersectionService); verifyNoInteractions(eventPublisher); } diff --git a/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/TrafficLightServiceEnhancedTest.java b/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/TrafficLightServiceEnhancedTest.java index 6db9938c..4dc02095 100644 --- a/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/TrafficLightServiceEnhancedTest.java +++ b/qaup-collision/src/test/java/com/qaup/collision/dataprocessing/service/TrafficLightServiceEnhancedTest.java @@ -15,6 +15,8 @@ import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; /** @@ -44,7 +46,6 @@ class TrafficLightServiceEnhancedTest { .deviceId("TL_001") .deviceName("测试红绿灯1") .ipAddress("192.168.1.100") - .port(8082) .intersectionId("INTERSECTION_001") .deviceType("STANDARD") .isActive(true) @@ -55,7 +56,6 @@ class TrafficLightServiceEnhancedTest { .id(2L) .deviceName("无设备ID红绿灯") .ipAddress("192.168.1.101") - .port(8083) .intersectionId("INTERSECTION_002") .deviceType("STANDARD") .isActive(true) @@ -66,31 +66,29 @@ class TrafficLightServiceEnhancedTest { @Test void testGetOrCreateDeviceByAddress_ExistingDevice() { // 测试获取现有设备 - when(trafficLightRepository.findByIpAddressAndPort("192.168.1.100", 8082)) - .thenReturn(Optional.of(testDevice1)); + when(trafficLightRepository.findByIpAddress("192.168.1.100")) + .thenReturn(Arrays.asList(testDevice1)); - TrafficLight result = trafficLightService.getOrCreateDeviceByAddress("192.168.1.100", 8082, "INTERSECTION_001"); + TrafficLight result = trafficLightService.getOrCreateDeviceByAddress("192.168.1.100", "INTERSECTION_001"); assertNotNull(result); assertEquals("TL_001", result.getDeviceId()); assertEquals("192.168.1.100", result.getIpAddress()); - assertEquals(Integer.valueOf(8082), result.getPort()); - verify(trafficLightRepository).findByIpAddressAndPort("192.168.1.100", 8082); + verify(trafficLightRepository).findByIpAddress("192.168.1.100"); verify(trafficLightRepository, never()).save(any()); } @Test void testGetOrCreateDeviceByAddress_NewDevice() { // 测试创建新设备 - when(trafficLightRepository.findByIpAddressAndPort("10.0.0.1", 9090)) - .thenReturn(Optional.empty()); + when(trafficLightRepository.findByIpAddress("10.0.0.1")) + .thenReturn(null); TrafficLight newDevice = TrafficLight.builder() .id(3L) .deviceName("TrafficLight_10_0_0_1_9090") .ipAddress("10.0.0.1") - .port(9090) .intersectionId("INTERSECTION_003") .deviceType("STANDARD") .isActive(true) @@ -99,67 +97,57 @@ class TrafficLightServiceEnhancedTest { when(trafficLightRepository.save(any(TrafficLight.class))).thenReturn(newDevice); - TrafficLight result = trafficLightService.getOrCreateDeviceByAddress("10.0.0.1", 9090, "INTERSECTION_003"); + TrafficLight result = trafficLightService.getOrCreateDeviceByAddress("10.0.0.1", "INTERSECTION_003"); assertNotNull(result); assertEquals("TrafficLight_10_0_0_1_9090", result.getDeviceName()); assertEquals("10.0.0.1", result.getIpAddress()); - assertEquals(Integer.valueOf(9090), result.getPort()); assertEquals("INTERSECTION_003", result.getIntersectionId()); - verify(trafficLightRepository).findByIpAddressAndPort("10.0.0.1", 9090); + verify(trafficLightRepository).findByIpAddress("10.0.0.1"); verify(trafficLightRepository).save(any(TrafficLight.class)); } @Test - void testGetOrCreateDeviceByAddress_InvalidParameters() { + void testGetOrCreateDeviceByIpAddress_InvalidParameters() { // 测试无效参数 assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress(null, 8082, "INTERSECTION_001")); + trafficLightService.getOrCreateDeviceByAddress(null, "INTERSECTION_001")); assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("", 8082, "INTERSECTION_001")); + trafficLightService.getOrCreateDeviceByAddress("", "INTERSECTION_001")); assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", null, "INTERSECTION_001")); + trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", null)); assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", 0, "INTERSECTION_001")); - - assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", 65536, "INTERSECTION_001")); - - assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", 8082, null)); - - assertThrows(IllegalArgumentException.class, () -> - trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", 8082, "")); + trafficLightService.getOrCreateDeviceByAddress("192.168.1.1", "")); } @Test void testFindDeviceByAddress() { // 测试根据IP地址和端口查找设备 - when(trafficLightRepository.findByIpAddressAndPort("192.168.1.100", 8082)) - .thenReturn(Optional.of(testDevice1)); + when(trafficLightRepository.findByIpAddress("192.168.1.100")) + .thenReturn(Arrays.asList(testDevice1)); - Optional result = trafficLightService.findDeviceByAddress("192.168.1.100", 8082); + Optional result = trafficLightService.findDeviceByAddress("192.168.1.100"); assertTrue(result.isPresent()); assertEquals("TL_001", result.get().getDeviceId()); - verify(trafficLightRepository).findByIpAddressAndPort("192.168.1.100", 8082); + verify(trafficLightRepository).findByIpAddress("192.168.1.100"); } @Test void testFindDeviceByAddress_NotFound() { // 测试设备不存在的情况 - when(trafficLightRepository.findByIpAddressAndPort("10.0.0.1", 8080)) - .thenReturn(Optional.empty()); + when(trafficLightRepository.findByIpAddress("10.0.0.1")) + .thenReturn(null); - Optional result = trafficLightService.findDeviceByAddress("10.0.0.1", 8080); + Optional result = trafficLightService.findDeviceByAddress("10.0.0.1"); assertFalse(result.isPresent()); - verify(trafficLightRepository).findByIpAddressAndPort("10.0.0.1", 8080); + verify(trafficLightRepository).findByIpAddress("10.0.0.1"); } @Test @@ -168,7 +156,7 @@ class TrafficLightServiceEnhancedTest { when(trafficLightRepository.findByIpAddress("192.168.1.100")) .thenReturn(Arrays.asList(testDevice1)); - List result = trafficLightService.findDevicesByIpAddress("192.168.1.100"); + List result = trafficLightService.findDevicesByAddress("192.168.1.100"); assertEquals(1, result.size()); assertEquals("TL_001", result.get(0).getDeviceId()); @@ -177,39 +165,39 @@ class TrafficLightServiceEnhancedTest { } @Test - void testUpdateDeviceHeartbeatByAddress() { + void testupdateDeviceHeartbeatByIpAddress() { // 测试基于IP地址和端口更新心跳时间 - when(trafficLightRepository.updateHeartbeatByIpAndPort(eq("192.168.1.100"), eq(8082), any(LocalDateTime.class))) + when(trafficLightRepository.updateHeartbeatByIpAddress(eq("192.168.1.100"), any(LocalDateTime.class))) .thenReturn(1); - boolean result = trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100", 8082); + boolean result = trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100"); assertTrue(result); - verify(trafficLightRepository).updateHeartbeatByIpAndPort(eq("192.168.1.100"), eq(8082), any(LocalDateTime.class)); + verify(trafficLightRepository).updateHeartbeatByIpAddress(eq("192.168.1.100"), any(LocalDateTime.class)); } @Test - void testUpdateDeviceHeartbeatByAddress_DeviceNotFound() { + void testupdateDeviceHeartbeatByIpAddress_DeviceNotFound() { // 测试设备不存在时更新心跳 - when(trafficLightRepository.updateHeartbeatByIpAndPort(eq("10.0.0.1"), eq(8080), any(LocalDateTime.class))) + when(trafficLightRepository.updateHeartbeatByIpAddress(eq("10.0.0.1"), any(LocalDateTime.class))) .thenReturn(0); - boolean result = trafficLightService.updateDeviceHeartbeatByAddress("10.0.0.1", 8080); + boolean result = trafficLightService.updateDeviceHeartbeatByAddress("10.0.0.1"); assertFalse(result); - verify(trafficLightRepository).updateHeartbeatByIpAndPort(eq("10.0.0.1"), eq(8080), any(LocalDateTime.class)); + verify(trafficLightRepository).updateHeartbeatByIpAddress(eq("10.0.0.1"), any(LocalDateTime.class)); } @Test void testUpdateDeviceOnlineStatusByAddress() { // 测试基于IP地址和端口更新在线状态 - when(trafficLightRepository.updateOnlineStatusByIpAndPort("192.168.1.100", 8082, true)) + when(trafficLightRepository.updateHeartbeatByIpAddress(eq("192.168.1.100"), any(LocalDateTime.class))) .thenReturn(1); - boolean result = trafficLightService.updateDeviceOnlineStatusByAddress("192.168.1.100", 8082, true); + boolean result = trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.100"); assertTrue(result); - verify(trafficLightRepository).updateOnlineStatusByIpAndPort("192.168.1.100", 8082, true); + verify(trafficLightRepository).updateHeartbeatByIpAddress(eq("192.168.1.100"), any(LocalDateTime.class)); } @Test @@ -218,7 +206,7 @@ class TrafficLightServiceEnhancedTest { when(trafficLightRepository.findByIpAddressPrefix("192.168.1")) .thenReturn(Arrays.asList(testDevice1, testDevice2)); - List result = trafficLightService.findDevicesByIpPrefix("192.168.1"); + List result = trafficLightService.findDevicesByPrefix("192.168.1"); assertEquals(2, result.size()); verify(trafficLightRepository).findByIpAddressPrefix("192.168.1"); @@ -245,7 +233,6 @@ class TrafficLightServiceEnhancedTest { .deviceId("TL_DEFAULT") .deviceName("默认IP设备") .ipAddress("0.0.0.0") - .port(8082) .intersectionId("INTERSECTION_003") .isActive(true) .build(); @@ -260,34 +247,6 @@ class TrafficLightServiceEnhancedTest { verify(trafficLightRepository).findDevicesWithDefaultIp(); } - @Test - void testFindDevicesByPort() { - // 测试根据端口号查找设备 - when(trafficLightRepository.findByPort(8082)) - .thenReturn(Arrays.asList(testDevice1)); - - List result = trafficLightService.findDevicesByPort(8082); - - assertEquals(1, result.size()); - assertEquals(Integer.valueOf(8082), result.get(0).getPort()); - verify(trafficLightRepository).findByPort(8082); - } - - @Test - void testFindDevicesByPort_InvalidPort() { - // 测试无效端口号 - List result1 = trafficLightService.findDevicesByPort(null); - assertTrue(result1.isEmpty()); - - List result2 = trafficLightService.findDevicesByPort(0); - assertTrue(result2.isEmpty()); - - List result3 = trafficLightService.findDevicesByPort(65536); - assertTrue(result3.isEmpty()); - - verifyNoInteractions(trafficLightRepository); - } - @Test void testGetStatisticsWithIpInfo() { // 测试获取包含IP地址信息的统计数据 @@ -318,19 +277,18 @@ class TrafficLightServiceEnhancedTest { // 测试验证包含IP地址的设备数据 TrafficLight validDevice = TrafficLight.builder() .ipAddress("192.168.1.100") - .port(8082) .deviceName("测试设备") .intersectionId("INTERSECTION_001") .build(); // 这个测试验证包含IP地址的设备数据验证逻辑 // 由于validateTrafficLightData是私有方法,我们通过getOrCreateDeviceByAddress来间接测试 - when(trafficLightRepository.findByIpAddressAndPort("192.168.1.100", 8082)).thenReturn(Optional.empty()); + when(trafficLightRepository.findByIpAddress("192.168.1.100")).thenReturn(null); when(trafficLightRepository.save(any(TrafficLight.class))).thenReturn(validDevice); // 这应该不会抛出异常,因为设备有有效的IP地址和端口 assertDoesNotThrow(() -> { - trafficLightService.getOrCreateDeviceByAddress("192.168.1.100", 8082, "INTERSECTION_001"); + trafficLightService.getOrCreateDeviceByAddress("192.168.1.100", "INTERSECTION_001"); }); } @@ -339,35 +297,31 @@ class TrafficLightServiceEnhancedTest { // 测试各种参数验证 // IP地址为空 - Optional result1 = trafficLightService.findDeviceByAddress(null, 8082); + Optional result1 = trafficLightService.findDeviceByAddress(null); assertTrue(result1.isEmpty()); - Optional result2 = trafficLightService.findDeviceByAddress("", 8082); + Optional result2 = trafficLightService.findDeviceByAddress(""); assertTrue(result2.isEmpty()); - // 端口为空 - Optional result3 = trafficLightService.findDeviceByAddress("192.168.1.1", null); - assertTrue(result3.isEmpty()); - // IP地址为空时的其他方法 - List result4 = trafficLightService.findDevicesByIpAddress(null); + List result4 = trafficLightService.findDevicesByAddress(null); assertTrue(result4.isEmpty()); - List result5 = trafficLightService.findDevicesByIpAddress(""); + List result5 = trafficLightService.findDevicesByAddress(""); assertTrue(result5.isEmpty()); // 心跳更新参数验证 - boolean result6 = trafficLightService.updateDeviceHeartbeatByAddress(null, 8082); + boolean result6 = trafficLightService.updateDeviceHeartbeatByAddress(null); assertFalse(result6); - boolean result7 = trafficLightService.updateDeviceHeartbeatByAddress("192.168.1.1", null); + boolean result7 = trafficLightService.updateDeviceHeartbeatByAddress(""); assertFalse(result7); // 在线状态更新参数验证 - boolean result8 = trafficLightService.updateDeviceOnlineStatusByAddress(null, 8082, true); + boolean result8 = trafficLightService.updateDeviceOnlineStatus(null, true); assertFalse(result8); - boolean result9 = trafficLightService.updateDeviceOnlineStatusByAddress("192.168.1.1", null, true); + boolean result9 = trafficLightService.updateDeviceOnlineStatus("", true); assertFalse(result9); verifyNoInteractions(trafficLightRepository); diff --git a/sql/add_ip_port_to_traffic_lights.sql b/sql/add_ip_port_to_traffic_lights.sql deleted file mode 100644 index c741fa6e..00000000 --- a/sql/add_ip_port_to_traffic_lights.sql +++ /dev/null @@ -1,115 +0,0 @@ --- 红绿灯IP地址增强功能数据库迁移脚本 --- 创建时间: 2025-01-06 --- 描述: 为traffic_lights表添加IP地址和端口字段,并修改device_id为可选字段 --- 版本: V1.1 - --- 开始事务 -BEGIN; - --- 1. 添加IP地址字段(必填,默认值为'0.0.0.0') -ALTER TABLE traffic_lights -ADD COLUMN IF NOT EXISTS ip_address VARCHAR(45) NOT NULL DEFAULT '0.0.0.0'; - --- 2. 添加端口字段(可选) -ALTER TABLE traffic_lights -ADD COLUMN IF NOT EXISTS port INTEGER; - --- 3. 修改device_id字段为可选(移除NOT NULL约束) --- 注意:PostgreSQL需要先检查是否存在NOT NULL约束 -DO $$ -BEGIN - -- 检查device_id字段是否有NOT NULL约束,如果有则移除 - IF EXISTS ( - SELECT 1 - FROM information_schema.columns - WHERE table_name = 'traffic_lights' - AND column_name = 'device_id' - AND is_nullable = 'NO' - ) THEN - ALTER TABLE traffic_lights ALTER COLUMN device_id DROP NOT NULL; - END IF; -END $$; - --- 4. 为现有记录设置默认值 --- 为现有记录设置默认IP地址和端口(如果字段为空) --- 为了避免唯一约束冲突,为每个记录生成唯一的IP地址和端口组合 -UPDATE traffic_lights -SET ip_address = '0.0.0.0' -WHERE ip_address IS NULL OR ip_address = ''; - --- 为现有记录生成唯一的端口号,避免重复 --- 使用记录ID + 8082作为基础端口,确保每个记录都有唯一的端口 -UPDATE traffic_lights -SET port = 8082 + (id % 1000) -- 使用ID的模运算确保端口在合理范围内 -WHERE port IS NULL; - --- 5. 创建唯一约束:IP地址和端口组合必须唯一 --- 先检查是否已存在该索引 -DO $$ -BEGIN - IF NOT EXISTS ( - SELECT 1 - FROM pg_indexes - WHERE tablename = 'traffic_lights' - AND indexname = 'idx_traffic_light_ip_port_unique' - ) THEN - CREATE UNIQUE INDEX idx_traffic_light_ip_port_unique - ON traffic_lights(ip_address, port); - END IF; -END $$; - --- 6. 创建IP地址索引(用于快速查询) -CREATE INDEX IF NOT EXISTS idx_traffic_light_ip_address -ON traffic_lights(ip_address); - --- 7. 添加字段注释 -COMMENT ON COLUMN traffic_lights.ip_address IS '红绿灯设备IP地址'; -COMMENT ON COLUMN traffic_lights.port IS '红绿灯设备端口号'; - --- 8. 验证数据完整性 --- 检查是否有重复的IP地址和端口组合 -DO $$ -DECLARE - duplicate_count INTEGER; -BEGIN - SELECT COUNT(*) INTO duplicate_count - FROM ( - SELECT ip_address, port, COUNT(*) as cnt - FROM traffic_lights - WHERE ip_address IS NOT NULL AND port IS NOT NULL - GROUP BY ip_address, port - HAVING COUNT(*) > 1 - ) duplicates; - - IF duplicate_count > 0 THEN - RAISE WARNING '发现 % 个重复的IP地址和端口组合,请检查数据', duplicate_count; - ELSE - RAISE NOTICE '数据完整性检查通过,没有发现重复的IP地址和端口组合'; - END IF; -END $$; - --- 9. 显示迁移结果 -DO $$ -DECLARE - total_records INTEGER; - records_with_ip INTEGER; - records_with_port INTEGER; - records_without_device_id INTEGER; -BEGIN - SELECT COUNT(*) INTO total_records FROM traffic_lights; - SELECT COUNT(*) INTO records_with_ip FROM traffic_lights WHERE ip_address IS NOT NULL AND ip_address != ''; - SELECT COUNT(*) INTO records_with_port FROM traffic_lights WHERE port IS NOT NULL; - SELECT COUNT(*) INTO records_without_device_id FROM traffic_lights WHERE device_id IS NULL OR device_id = ''; - - RAISE NOTICE '=== 数据库迁移完成 ==='; - RAISE NOTICE '总记录数: %', total_records; - RAISE NOTICE '有IP地址的记录数: %', records_with_ip; - RAISE NOTICE '有端口号的记录数: %', records_with_port; - RAISE NOTICE '没有设备ID的记录数: %', records_without_device_id; -END $$; - --- 提交事务 -COMMIT; - --- 迁移完成提示 -SELECT 'traffic_lights表IP地址增强迁移完成' AS migration_status; \ No newline at end of file diff --git a/sql/create_traffic_light_tables.sql b/sql/create_traffic_light_tables.sql index 91f5d5d8..60f9f868 100644 --- a/sql/create_traffic_light_tables.sql +++ b/sql/create_traffic_light_tables.sql @@ -21,8 +21,7 @@ CREATE TABLE IF NOT EXISTS traffic_lights ( id BIGSERIAL PRIMARY KEY, device_id VARCHAR(50) UNIQUE, -- 红绿灯设备编号(可选) device_name VARCHAR(100) NOT NULL, -- 设备名称 - ip_address VARCHAR(45) NOT NULL DEFAULT '0.0.0.0', -- 设备IP地址 - port INTEGER, -- 设备端口号 + ip_address VARCHAR(45) NOT NULL DEFAULT '0.0.0.0', -- 设备IP地址(唯一标识) intersection_id VARCHAR(50) NOT NULL, -- 关联的路口编号 device_type VARCHAR(20) DEFAULT 'STANDARD', -- 设备类型 manufacturer VARCHAR(50), -- 制造商 @@ -51,8 +50,7 @@ CREATE INDEX IF NOT EXISTS idx_traffic_light_device_id ON traffic_lights(device_ CREATE INDEX IF NOT EXISTS idx_traffic_light_intersection ON traffic_lights(intersection_id); CREATE INDEX IF NOT EXISTS idx_traffic_light_online ON traffic_lights(is_online); CREATE INDEX IF NOT EXISTS idx_traffic_light_active ON traffic_lights(is_active); -CREATE UNIQUE INDEX IF NOT EXISTS idx_traffic_light_ip_port_unique ON traffic_lights(ip_address, port); -CREATE INDEX IF NOT EXISTS idx_traffic_light_ip_address ON traffic_lights(ip_address); +CREATE UNIQUE INDEX IF NOT EXISTS idx_traffic_light_ip_unique ON traffic_lights(ip_address); -- 创建更新时间触发器函数 CREATE OR REPLACE FUNCTION update_updated_time_column() @@ -79,19 +77,13 @@ CREATE TRIGGER update_traffic_lights_updated_time INSERT INTO intersections (intersection_id, intersection_name, latitude, longitude, area_code, description) VALUES ('INTERSECTION_001', '主要路口1号', 39.9042, 116.4074, 'AREA_A', '机场主要路口,连接航站楼和跑道区域'), - ('INTERSECTION_002', '次要路口2号', 39.9050, 116.4080, 'AREA_B', '机场次要路口,连接货运区域'), - ('INTERSECTION_003', '货运区路口', 39.9035, 116.4065, 'AREA_C', '货运区域主要路口'), - ('INTERSECTION_004', '维修区路口', 39.9055, 116.4085, 'AREA_D', '维修区域路口'), - ('INTERSECTION_005', '停机坪路口', 39.9045, 116.4070, 'AREA_A', '停机坪入口路口') + ('INTERSECTION_002', '次要路口2号', 39.9050, 116.4080, 'AREA_B', '机场次要路口,连接货运区域') ON CONFLICT (intersection_id) DO NOTHING; -INSERT INTO traffic_lights (device_id, device_name, ip_address, port, intersection_id, device_type, manufacturer, model, install_date) +INSERT INTO traffic_lights (device_id, device_name, ip_address, intersection_id, device_type, manufacturer, model, install_date) VALUES - ('TL_001', '主路口红绿灯1号', '192.168.1.101', 8082, 'INTERSECTION_001', 'STANDARD', '海康威视', 'DS-TL100', '2024-01-01'), - ('TL_002', '次路口红绿灯2号', '192.168.1.102', 8082, 'INTERSECTION_002', 'STANDARD', '大华技术', 'DH-TL200', '2024-01-15'), - ('TL_003', '货运区红绿灯', '192.168.1.103', 8082, 'INTERSECTION_003', 'STANDARD', '海康威视', 'DS-TL100', '2024-02-01'), - ('TL_004', '维修区红绿灯', '192.168.1.104', 8082, 'INTERSECTION_004', 'HEAVY_DUTY', '大华技术', 'DH-TL300', '2024-02-15'), - ('TL_005', '停机坪红绿灯', '192.168.1.105', 8082, 'INTERSECTION_005', 'STANDARD', '海康威视', 'DS-TL100', '2024-03-01') + ('TL_001', '主路口红绿灯1号', '192.168.1.101', 'INTERSECTION_001', 'STANDARD', '海康威视', 'DS-TL100', '2024-01-01'), + ('TL_002', '次路口红绿灯2号', '192.168.1.102', 'INTERSECTION_002', 'STANDARD', '大华技术', 'DH-TL200', '2024-01-15') ON CONFLICT (device_id) DO NOTHING; -- 添加注释 @@ -107,7 +99,6 @@ COMMENT ON COLUMN intersections.area_code IS '所属区域编码'; COMMENT ON COLUMN traffic_lights.device_id IS '红绿灯设备唯一编号(可选)'; COMMENT ON COLUMN traffic_lights.device_name IS '设备名称'; COMMENT ON COLUMN traffic_lights.ip_address IS '红绿灯设备IP地址'; -COMMENT ON COLUMN traffic_lights.port IS '红绿灯设备端口号'; COMMENT ON COLUMN traffic_lights.intersection_id IS '关联的路口编号'; COMMENT ON COLUMN traffic_lights.is_online IS '设备是否在线'; COMMENT ON COLUMN traffic_lights.last_heartbeat IS '最后一次心跳时间'; \ No newline at end of file