From 180dea184539ba5c20a28b08a5a09583bf381a8b Mon Sep 17 00:00:00 2001 From: Tian jianyong <11429339@qq.com> Date: Fri, 27 Jun 2025 17:17:19 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8E=BB=E6=8E=89=E4=B8=8D=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E7=9A=84Websocket=E6=B6=88=E6=81=AF=E7=BC=93=E5=AD=98=EF=BC=8C?= =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=95=B0=E6=8D=AE=E5=BA=93=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 3 +- .../VehicleControlExceptionHandler.java | 2 - .../common/model/dto/Response.java | 8 +- .../common/service/AirportAreaService.java | 3 - .../common/service/SpatialQueryService.java | 4 - .../service/VehicleLocationCacheService.java | 5 +- .../service/VehiclePermissionService.java | 24 -- .../dataCollector/dao/DataCollectorDao.java | 5 +- .../service/DataCollectorService.java | 26 ++ .../UnmannedVehicleControlService.java | 17 +- .../service/VehicleTypePermissionService.java | 2 - .../WebSocketMessageBroadcaster.java | 11 +- .../webSocket/cache/MessageCacheService.java | 1 - ...001__initialize_collision_avoidance_db.sql | 281 ++++++++++++++++++ .../V001__Enable_PostGIS.sql | 0 .../V002__create_core_tables.sql | 0 .../V003__create_vehicle_commands_table.sql | 0 .../V006__clean_airport_areas_table.sql | 0 .../V007__create_unified_rule_engine.sql | 0 .../V008__fix_rule_violation_events_table.sql | 0 ...009__fix_spatial_rules_table_structure.sql | 0 .../AirportAreaServiceIntegrationTest.java | 2 +- .../repository/AirportAreaRepositoryTest.java | 4 +- .../VehicleLocationRepositoryTest.java | 2 +- .../UnmannedVehicleControllerTest.java | 3 +- ...DataPersistenceServiceIntegrationTest.java | 5 - .../service/RuleExecutionEngineTest.java | 6 +- .../RuleViolationRealtimePushTest.java | 5 +- .../service/SpatialRuleIntegrationTest.java | 4 - .../RoadNetworkServiceIntegrationTest.java | 8 - .../webSocket/event/WebSocketEventTest.java | 3 - src/test/resources/application-test.yml | 31 -- tools/mock_server.py | 125 ++++---- 33 files changed, 410 insertions(+), 180 deletions(-) create mode 100644 src/main/resources/db/migration/V001__initialize_collision_avoidance_db.sql rename src/main/resources/{db/migration => sql}/V001__Enable_PostGIS.sql (100%) rename src/main/resources/{db/migration => sql}/V002__create_core_tables.sql (100%) rename src/main/resources/{db/migration => sql}/V003__create_vehicle_commands_table.sql (100%) rename src/main/resources/{db/migration => sql}/V006__clean_airport_areas_table.sql (100%) rename src/main/resources/{db/migration => sql}/V007__create_unified_rule_engine.sql (100%) rename src/main/resources/{db/migration => sql}/V008__fix_rule_violation_events_table.sql (100%) rename src/main/resources/{db/migration => sql}/V009__fix_spatial_rules_table_structure.sql (100%) diff --git a/.gitignore b/.gitignore index 92b8ce5..491e566 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ __pycache__/ .venv/ ### 日志 ### -logs/ \ No newline at end of file +logs/ +*.log \ No newline at end of file diff --git a/src/main/java/com/dongni/collisionavoidance/common/exception/VehicleControlExceptionHandler.java b/src/main/java/com/dongni/collisionavoidance/common/exception/VehicleControlExceptionHandler.java index dfd43e4..8f6185e 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/exception/VehicleControlExceptionHandler.java +++ b/src/main/java/com/dongni/collisionavoidance/common/exception/VehicleControlExceptionHandler.java @@ -9,11 +9,9 @@ import org.springframework.validation.FieldError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; import jakarta.validation.ConstraintViolation; import jakarta.validation.ConstraintViolationException; -import java.time.LocalDateTime; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; diff --git a/src/main/java/com/dongni/collisionavoidance/common/model/dto/Response.java b/src/main/java/com/dongni/collisionavoidance/common/model/dto/Response.java index 1378799..06d798d 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/model/dto/Response.java +++ b/src/main/java/com/dongni/collisionavoidance/common/model/dto/Response.java @@ -23,7 +23,7 @@ public class Response { /** * 响应消息 */ - private String message; + private String msg; /** * 响应数据 @@ -36,7 +36,7 @@ public class Response { public static Response success(T data) { return Response.builder() .status(200) - .message("操作成功") + .msg("操作成功") .data(data) .build(); } @@ -47,7 +47,7 @@ public class Response { public static Response success(String message, T data) { return Response.builder() .status(200) - .message(message) + .msg(message) .data(data) .build(); } @@ -58,7 +58,7 @@ public class Response { public static Response error(Integer status, String message) { return Response.builder() .status(status) - .message(message) + .msg(message) .build(); } diff --git a/src/main/java/com/dongni/collisionavoidance/common/service/AirportAreaService.java b/src/main/java/com/dongni/collisionavoidance/common/service/AirportAreaService.java index 94abcff..9577330 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/service/AirportAreaService.java +++ b/src/main/java/com/dongni/collisionavoidance/common/service/AirportAreaService.java @@ -2,7 +2,6 @@ package com.dongni.collisionavoidance.common.service; import com.dongni.collisionavoidance.common.model.spatial.AirportArea; import com.dongni.collisionavoidance.common.model.repository.AirportAreaRepository; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.locationtech.jts.geom.Coordinate; @@ -31,8 +30,6 @@ public class AirportAreaService { private final AirportAreaRepository airportAreaRepository; private final GeometryFactory geometryFactory = new GeometryFactory(); private final WKTReader wktReader = new WKTReader(geometryFactory); - private final ObjectMapper objectMapper = new ObjectMapper(); - /** * 保存机场区域配置 */ diff --git a/src/main/java/com/dongni/collisionavoidance/common/service/SpatialQueryService.java b/src/main/java/com/dongni/collisionavoidance/common/service/SpatialQueryService.java index f3b3ff9..fd2b14a 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/service/SpatialQueryService.java +++ b/src/main/java/com/dongni/collisionavoidance/common/service/SpatialQueryService.java @@ -5,7 +5,6 @@ import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.locationtech.jts.geom.*; -import org.locationtech.jts.io.WKTReader; import org.springframework.stereotype.Service; import java.time.LocalDateTime; @@ -23,9 +22,6 @@ public class SpatialQueryService { private final VehicleLocationService vehicleLocationService; private final AirportAreaService airportAreaService; - private final GeometryFactory geometryFactory = new GeometryFactory(); - private final WKTReader wktReader = new WKTReader(geometryFactory); - /** * 综合空间查询结果 */ diff --git a/src/main/java/com/dongni/collisionavoidance/common/service/VehicleLocationCacheService.java b/src/main/java/com/dongni/collisionavoidance/common/service/VehicleLocationCacheService.java index 2d46919..e917160 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/service/VehicleLocationCacheService.java +++ b/src/main/java/com/dongni/collisionavoidance/common/service/VehicleLocationCacheService.java @@ -117,7 +117,10 @@ public class VehicleLocationCacheService { .collect(Collectors.toList()); List locations = vehicleLocationRedisTemplate.opsForValue().multiGet(keys); - + if (locations == null) { + return Collections.emptyMap(); + } + Map resultMap = new HashMap<>(); for (int i = 0; i < vehicleIds.size(); i++) { VehicleLocation location = locations.get(i); diff --git a/src/main/java/com/dongni/collisionavoidance/common/service/VehiclePermissionService.java b/src/main/java/com/dongni/collisionavoidance/common/service/VehiclePermissionService.java index a45694a..579f59d 100644 --- a/src/main/java/com/dongni/collisionavoidance/common/service/VehiclePermissionService.java +++ b/src/main/java/com/dongni/collisionavoidance/common/service/VehiclePermissionService.java @@ -6,9 +6,6 @@ import com.dongni.collisionavoidance.common.model.spatial.AirportArea; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; -import java.util.Arrays; -import java.util.List; - /** * 车辆权限管理服务 * 处理特勤车辆的特殊权限和业务规则 @@ -223,25 +220,4 @@ public class VehiclePermissionService { return "未知权限"; } - - /** - * 解析允许的车辆类型JSON数组 - */ - private List parseAllowedVehicleTypes(String allowedVehicleTypesJson) { - if (allowedVehicleTypesJson == null || allowedVehicleTypesJson.trim().isEmpty()) { - return List.of(); - } - - try { - // 简单的JSON数组解析,实际项目中可以使用Jackson - String cleaned = allowedVehicleTypesJson.replaceAll("[\\[\\]\"\\s]", ""); - if (cleaned.isEmpty()) { - return List.of(); - } - return Arrays.asList(cleaned.split(",")); - } catch (Exception e) { - log.warn("解析允许车辆类型失败: {}", allowedVehicleTypesJson, e); - return List.of(); - } - } } \ No newline at end of file diff --git a/src/main/java/com/dongni/collisionavoidance/dataCollector/dao/DataCollectorDao.java b/src/main/java/com/dongni/collisionavoidance/dataCollector/dao/DataCollectorDao.java index 0928ab2..00f12ec 100644 --- a/src/main/java/com/dongni/collisionavoidance/dataCollector/dao/DataCollectorDao.java +++ b/src/main/java/com/dongni/collisionavoidance/dataCollector/dao/DataCollectorDao.java @@ -115,7 +115,6 @@ public class DataCollectorDao { * @return 无人车位置信息列表 */ public List getVehicleLocationInfo() { - System.out.println("接口被调用"); try { String url = UriComponentsBuilder .fromUriString(vehicleBaseUrl) @@ -137,12 +136,12 @@ public class DataCollectorDao { if (response.getStatusCode().is2xxSuccessful()) { List responseBody = response.getBody(); if (responseBody != null) { - log.info("成功获取车辆定位信息,数量: {}", responseBody.size()); + log.info("成功获取无人车定位信息,数量: {}", responseBody.size()); return responseBody; } } } catch (Exception e) { - log.error("获取车辆定位信息时发生异常", e); + log.error("获取无人车定位信息时发生异常", e); return Collections.emptyList(); } return Collections.emptyList(); diff --git a/src/main/java/com/dongni/collisionavoidance/dataCollector/service/DataCollectorService.java b/src/main/java/com/dongni/collisionavoidance/dataCollector/service/DataCollectorService.java index cf69c3e..412392a 100644 --- a/src/main/java/com/dongni/collisionavoidance/dataCollector/service/DataCollectorService.java +++ b/src/main/java/com/dongni/collisionavoidance/dataCollector/service/DataCollectorService.java @@ -208,6 +208,32 @@ public class DataCollectorService { log.info("开始处理 {} 条无人车数据", unmannedVehicles.size()); + // 先推送WebSocket位置更新事件 + for (UnmannedVehicle vehicle : unmannedVehicles) { + try { + PositionUpdatePayload.Position position = PositionUpdatePayload.Position.builder() + .latitude(vehicle.getCurrentPosition().getLatitude()) + .longitude(vehicle.getCurrentPosition().getLongitude()) + .build(); + PositionUpdatePayload payload = PositionUpdatePayload.builder() + .objectId(vehicle.getVehicleId()) + .objectType(MovingObjectType.UNMANNED_VEHICLE.name()) + .position(position) + .heading(vehicle.getHeading()) + .speed(vehicle.getVelocity() != null ? vehicle.getVelocity().getSpeed() : null) + .timestamp(System.currentTimeMillis() * 1000) // 微秒级时间戳 + .build(); + eventPublisher.publishEvent(new PositionUpdateEvent(payload)); + log.debug("处理无人车数据并发布事件: {} (车辆ID: {}, 位置: {}, {})", + vehicle.getVehicleId(), + vehicle.getVehicleId(), + vehicle.getCurrentPosition().getLongitude(), + vehicle.getCurrentPosition().getLatitude()); + } catch (Exception e) { + log.error("处理无人车数据异常: vehicleId={}", vehicle.getVehicleId(), e); + } + } + // 转换为VehicleLocation对象列表 List vehicleLocations = unmannedVehicles.stream() diff --git a/src/main/java/com/dongni/collisionavoidance/dataCollector/service/UnmannedVehicleControlService.java b/src/main/java/com/dongni/collisionavoidance/dataCollector/service/UnmannedVehicleControlService.java index f3b22c1..cb4732c 100644 --- a/src/main/java/com/dongni/collisionavoidance/dataCollector/service/UnmannedVehicleControlService.java +++ b/src/main/java/com/dongni/collisionavoidance/dataCollector/service/UnmannedVehicleControlService.java @@ -254,13 +254,20 @@ public class UnmannedVehicleControlService { ResponseEntity response = restTemplate.exchange( url, HttpMethod.POST, requestEntity, VehicleStateInfo[].class); + - if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null && response.getBody().length > 0) { - VehicleStateInfo stateInfo = response.getBody()[0]; - logger.info("从外部系统获取车辆状态成功: vehicleId={}", vehicleId); - return stateInfo; + if (response != null && response.getStatusCode().is2xxSuccessful()) { + VehicleStateInfo[] body = response.getBody(); + if (body != null && body.length > 0) { + VehicleStateInfo stateInfo = body[0]; + logger.info("从外部系统获取车辆状态成功: vehicleId={}", vehicleId); + return stateInfo; + } else { + logger.warn("外部系统未返回车辆状态: vehicleId={}", vehicleId); + return null; + } } else { - logger.warn("外部系统未返回车辆状态: vehicleId={}", vehicleId); + logger.warn("外部系统调用失败: vehicleId={}, status={}", vehicleId, response != null ? response.getStatusCode() : "无响应"); return null; } diff --git a/src/main/java/com/dongni/collisionavoidance/rule/service/VehicleTypePermissionService.java b/src/main/java/com/dongni/collisionavoidance/rule/service/VehicleTypePermissionService.java index 8f1e20c..087a05e 100644 --- a/src/main/java/com/dongni/collisionavoidance/rule/service/VehicleTypePermissionService.java +++ b/src/main/java/com/dongni/collisionavoidance/rule/service/VehicleTypePermissionService.java @@ -4,8 +4,6 @@ import com.dongni.collisionavoidance.common.model.MovingObjectType; import com.dongni.collisionavoidance.rule.model.entity.SpatialRule; import com.dongni.collisionavoidance.rule.model.enums.RuleCategory; -import org.locationtech.jts.geom.Point; - import java.time.LocalDateTime; import java.util.List; import java.util.Set; diff --git a/src/main/java/com/dongni/collisionavoidance/webSocket/broadcaster/WebSocketMessageBroadcaster.java b/src/main/java/com/dongni/collisionavoidance/webSocket/broadcaster/WebSocketMessageBroadcaster.java index 2bca65a..1b8a349 100644 --- a/src/main/java/com/dongni/collisionavoidance/webSocket/broadcaster/WebSocketMessageBroadcaster.java +++ b/src/main/java/com/dongni/collisionavoidance/webSocket/broadcaster/WebSocketMessageBroadcaster.java @@ -22,7 +22,6 @@ import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Component; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; -import org.springframework.beans.factory.annotation.Value; import java.util.UUID; @@ -39,10 +38,6 @@ public class WebSocketMessageBroadcaster { // 统一推送主题,控制台接收所有消息 private static final String UNIFIED_TOPIC = "/topic/realtime"; - // 是否启用消息缓存(测试环境中禁用以避免序列化问题) - @Value("${websocket.message.cache.enabled:true}") - private boolean messageCacheEnabled; - @Autowired public WebSocketMessageBroadcaster( SimpMessagingTemplate messagingTemplate, @@ -174,10 +169,8 @@ public class WebSocketMessageBroadcaster { // 立即推送到控制台 messagingTemplate.convertAndSend(UNIFIED_TOPIC, message); - // 缓存消息用于重连恢复(测试环境中可能禁用) - if (messageCacheEnabled) { - messageCacheService.cacheMessage(message); - } + // 缓存消息用于重连恢复 + messageCacheService.cacheMessage(message); } catch (Exception e) { System.err.println("Failed to broadcast message: " + e.getMessage()); diff --git a/src/main/java/com/dongni/collisionavoidance/webSocket/cache/MessageCacheService.java b/src/main/java/com/dongni/collisionavoidance/webSocket/cache/MessageCacheService.java index fb7b31b..e2d1360 100644 --- a/src/main/java/com/dongni/collisionavoidance/webSocket/cache/MessageCacheService.java +++ b/src/main/java/com/dongni/collisionavoidance/webSocket/cache/MessageCacheService.java @@ -57,7 +57,6 @@ public class MessageCacheService { * @param count 获取消息数量 * @return 最近的消息列表 */ - @SuppressWarnings("unchecked") public List> getRecentMessages(String messageType, int count) { try { String key = CACHE_KEY_PREFIX + messageType; diff --git a/src/main/resources/db/migration/V001__initialize_collision_avoidance_db.sql b/src/main/resources/db/migration/V001__initialize_collision_avoidance_db.sql new file mode 100644 index 0000000..3d5c4ca --- /dev/null +++ b/src/main/resources/db/migration/V001__initialize_collision_avoidance_db.sql @@ -0,0 +1,281 @@ +-- ============================================ +-- ⚠️ 脚本执行前警告:本操作将删除并重建整个数据库,所有原有数据将丢失! +-- 如仅需初始化结构,请注释掉下方 DROP/CREATE DATABASE 语句。 +-- ============================================ + +-- 0. 删除并重建数据库(请根据实际数据库名修改) +-- 需以超级用户身份连接到postgres或其他管理数据库后执行: +-- +-- DROP DATABASE IF EXISTS collision_avoidance; +-- CREATE DATABASE collision_avoidance WITH ENCODING='UTF8' TEMPLATE=template0; +-- \c collision_avoidance + +-- ============================================ +-- 碰撞避免系统数据库重建脚本(PostgreSQL 17 + PostGIS) +-- 适用场景:全新初始化/重建,确保所有表结构、索引、触发器、注释、权限一次性到位 +-- 依赖扩展:postgis, postgis_topology +-- 执行权限:需超级用户权限(启用扩展),后续表操作可用普通DBA权限 +-- ============================================ + +-- 1. 启用PostGIS扩展 +CREATE EXTENSION IF NOT EXISTS postgis; +CREATE EXTENSION IF NOT EXISTS postgis_topology; + +-- 2. 通用函数与触发器 +CREATE OR REPLACE FUNCTION update_updated_at_column() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION cleanup_old_vehicle_locations() +RETURNS INTEGER AS $$ +DECLARE + deleted_count INTEGER; +BEGIN + DELETE FROM vehicle_locations + WHERE created_at < CURRENT_TIMESTAMP - INTERVAL '30 days'; + GET DIAGNOSTICS deleted_count = ROW_COUNT; + RAISE NOTICE 'Cleaned up % old vehicle location records', deleted_count; + RETURN deleted_count; +END; +$$ LANGUAGE plpgsql; + +-- 3. 车辆位置表 +CREATE TABLE vehicle_locations ( + id BIGSERIAL PRIMARY KEY, + vehicle_id VARCHAR(50) NOT NULL, + vehicle_type VARCHAR(20) NOT NULL CHECK (vehicle_type IN ('AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE')), + location GEOMETRY(POINT, 4326) NOT NULL, + altitude DOUBLE PRECISION, + heading DOUBLE PRECISION CHECK (heading >= 0 AND heading < 360), + speed DOUBLE PRECISION CHECK (speed >= 0), + timestamp TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + data_quality VARCHAR(20), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX idx_vehicle_locations_vehicle_id ON vehicle_locations(vehicle_id); +CREATE INDEX idx_vehicle_locations_vehicle_type ON vehicle_locations(vehicle_type); +CREATE INDEX idx_vehicle_locations_timestamp ON vehicle_locations(timestamp DESC); +CREATE INDEX idx_vehicle_locations_created_at ON vehicle_locations(created_at); +CREATE INDEX idx_vehicle_locations_location_gist ON vehicle_locations USING GIST(location); +CREATE INDEX idx_vehicle_locations_compound ON vehicle_locations(vehicle_id, timestamp DESC); +CREATE INDEX idx_vehicle_locations_type_time ON vehicle_locations(vehicle_type, timestamp DESC); +CREATE TRIGGER trigger_vehicle_locations_updated_at + BEFORE UPDATE ON vehicle_locations + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); + +-- 4. 机场区域表(纯地理属性) +CREATE TABLE airport_areas ( + id BIGSERIAL PRIMARY KEY, + area_id VARCHAR(50) NOT NULL UNIQUE, + name VARCHAR(100) NOT NULL, + type VARCHAR(30) NOT NULL, + description VARCHAR(500), + boundary GEOMETRY(POLYGON, 4326) NOT NULL, + active_time TIMESTAMP WITH TIME ZONE, + expiry_time TIMESTAMP WITH TIME ZONE, + enabled BOOLEAN NOT NULL DEFAULT true, + priority INTEGER NOT NULL DEFAULT 1 CHECK (priority >= 1), + version BIGINT NOT NULL DEFAULT 1, + area_sqm DOUBLE PRECISION, + functional_category VARCHAR(50), + parent_area_id VARCHAR(50), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX idx_airport_areas_area_id ON airport_areas(area_id); +CREATE INDEX idx_airport_areas_name ON airport_areas(name); +CREATE INDEX idx_airport_areas_type ON airport_areas(type); +CREATE INDEX idx_airport_areas_priority ON airport_areas(priority DESC); +CREATE INDEX idx_airport_areas_enabled ON airport_areas(enabled); +CREATE INDEX idx_airport_areas_version ON airport_areas(version); +CREATE INDEX idx_airport_areas_boundary_gist ON airport_areas USING GIST(boundary); +CREATE INDEX idx_airport_areas_type_enabled ON airport_areas(type, enabled); +CREATE INDEX idx_airport_areas_enabled_priority ON airport_areas(enabled, priority DESC); +CREATE INDEX idx_airport_areas_functional_category ON airport_areas(functional_category); +CREATE INDEX idx_airport_areas_parent_area_id ON airport_areas(parent_area_id); +CREATE TRIGGER trigger_airport_areas_updated_at + BEFORE UPDATE ON airport_areas + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); +COMMENT ON TABLE airport_areas IS '机场区域表 - 纯地理功能概念,定义机场内各个区域的地理边界和功能属性。安全控制规则在geofences表中配置。'; + +-- 5. 车辆轨迹表 +CREATE TABLE vehicle_trajectories ( + id BIGSERIAL PRIMARY KEY, + vehicle_id VARCHAR(50) NOT NULL, + trajectory_date DATE NOT NULL, + trajectory_line GEOMETRY(LINESTRING, 4326), + total_distance DOUBLE PRECISION, + max_speed DOUBLE PRECISION, + avg_speed DOUBLE PRECISION, + duration_seconds INTEGER, + point_count INTEGER, + start_time TIMESTAMP WITH TIME ZONE NOT NULL, + end_time TIMESTAMP WITH TIME ZONE NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX idx_vehicle_trajectories_vehicle_id ON vehicle_trajectories(vehicle_id); +CREATE INDEX idx_vehicle_trajectories_date ON vehicle_trajectories(trajectory_date DESC); +CREATE INDEX idx_vehicle_trajectories_start_time ON vehicle_trajectories(start_time DESC); +CREATE INDEX idx_vehicle_trajectories_line_gist ON vehicle_trajectories USING GIST(trajectory_line); +CREATE INDEX idx_vehicle_trajectories_compound ON vehicle_trajectories(vehicle_id, trajectory_date DESC); + +-- 6. 无人车控制指令表 +CREATE TABLE vehicle_commands ( + id BIGSERIAL PRIMARY KEY, + trans_id VARCHAR(100) NOT NULL, + timestamp TIMESTAMP NOT NULL, + vehicle_id VARCHAR(50) NOT NULL, + command_type VARCHAR(20) NOT NULL CHECK (command_type IN ('ALERT', 'SIGNAL', 'WARNING', 'RESUME', 'PARKING')), + command_reason VARCHAR(30) NOT NULL CHECK (command_reason IN ('TRAFFIC_LIGHT', 'AIRCRAFT_CROSSING', 'SPECIAL_VEHICLE', 'AIRCRAFT_PUSH', 'RESUME_TRAFFIC', 'PARKING_SIDE')), + signal_state VARCHAR(10) CHECK (signal_state IN ('RED', 'YELLOW', 'GREEN')), + intersection_id VARCHAR(50), + target_location GEOMETRY(POINT, 4326) NOT NULL, + relative_speed DOUBLE PRECISION, + relative_motion_x DOUBLE PRECISION, + relative_motion_y DOUBLE PRECISION, + min_distance DOUBLE PRECISION, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); +CREATE INDEX idx_vehicle_commands_vehicle_id ON vehicle_commands(vehicle_id); +CREATE INDEX idx_vehicle_commands_timestamp ON vehicle_commands(timestamp DESC); +CREATE INDEX idx_vehicle_commands_trans_id ON vehicle_commands(trans_id); +CREATE INDEX idx_vehicle_commands_command_type ON vehicle_commands(command_type); +CREATE INDEX idx_vehicle_commands_created_at ON vehicle_commands(created_at); +CREATE INDEX idx_vehicle_commands_location_gist ON vehicle_commands USING GIST(target_location); +CREATE INDEX idx_vehicle_commands_vehicle_time ON vehicle_commands(vehicle_id, timestamp DESC); +CREATE INDEX idx_vehicle_commands_type_time ON vehicle_commands(command_type, timestamp DESC); +COMMENT ON TABLE vehicle_commands IS '无人车控制指令表,用于存储发送给无人车的控制指令,支持轨迹回放和日志审计'; + +-- 7. 空间规则表(以V009为准) +CREATE TABLE spatial_rules ( + rule_id VARCHAR(50) PRIMARY KEY, + rule_name VARCHAR(200) NOT NULL, + description TEXT, + rule_category VARCHAR(50) NOT NULL CHECK (rule_category IN ( + 'ACCESS_CONTROL', 'SPEED_LIMIT', 'VEHICLE_RESTRICTION', + 'HEIGHT_LIMIT', 'WEIGHT_LIMIT', 'TIME_RESTRICTION', + 'PRIORITY_CONTROL', 'SAFETY_ZONE', 'CUSTOM' + )), + status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE' CHECK (status IN ( + 'ACTIVE', 'INACTIVE', 'SUSPENDED', 'EXPIRED' + )), + priority INTEGER NOT NULL DEFAULT 1 CHECK (priority >= 1), + spatial_object_type VARCHAR(30) NOT NULL CHECK (spatial_object_type IN ( + 'AREA', 'ROAD', 'INTERSECTION', 'CUSTOM_GEOMETRY' + )), + spatial_object_id VARCHAR(50), + custom_geometry GEOMETRY(POLYGON, 4326), + rule_parameters JSONB DEFAULT '{}', + alert_level VARCHAR(20) NOT NULL DEFAULT 'WARNING' CHECK (alert_level IN ( + 'INFO', 'WARNING', 'CRITICAL', 'EMERGENCY' + )), + alert_message VARCHAR(500), + effective_start_time TIMESTAMP, + effective_end_time TIMESTAMP, + daily_start_time TIME, + daily_end_time TIME, + weekday_pattern INTEGER, + time_patterns JSONB, + created_by VARCHAR(100), + updated_by VARCHAR(100), + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + version BIGINT NOT NULL DEFAULT 1 +); +CREATE INDEX idx_spatial_rules_status ON spatial_rules(status); +CREATE INDEX idx_spatial_rules_category ON spatial_rules(rule_category); +CREATE INDEX idx_spatial_rules_object_type ON spatial_rules(spatial_object_type); +CREATE INDEX idx_spatial_rules_priority ON spatial_rules(priority DESC); +CREATE INDEX idx_spatial_rules_spatial_object ON spatial_rules(spatial_object_type, spatial_object_id); +CREATE INDEX idx_spatial_rules_effective_time ON spatial_rules(effective_start_time, effective_end_time); +CREATE INDEX idx_spatial_rules_daily_time ON spatial_rules(daily_start_time, daily_end_time); +CREATE INDEX idx_spatial_rules_custom_geometry_gist ON spatial_rules USING GIST(custom_geometry); +CREATE INDEX idx_spatial_rules_parameters_gin ON spatial_rules USING GIN(rule_parameters); +CREATE INDEX idx_spatial_rules_time_patterns_gin ON spatial_rules USING GIN(time_patterns); +CREATE TRIGGER trigger_spatial_rules_updated_at + BEFORE UPDATE ON spatial_rules + FOR EACH ROW + EXECUTE FUNCTION update_updated_at_column(); +COMMENT ON TABLE spatial_rules IS '空间规则表 - 统一管理所有空间安全规则,支持多层次时间控制'; + +-- 8. 空间规则允许车辆类型关联表 +CREATE TABLE spatial_rule_vehicle_types ( + rule_id VARCHAR(50) NOT NULL, + vehicle_type VARCHAR(30) NOT NULL CHECK (vehicle_type IN ( + 'AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE' + )), + PRIMARY KEY (rule_id, vehicle_type), + FOREIGN KEY (rule_id) REFERENCES spatial_rules(rule_id) ON DELETE CASCADE +); +CREATE INDEX idx_spatial_rule_vehicle_types_rule_id ON spatial_rule_vehicle_types(rule_id); +CREATE INDEX idx_spatial_rule_vehicle_types_vehicle_type ON spatial_rule_vehicle_types(vehicle_type); +COMMENT ON TABLE spatial_rule_vehicle_types IS '空间规则允许车辆类型关联表'; + +-- 9. 规则违规事件表 +CREATE TABLE rule_violation_events ( + event_id VARCHAR(36) PRIMARY KEY, + rule_id VARCHAR(50) NOT NULL, + vehicle_id VARCHAR(50) NOT NULL, + vehicle_type VARCHAR(30) NOT NULL CHECK (vehicle_type IN ( + 'AIRCRAFT', 'AIRPORT_VEHICLE', 'UNMANNED_VEHICLE', 'SPECIAL_VEHICLE' + )), + violation_type VARCHAR(50) NOT NULL, + alert_level VARCHAR(20) NOT NULL CHECK (alert_level IN ( + 'INFO', 'WARNING', 'CRITICAL', 'EMERGENCY' + )), + violation_timestamp TIMESTAMP NOT NULL, + violation_location GEOMETRY(POINT, 4326), + violation_description TEXT, + vehicle_state JSONB, + rule_details JSONB, + severity_score INTEGER NOT NULL, + processed BOOLEAN NOT NULL DEFAULT FALSE, + processed_at TIMESTAMP, + processed_by VARCHAR(100), + processing_result TEXT, + response_actions JSONB, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + metadata JSONB +); +CREATE INDEX idx_violation_events_vehicle_id ON rule_violation_events(vehicle_id); +CREATE INDEX idx_violation_events_rule_id ON rule_violation_events(rule_id); +CREATE INDEX idx_violation_events_violation_type ON rule_violation_events(violation_type); +CREATE INDEX idx_violation_events_alert_level ON rule_violation_events(alert_level); +CREATE INDEX idx_violation_events_timestamp ON rule_violation_events(violation_timestamp DESC); +CREATE INDEX idx_violation_events_processed ON rule_violation_events(processed); +CREATE INDEX idx_violation_events_location_gist ON rule_violation_events USING GIST(violation_location); +COMMENT ON TABLE rule_violation_events IS '规则违规事件表 - 记录所有违规事件及其处理状态'; + +-- 10. 权限设置(可选) +-- GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO collision_avoidance_app; +-- GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO collision_avoidance_app; + +-- 11. 示例数据(可选,按需取消注释) +/* +INSERT INTO airport_areas (area_id, name, type, description, boundary, priority, enabled, functional_category) VALUES +('runway_01', 'Runway 01', 'RUNWAY', '主跑道01,长度3200米', + ST_GeomFromText('POLYGON((116.5 39.8, 116.52 39.8, 116.52 39.81, 116.5 39.81, 116.5 39.8))', 4326), + 10, true, 'AIRCRAFT_OPERATIONS'), +('taxiway_a', 'Taxiway A', 'TAXIWAY', '连接跑道和停机坪的滑行道A', + ST_GeomFromText('POLYGON((116.51 39.79, 116.515 39.79, 116.515 39.795, 116.51 39.795, 116.51 39.79))', 4326), + 5, true, 'AIRCRAFT_MOVEMENT'), +('terminal_area', 'Terminal Area', 'TERMINAL', 'T1航站楼及周边区域', + ST_GeomFromText('POLYGON((116.48 39.78, 116.50 39.78, 116.50 39.80, 116.48 39.80, 116.48 39.78))', 4326), + 3, true, 'PASSENGER_SERVICES'), +('temp_construction_01', 'Temporary Construction Zone', 'TEMPORARY_CONSTRUCTION', '滑行道扩建临时施工区域', + ST_GeomFromText('POLYGON((116.49 39.785, 116.495 39.785, 116.495 39.790, 116.49 39.790, 116.49 39.785))', 4326), + 1, true, 'CONSTRUCTION'); +*/ + +-- 12. 结尾说明 +-- ============================================ +-- 数据库结构初始化完成。请根据实际业务需要补充数据、权限和后续配置。 +-- ============================================ \ No newline at end of file diff --git a/src/main/resources/db/migration/V001__Enable_PostGIS.sql b/src/main/resources/sql/V001__Enable_PostGIS.sql similarity index 100% rename from src/main/resources/db/migration/V001__Enable_PostGIS.sql rename to src/main/resources/sql/V001__Enable_PostGIS.sql diff --git a/src/main/resources/db/migration/V002__create_core_tables.sql b/src/main/resources/sql/V002__create_core_tables.sql similarity index 100% rename from src/main/resources/db/migration/V002__create_core_tables.sql rename to src/main/resources/sql/V002__create_core_tables.sql diff --git a/src/main/resources/db/migration/V003__create_vehicle_commands_table.sql b/src/main/resources/sql/V003__create_vehicle_commands_table.sql similarity index 100% rename from src/main/resources/db/migration/V003__create_vehicle_commands_table.sql rename to src/main/resources/sql/V003__create_vehicle_commands_table.sql diff --git a/src/main/resources/db/migration/V006__clean_airport_areas_table.sql b/src/main/resources/sql/V006__clean_airport_areas_table.sql similarity index 100% rename from src/main/resources/db/migration/V006__clean_airport_areas_table.sql rename to src/main/resources/sql/V006__clean_airport_areas_table.sql diff --git a/src/main/resources/db/migration/V007__create_unified_rule_engine.sql b/src/main/resources/sql/V007__create_unified_rule_engine.sql similarity index 100% rename from src/main/resources/db/migration/V007__create_unified_rule_engine.sql rename to src/main/resources/sql/V007__create_unified_rule_engine.sql diff --git a/src/main/resources/db/migration/V008__fix_rule_violation_events_table.sql b/src/main/resources/sql/V008__fix_rule_violation_events_table.sql similarity index 100% rename from src/main/resources/db/migration/V008__fix_rule_violation_events_table.sql rename to src/main/resources/sql/V008__fix_rule_violation_events_table.sql diff --git a/src/main/resources/db/migration/V009__fix_spatial_rules_table_structure.sql b/src/main/resources/sql/V009__fix_spatial_rules_table_structure.sql similarity index 100% rename from src/main/resources/db/migration/V009__fix_spatial_rules_table_structure.sql rename to src/main/resources/sql/V009__fix_spatial_rules_table_structure.sql diff --git a/src/test/java/com/dongni/collisionavoidance/areas/service/AirportAreaServiceIntegrationTest.java b/src/test/java/com/dongni/collisionavoidance/areas/service/AirportAreaServiceIntegrationTest.java index fb2fa59..9404894 100644 --- a/src/test/java/com/dongni/collisionavoidance/areas/service/AirportAreaServiceIntegrationTest.java +++ b/src/test/java/com/dongni/collisionavoidance/areas/service/AirportAreaServiceIntegrationTest.java @@ -138,7 +138,7 @@ class AirportAreaServiceIntegrationTest { assertThat(area).isNotNull(); assertThat(area.getName()).isEqualTo("测试区域"); assertThat(area.getType()).isEqualTo("TEST"); - assertThat(area.getBoundary()).isNotNull(); + assertThat((Object)area.getBoundary()).isNotNull(); assertThat(area.getBoundary().getSRID()).isEqualTo(4326); } diff --git a/src/test/java/com/dongni/collisionavoidance/common/repository/AirportAreaRepositoryTest.java b/src/test/java/com/dongni/collisionavoidance/common/repository/AirportAreaRepositoryTest.java index 9e7bd43..7782d0f 100644 --- a/src/test/java/com/dongni/collisionavoidance/common/repository/AirportAreaRepositoryTest.java +++ b/src/test/java/com/dongni/collisionavoidance/common/repository/AirportAreaRepositoryTest.java @@ -228,7 +228,7 @@ class AirportAreaRepositoryTest { assertThat(area).isPresent(); Polygon boundary = area.get().getBoundary(); - assertThat(boundary).isNotNull(); + assertThat((Object) boundary).isNotNull(); assertThat(boundary.getSRID()).isEqualTo(4326); // WGS84坐标系 assertThat(boundary.isValid()).isTrue(); assertThat(boundary.getArea()).isGreaterThan(0); @@ -271,7 +271,7 @@ class AirportAreaRepositoryTest { // 测试中心点计算 Point centroid = aa.getCentroid(); - assertThat(centroid).isNotNull(); + assertThat((Object) centroid).isNotNull(); assertThat(centroid.getX()).isBetween(116.0, 117.0); assertThat(centroid.getY()).isBetween(39.0, 40.0); diff --git a/src/test/java/com/dongni/collisionavoidance/common/repository/VehicleLocationRepositoryTest.java b/src/test/java/com/dongni/collisionavoidance/common/repository/VehicleLocationRepositoryTest.java index 2da693d..4204b3f 100644 --- a/src/test/java/com/dongni/collisionavoidance/common/repository/VehicleLocationRepositoryTest.java +++ b/src/test/java/com/dongni/collisionavoidance/common/repository/VehicleLocationRepositoryTest.java @@ -250,7 +250,7 @@ class VehicleLocationRepositoryTest { assertThat(vehicle).isPresent(); Point location = vehicle.get().getLocation(); - assertThat(location).isNotNull(); + assertThat((Object)location).isNotNull(); assertThat(location.getSRID()).isEqualTo(4326); // WGS84坐标系 assertThat(location.getX()).isBetween(116.0, 117.0); assertThat(location.getY()).isBetween(39.0, 40.0); diff --git a/src/test/java/com/dongni/collisionavoidance/controller/UnmannedVehicleControllerTest.java b/src/test/java/com/dongni/collisionavoidance/controller/UnmannedVehicleControllerTest.java index 31708a5..362ccf9 100644 --- a/src/test/java/com/dongni/collisionavoidance/controller/UnmannedVehicleControllerTest.java +++ b/src/test/java/com/dongni/collisionavoidance/controller/UnmannedVehicleControllerTest.java @@ -1,6 +1,5 @@ package com.dongni.collisionavoidance.controller; -import com.dongni.collisionavoidance.common.model.dto.Response; import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation; import com.dongni.collisionavoidance.datacollector.model.dto.VehicleCommand; import com.dongni.collisionavoidance.datacollector.model.dto.VehicleStateInfo; @@ -74,7 +73,7 @@ class UnmannedVehicleControllerTest { .content(objectMapper.writeValueAsString(command))) .andExpect(status().isOk()) .andExpect(jsonPath("$.status").value(200)) - .andExpect(jsonPath("$.message").value("控制指令执行成功")); + .andExpect(jsonPath("$.msg").value("控制指令执行成功")); } /** diff --git a/src/test/java/com/dongni/collisionavoidance/dataCollector/service/VehicleDataPersistenceServiceIntegrationTest.java b/src/test/java/com/dongni/collisionavoidance/dataCollector/service/VehicleDataPersistenceServiceIntegrationTest.java index 59f9511..3df66bc 100644 --- a/src/test/java/com/dongni/collisionavoidance/dataCollector/service/VehicleDataPersistenceServiceIntegrationTest.java +++ b/src/test/java/com/dongni/collisionavoidance/dataCollector/service/VehicleDataPersistenceServiceIntegrationTest.java @@ -6,8 +6,6 @@ import com.dongni.collisionavoidance.common.service.VehicleLocationService; import com.dongni.collisionavoidance.datacollector.model.entity.VehicleCommandEntity; import com.dongni.collisionavoidance.datacollector.model.enums.CommandReason; import com.dongni.collisionavoidance.datacollector.model.enums.CommandType; -import com.dongni.collisionavoidance.datacollector.repository.VehicleCommandRepository; -import com.dongni.collisionavoidance.datacollector.service.VehicleDataPersistenceService; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -44,9 +42,6 @@ class VehicleDataPersistenceServiceIntegrationTest { @Autowired private VehicleLocationService vehicleLocationService; - @Autowired - private VehicleCommandRepository vehicleCommandRepository; - private final GeometryFactory geometryFactory = new GeometryFactory(); /** diff --git a/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleExecutionEngineTest.java b/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleExecutionEngineTest.java index 3290506..b53d724 100644 --- a/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleExecutionEngineTest.java +++ b/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleExecutionEngineTest.java @@ -130,7 +130,7 @@ class RuleExecutionEngineTest { // 验证事件发布(只验证开始事件,因为完成事件可能因业务逻辑而不同) verify(ruleEventPublisher).publishRuleExecutionStarted(eq(testRule), eq(vehicleId), - any(), any(Map.class)); + any(), anyMap()); } @Test @@ -147,7 +147,7 @@ class RuleExecutionEngineTest { assertNotNull(result); // 验证执行开始事件 verify(ruleEventPublisher).publishRuleExecutionStarted(eq(testRule), eq(vehicleId), - any(), any(Map.class)); + any(), anyMap()); } @Test @@ -184,7 +184,7 @@ class RuleExecutionEngineTest { // 验证每个规则都被执行 verify(ruleEventPublisher, times(2)).publishRuleExecutionStarted( - any(SpatialRule.class), eq(vehicleId), any(), any(Map.class)); + any(SpatialRule.class), eq(vehicleId), any(), anyMap()); } @Test diff --git a/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleViolationRealtimePushTest.java b/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleViolationRealtimePushTest.java index 15f11f6..0616d6b 100644 --- a/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleViolationRealtimePushTest.java +++ b/src/test/java/com/dongni/collisionavoidance/geofence/service/RuleViolationRealtimePushTest.java @@ -16,7 +16,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; -import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.context.ApplicationEventPublisher; @@ -126,7 +125,7 @@ public class RuleViolationRealtimePushTest { any(SpatialRule.class), any(VehicleLocation.class), any(RuleExecutionResult.class), - any(Map.class) + anyMap() )).thenReturn(testViolationEvent); // When: 创建违规事件 @@ -146,7 +145,7 @@ public class RuleViolationRealtimePushTest { any(SpatialRule.class), any(VehicleLocation.class), any(RuleExecutionResult.class), - any(Map.class) + anyMap() ); } diff --git a/src/test/java/com/dongni/collisionavoidance/geofence/service/SpatialRuleIntegrationTest.java b/src/test/java/com/dongni/collisionavoidance/geofence/service/SpatialRuleIntegrationTest.java index 327dabb..7dc0b3f 100644 --- a/src/test/java/com/dongni/collisionavoidance/geofence/service/SpatialRuleIntegrationTest.java +++ b/src/test/java/com/dongni/collisionavoidance/geofence/service/SpatialRuleIntegrationTest.java @@ -1,7 +1,6 @@ package com.dongni.collisionavoidance.geofence.service; import com.dongni.collisionavoidance.common.model.MovingObjectType; -import com.dongni.collisionavoidance.common.service.VehicleLocationService; import com.dongni.collisionavoidance.common.model.spatial.VehicleLocation; import com.dongni.collisionavoidance.geofence.model.enums.*; import com.dongni.collisionavoidance.rule.event.RuleViolationEvent; @@ -57,9 +56,6 @@ public class SpatialRuleIntegrationTest { @Autowired private RealTimeViolationDetector realTimeViolationDetector; - @Autowired - private VehicleLocationService vehicleLocationService; - @Autowired private SpatialRuleRepository spatialRuleRepository; diff --git a/src/test/java/com/dongni/collisionavoidance/roads/service/RoadNetworkServiceIntegrationTest.java b/src/test/java/com/dongni/collisionavoidance/roads/service/RoadNetworkServiceIntegrationTest.java index 63cb2ff..a9febc6 100644 --- a/src/test/java/com/dongni/collisionavoidance/roads/service/RoadNetworkServiceIntegrationTest.java +++ b/src/test/java/com/dongni/collisionavoidance/roads/service/RoadNetworkServiceIntegrationTest.java @@ -228,12 +228,4 @@ class RoadNetworkServiceIntegrationTest { assertThat(roadNetworkService.getRoadById("road-invalid-geom")).isEmpty(); // Also check spatial index doesn't contain it (harder to test directly) } - - // Helper method example (if roadInfoMap was accessible or we add a getter for tests) - private boolean roadInfoMapContainsName(String name) { - // This requires access to roadInfoMap or a dedicated getter in the service - // return roadNetworkService.getRoadInfoMap().values().stream() - // .anyMatch(info -> name.equals(info.getName())); - return false; // Placeholder - } } \ No newline at end of file diff --git a/src/test/java/com/dongni/collisionavoidance/webSocket/event/WebSocketEventTest.java b/src/test/java/com/dongni/collisionavoidance/webSocket/event/WebSocketEventTest.java index a14b684..a55f0fa 100644 --- a/src/test/java/com/dongni/collisionavoidance/webSocket/event/WebSocketEventTest.java +++ b/src/test/java/com/dongni/collisionavoidance/webSocket/event/WebSocketEventTest.java @@ -2,9 +2,6 @@ package com.dongni.collisionavoidance.websocket.event; import org.junit.jupiter.api.Test; -import com.dongni.collisionavoidance.websocket.event.CollisionWarningEvent; -import com.dongni.collisionavoidance.websocket.event.PositionUpdateEvent; -import com.dongni.collisionavoidance.websocket.event.TrafficLightStatusEvent; import com.dongni.collisionavoidance.websocket.message.*; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index f779b8a..19fc5f0 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -24,29 +24,12 @@ spring: # 禁用Flyway在测试中执行 - 使用JPA自动建表 flyway: enabled: false - - # 禁用MongoDB自动配置 - autoconfigure: - exclude: - - org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration - - org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration # 禁用数据服务 data: - mongodb: - auto-index-creation: false redis: repositories: enabled: false - - # 禁用Kafka - kafka: - bootstrap-servers: - producer: - bootstrap-servers: - consumer: - bootstrap-servers: - auto-startup: false # 禁用调度和异步任务 task: @@ -64,12 +47,6 @@ spring: # 测试模式标记 test-mode: true -# WebSocket配置 -websocket: - message: - cache: - enabled: false # 禁用消息缓存以避免序列化问题 - # 数据采集器配置 data: collector: @@ -86,14 +63,6 @@ data: processor: enabled: false # 禁用数据处理器 -# 规则检测配置 -rule: - detection: - async: - enabled: false # 禁用异步规则检测,避免测试中的复杂事件链 - - - # 日志配置 logging: level: diff --git a/tools/mock_server.py b/tools/mock_server.py index 853d8a3..510e8c3 100644 --- a/tools/mock_server.py +++ b/tools/mock_server.py @@ -133,6 +133,22 @@ if qn002_dist > 0: # 修改车辆数据中 QN002 的初始位置和方向 vehicle_data = [ + { + "vehicleNo": "TQ001", # 特勤车 + "longitude": POINT_T4["longitude"], + "latitude": POINT_T4["latitude"], + "time": int(time.time() * 1000), + "direction": 270.0, # 向西方向为270度 + "speed": DEFAULT_VEHICLE_SPEED + }, + { + "vehicleNo": "BD001", # 摆渡车 + "longitude": POINT_T5["longitude"], + "latitude": POINT_T5["latitude"], + "time": int(time.time() * 1000), + "direction": 0.0, # 向北方向为0度 + "speed": DEFAULT_VEHICLE_SPEED + }, { "vehicleNo": "QN001", # 无人车1 "longitude": POINT_T1["longitude"], @@ -148,17 +164,13 @@ vehicle_data = [ "time": int(time.time() * 1000), "direction": 90.0, # 向东方向为90度 "speed": DEFAULT_VEHICLE_SPEED - }, - { - "vehicleNo": "TQ001", # 特勤车 - "longitude": POINT_T4["longitude"], - "latitude": POINT_T4["latitude"], - "time": int(time.time() * 1000), - "direction": 270.0, # 向西方向为270度 - "speed": DEFAULT_VEHICLE_SPEED } ] +# 拆分 +airport_vehicle_data = [v for v in vehicle_data if not v["vehicleNo"].startswith("QN")] +unmanned_vehicle_data = [v for v in vehicle_data if v["vehicleNo"].startswith("QN")] + # 添加车辆状态类 class VehicleState: def __init__(self, vehicle_no): @@ -872,7 +884,7 @@ def switch_traffic_light_state(): @app.route('/openApi/getCurrentVehiclePositions', methods=['GET', 'OPTIONS']) def get_vehicle_positions(): - """获取当前车辆位置信息""" + """获取当前车辆位置信息(仅机场车辆)""" if request.method == 'OPTIONS': return '', 204 if not check_auth(): @@ -884,24 +896,29 @@ def get_vehicle_positions(): global last_vehicle_update_time current_time = time.time() elapsed_time = current_time - last_vehicle_update_time - try: - # 统一处理红绿灯状态切换 - # switch_traffic_light_state() # 采用模拟的红绿灯脚本,注释掉此处的调用 - # 只在达到更新间隔时更新位置 if elapsed_time >= UPDATE_INTERVAL: - for vehicle in vehicle_data: + for vehicle in airport_vehicle_data: update_vehicle_position(vehicle, UPDATE_INTERVAL) vehicle["time"] = int(current_time * 1000) last_vehicle_update_time = current_time - + response_data = [] + for v in airport_vehicle_data: + v_out = { + "vehicleNo": v.get("vehicleNo"), + "longitude": v.get("longitude"), + "latitude": v.get("latitude"), + "time": v.get("time"), + "direction": v.get("direction"), + "speed": v.get("speed") + } + response_data.append(v_out) return jsonify({ "status": 200, "msg": "当前车辆实时位置数据", - "data": vehicle_data + "data": response_data }) - except Exception as e: print(f"Error in get_vehicle_positions: {str(e)}") return jsonify({ @@ -1036,18 +1053,17 @@ def generate_unmanned_vehicle_location_data(): unmanned_vehicles = [] # 从现有vehicle_data中筛选无人车(QN开头的车辆) - for vehicle in vehicle_data: - if vehicle["vehicleNo"].startswith("QN"): - location_info = { - "transId": str(uuid.uuid4()), - "timestamp": int(time.time() * 1000), - "vehicleID": vehicle["vehicleNo"], - "latitude": vehicle["latitude"], - "longitude": vehicle["longitude"], - "speed": vehicle["speed"] / 3.6, # 转换为m/s - "direction": math.radians(vehicle["direction"]) # 转换为弧度 - } - unmanned_vehicles.append(location_info) + for vehicle in unmanned_vehicle_data: + location_info = { + "transId": str(uuid.uuid4()), + "timestamp": int(time.time() * 1000), + "vehicleID": vehicle["vehicleNo"], + "latitude": vehicle["latitude"], + "longitude": vehicle["longitude"], + "speed": vehicle["speed"] / 3.6, # 转换为m/s + "direction": math.radians(vehicle["direction"]) # 转换为弧度 + } + unmanned_vehicles.append(location_info) return unmanned_vehicles @@ -1081,28 +1097,27 @@ def generate_unmanned_vehicle_state_data(vehicle_id=None, is_single=True): vehicle_states_data.append(state_info) else: # 所有车辆状态查询 - for vehicle in vehicle_data: - if vehicle["vehicleNo"].startswith("QN"): - vehicle_state = vehicle_states.get(vehicle["vehicleNo"]) - state_info = { - "transId": str(uuid.uuid4()), - "timestamp": int(time.time() * 1000), - "vehicleID": vehicle["vehicleNo"], - "loginState": True, - "faultInfo": [], - "activeSafety": not vehicle_state.is_running if vehicle_state else False, - "RC": False, - "Command": 1 if vehicle_state and vehicle_state.current_command == "ALERT" else 0, - "airportInfo": [], - "vehicleMode": 2, - "gearState": 2, - "chassisReady": vehicle_state.is_running if vehicle_state else True, - "collisionStatus": False, - "clearance": 1 if vehicle_state and vehicle_state.is_running else 0, - "turnSignalStstus": 0, - "pointCloud": [] - } - vehicle_states_data.append(state_info) + for vehicle in unmanned_vehicle_data: + vehicle_state = vehicle_states.get(vehicle["vehicleNo"]) + state_info = { + "transId": str(uuid.uuid4()), + "timestamp": int(time.time() * 1000), + "vehicleID": vehicle["vehicleNo"], + "loginState": True, + "faultInfo": [], + "activeSafety": not vehicle_state.is_running if vehicle_state else False, + "RC": False, + "Command": 1 if vehicle_state and vehicle_state.current_command == "ALERT" else 0, + "airportInfo": [], + "vehicleMode": 2, + "gearState": 2, + "chassisReady": vehicle_state.is_running if vehicle_state else True, + "collisionStatus": False, + "clearance": 1 if vehicle_state and vehicle_state.is_running else 0, + "turnSignalStstus": 0, + "pointCloud": [] + } + vehicle_states_data.append(state_info) return vehicle_states_data @@ -1112,25 +1127,19 @@ def get_unmanned_vehicle_location(): """无人车位置上报接口""" if request.method == 'OPTIONS': return '', 204 - try: - # 更新车辆位置 current_time = time.time() global last_vehicle_update_time elapsed_time = current_time - last_vehicle_update_time - if elapsed_time >= UPDATE_INTERVAL: - for vehicle in vehicle_data: + for vehicle in unmanned_vehicle_data: update_vehicle_position(vehicle, UPDATE_INTERVAL) vehicle["time"] = int(current_time * 1000) last_vehicle_update_time = current_time - # 生成无人车位置数据 location_data = generate_unmanned_vehicle_location_data() - print(f"返回无人车位置数据,数量: {len(location_data)}") return jsonify(location_data) - except Exception as e: print(f"Error in get_unmanned_vehicle_location: {str(e)}") return jsonify([]), 500